A follow-up as prequel to the previous quick thought (regarding Robert Penner’s easing equations in Corona)
This is just intended as some casual/beginner “background material” on easing, maybe (?) helpful if you might be exploring/creating your own curves at some point.
Let’s take a step back, and ask: what IS easing? Essentially, it’s just a linear interpolation, where the interpolation factor (typically as “t”) has been “tweaked” so that it now has a non-linear response curve.
Let’s consider basic linear interpolation, which is often formulated so:
1 2 3 |
function lerp(a,b,t) return a+t*(b-a) end |
Where “a” is the initial value, “b” is the target value, and “t” is the parameter on [0..1]. When t==0, a is returned; when t==1, b is returned; when t is some intermediate value, the corresponding intermediate value is returned.
(Aside: The “(b-a)” portion is the “delta” between target and initial values. Often in animation system usage, as with Corona’s implementation, you’ll find the delta pre-calculated instead of passing the target value. Both approaches are equivalent.)
Let’s now “tweak” our lerp function to allow for easing:
1 2 3 4 5 6 |
function easing(t) return t end function lerp(a,b,t) return a+easing(t)*(b-a) end |
So far, not very exciting. All we’ve done is add a “wrapper” around t to potentially allow for it to be altered. Our easing function will take an input value on [0..1] and return a potentially reshaped output value, again on [0..1]. Right now, all we have is a return of the original value, again producing a linear response.
Here’s the input/output curve of our current easing function:
Again, not yet very exciting. So let’s alter the easing function slightly:
1 2 3 |
function easing(t) return t*t end |
Which now produces a quadratic response that looks this this:
This curve, when used in an animation system, will cause slower initial movement, gradually speeding up until reaching the target at a more rapid velocity.
We can exaggerate that effect even further by simply using a cubic function (t*t*t). (or even higher order quartic, quintic, sextic/hexic, septic/heptic, etc – though there comes a point of diminishing returns for practical usage)
So far I’ve been presenting just the “in” response curve, but I’ll switch to the in/out response curves below, as I think it’s helpful to start visualizing the symmetry.
See the code in the previous quick thought for implementation details on how the symmetry was accomplished for the in/out curve.
Essentially, the diagonal symmetry of the out curve is achieved by inverting both axes (tin and tout) using 1-easing(1-t)
. Then the in/out curve is achieved by scaling the in curve into the lower-left quadrant, and scaling/translating the out curve into the upper-right quadrant.
Now let’s look at the “Back” family of curves, as implemented in the previous article, which have an initial “overshoot” (or “undershoot”, depending on your perspective) resulting from differencing a polynomial of one degree from another polynomial of a different degree. That curve looks like this (when s==2.6):
This curve is a pretty good basis for further exploration, as it has “just enough” complexity to inspire further modifications, yet not so much complexity that it hinders comprehension.
The most obvious tweak has already been addressed: alter the relative scaling between two polynomials by introducing a user-adjustable constant (“s”).
Perhaps the next most “obvious” experiment would be to change the degrees of the polynomials. For example, consider this function:
1 2 3 4 |
function easing(t) local s = 2.6 return (s+1)*t - s*t*t end |
Producing this response curve:
Having the animation effect of quickly moving to a bit more than halfway, then backing off a bit, then continuing on to the final value. A sort of “briefly unsure”, “moment of doubt”, “hesitant” or “reluctant” motion, if we anthropomorphize it. (as if it were saying: “Wait, did I leave the oven on at home? I’d better go back and check. No, no… I’m sure I turned it off – now I’d better hurry to make up for lost time!” :D)
You can insert that equation into the icurve() function of the prior article if you’d like to tinker further. (and, again, you can adjust the degree of overshoot by adjusting “s” to suit)
All for now.