user371320
user371320

Reputation: 1398

Ease-In Ease-Out function

I'm wanting an easing function equivalent to UIViewAnimationCurveEaseInOut, but don't see that Apple exposes that as a function.

I've tried the easeInOutQuad function posted here with no success. My Objective-C version of that function is:

-(float) getEaseInOutValueWithElapsedMillis: (float) t startValue: (float) b endValue: (float) c andTotalMillis: (float) d {
   float   value =0.0f;

   if ((t/=d/2.0f) <1.0f)
      value =c/2.0f*t*t + b;
   else
      value =-c/2.0f * ((--t)*(t-2.0f) - 1.0f) + b;

   debug(@"t=%f b=%f c=%f d=%f value=%f", t, b, c, d, value);
   return value;
}

And the logged results are:

When the start value is less than the end value:

t=0.066467 b=110.000000 c=225.000000 d=500.000000 value=110.497017
t=0.133133 b=110.000000 c=225.000000 d=500.000000 value=111.993996
t=0.199799 b=110.000000 c=225.000000 d=500.000000 value=114.490944
...
t=0.999786 b=110.000000 c=225.000000 d=500.000000 value=222.451935
t=0.066452 b=110.000000 c=225.000000 d=500.000000 value=236.954926
t=0.133118 b=110.000000 c=225.000000 d=500.000000 value=250.457932
... (note that value shot right past c)
t=0.866440 b=110.000000 c=225.000000 d=500.000000 value=332.993195
t=0.933105 b=110.000000 c=225.000000 d=500.000000 value=334.496582
t=0.999771 b=110.000000 c=225.000000 d=500.000000 value=335.000000

And the 100% value ends up as b +c, rather than c.

When the start value is greater than the end value:

t=0.047389 b=225.000000 c=110.000000 d=700.000000 value=225.123520
t=0.095008 b=225.000000 c=110.000000 d=700.000000 value=225.496460
...
t=0.904504 b=225.000000 c=110.000000 d=700.000000 value=334.498413
t=0.952122 b=225.000000 c=110.000000 d=700.000000 value=334.873932
t=0.999740 b=225.000000 c=110.000000 d=700.000000 value=335.000000

Again, the 100% value ends up as b +c, rather than c.

Perhaps I've mangled the code. How can I achieve a proper ease-in ease-out?

Upvotes: 4

Views: 3068

Answers (2)

Tommy
Tommy

Reputation: 100652

I'm wanting an easing function equivalent to UIViewAnimationCurveEaseInOut, but don't see that Apple exposes that as a function.

Apple exposes the CAMediaTimingFunction via [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut] on which you can call getControlPointAtIndex:values: to get the control points. You'll learn that it's a cubic bezier with control points (0, 0), (0.42, 0), (0.58, 1), (1, 1).

That gives you a Bezier function f(n) that plots as a 2d chart of value, along y, against time, along x. So to get the correct value just solve for n where x = time and read off the value.

Specifically, in this function:

f.x(n) = (1-n)^3 * 0 + 3(1-n)^2 * n * 0.42 + 3(1-n) * n^2 * 0.58 + n^3 * 1
       = 3(1-n)^2 * n * 0.42 + 3(1-n) * n^2 * 0.58 + n^3
       = 1.26(1 - 2n + n^2) * n + 1.74 (1 - n) * n^2 + n^3
       = 1.26n - 2.52n^2 + 1.26n^3 + 1.74n^2 - 1.74n^3 + n^3
       = 1.26n - 0.78n^2 + 0.52n^3

(disclaimer: written out extemporaneously; please check)

To get the value at time 0.5, solve for n in:

0.5 = 1.26n - 0.78n^2 + 0.52n^3

Then plug that into the equivalent f.y(n). There's a formula to solve a cubic but getting into that is slightly involved so (i) read the Wikipedia article and use the formula; or else (ii) write a numeric solver, e.g. by binary searching. This particular cubic is very well behaved, having only one solution for every time.

Upvotes: 2

user2799205
user2799205

Reputation: 23

A little late, but here it is, in swift.

Run it like this in Playgrounds:

import UIKit

var sValue: CGFloat = 0
var eValue: CGFloat = 100
var tDuration: CGFloat = 100
var eTime: CGFloat = 0

var currentValue: CGFloat = 0

func easeInOutQuad (percentComplete: CGFloat, elapsedTimeMs: CGFloat, startValue: CGFloat, endValue: CGFloat, totalDuration: CGFloat) -> CGFloat {
    var newElapsedTimeMs = elapsedTimeMs
    newElapsedTimeMs /= totalDuration/2

    if newElapsedTimeMs < 1 {
        return endValue/2*newElapsedTimeMs*newElapsedTimeMs + startValue
    }
    newElapsedTimeMs = newElapsedTimeMs - 1
    return -endValue/2 * ((newElapsedTimeMs)*(newElapsedTimeMs-2) - 1) + startValue
}

for i in eTime...tDuration {
    currentValue = easeInOutQuad(0, CGFloat(i), sValue, eValue, tDuration)
}

Upvotes: 1

Related Questions