Nice-looking, realistic motion requires more than just abrupt starts and stops with constant-rate motion in between. This is where easing functions come into play.
An easing function causes the animation to proceed at a varying rate over time. For instance, it may start quickly, and then gradually come to a stop.
Concert.js has five built-in easing functions, covering much of the sort of motion commonly seen in animations. It also easily allows you to supply your own, custom easing functions as well.
const LeftPosition = 0, RightPosition = 360, EndTime = 1500;
let animations =
[
{
target: document.getElementById("Block_ConstantRate"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: Concert.EasingFunctions.ConstantRate,
keyframes:
{
times: [0, EndTime],
values: [LeftPosition, RightPosition]
}
},
{
target: document.getElementById("Block_QuadIn"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: Concert.EasingFunctions.QuadIn,
keyframes:
{
times: [0, EndTime],
values: [LeftPosition, RightPosition]
}
},
{
target: document.getElementById("Block_QuadInOut"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: Concert.EasingFunctions.QuadInOut,
keyframes:
{
times: [0, EndTime],
values: [LeftPosition, RightPosition]
}
},
{
target: document.getElementById("Block_QuadOut"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: Concert.EasingFunctions.QuadOut,
keyframes:
{
times: [0, EndTime],
values: [LeftPosition, RightPosition]
}
},
{
target: document.getElementById("Block_Smoothstep"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: Concert.EasingFunctions.Smoothstep,
keyframes:
{
times: [0, EndTime],
values: [LeftPosition, RightPosition]
}
}
];
let sequence = new Concert.Sequence();
sequence.addTransformations(animations);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
The above code creates five nearly identical transformation set objects,
each applying to a different div
element.
Each one specifies animating the element's CSS left
style
from 0 to 360px over a period of 1500 ms.
The difference in each is the value specified for the easing
property.
These pre-defined easing functions allow for a smooth start and/or end to an animation.
(If this looks like a lot of code for just a little bit of action, don't worry. Later in the tutorial we'll cover setting default values so that not every common property of every transformation set object needs to be repeated again and again.)
Sometimes it is necessary to use an easing function other than those already defined. Let's examine how that can be accomplished.
First, let's look at what an easing function does, and what one looks like. An easing is a function which modifies the rate at which a transformation moves from beginning to end. For instance, it may progress steadily from the start time to the end time, or it may accelerate and decelerate to make motion appear smoother.
Important to note is that an easing function does not change the total time of the animation at all. The start and end times remain the same; what changes is the rate of the motion in between those times.
Besides the pre-defined easing functions we just saw above, Concert.js can also use a custom easing function.
An easing function is any function which takes the form function easingFunction(startTime, endTime, currentTime)
and returns a value from 0 to 1. (At least, ordinarily. There do exist easing functions which involve travel beyond
the start and end points, such as a slight out-of-bounds bounce-type movement, but most times you'll want to ensure
your easing functions never go below 0 or above 1 to avoid unexpected behavior.)
The inputs represent the start time of the transition, the end time, and the current position along that timeline. (Strictly speaking, these don't need to be times; if you were synchronizing your animation to, say, the position of a scroll bar rather than the system clock, the beginning and end would not be measured in microseconds. But in any case, there are a start point and an end point and a current place along that "timeline".)
The return value is a fraction representing the portion of the total distance the animation should have traversed in the time passed so far. This fraction is used to interpolate the current position in an animation segement.
For instance, for a simple, constant rate of motion, you would want a function that just calculated the fraction of the timeline traveled, and returned the same fraction. That is, if start time is 0, end time is 100, and current time is 50, then 50% of the time has passed and therefore 50% of the animated transition should have occurred, so the value returned would be 0.5. And indeed, here is the code for the build-in ConstantRate easing function:
ConstantRate:
function (startTime, endTime, currentTime)
{
if (currentTime >= endTime)
return 1;
else if (currentTime < startTime)
return 0;
else
return ((currentTime - startTime) / (endTime - startTime));
}
All it does is return 1 (the full distance has been reached) if the end time is reached or passed, return 0 if the current time is at or before the start time, and for any current time value in between start time and end time, return the fraction of the total time that has so far gone by. This will result in animated motion that starts abruptly at the start time, moves at exactly the same rate as time progresses, and ends abruptly at the end time.
Feel free to examine the Concert.js code to see how the other build-in easing functions are defined. They are all reasonably simple functions.
But what if none of them meets your needs? Simply build your own, and as long as it has the right function signature and returns a fractional value indicating how far the animation should be considered to have progressed, you can pass that function in wherever you would use a built-in easing function. Here is an example:
The above demonstrates an easing function which creates motion that accelerates to full speed and then decelerates to a pause three times over the course of its travel from beginning to end. Below is the code which accomplishes this.
function triplePiston(startTime, endTime, currentTime)
{
const Segments = 3,
s = (endTime - startTime) / Segments,
t = currentTime - startTime,
distanceFraction =
(Math.floor(t / s)
+ ((Math.cos(Math.PI * (s - (t % s)) / s) + 1) / 2)) / Segments;
return Math.max(Math.min(distanceFraction, 1), 0);
}
let animation =
{
target: document.getElementById("Block_CustomEasing"),
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
easing: triplePiston,
keyframes: { times: [0, 1500], values: [0, 360] }
};
var sequence = new Concert.Sequence();
sequence.addTransformations(animation);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
Don't worry about the mathematics used to calculate the distance fraction.
The point here isn't to understand how this particular easing function works,
but to understand how custom easing functions can be constructed and used.
Simply note that the function triplePiston
takes a start time,
and end time, and a current time, and from that it calculates a return value
which is a fraction representing the proportion of the total distance the box
should have moved during the time which has passed so far.
How is it used? As you can see above, everything is exactly the same as in previous
examples except that the transformation set definition's easing
property
is now set to the custom triplePiston
function. Then we simply set
the animation in motion as usual, and our custom function gets used internally
by Concert.js during the animation for determining where to place the sliding box.
Custom easing functions can work any way you like. Just remember that they can be called many times per second in a running animation, so how quickly they run can have an effect on how smoothly your animation runs.
Reference documentation links for items covered in this step of the tutorial: