Search⌘ K
AI Features

Creating Easing Operators

Explore how to build easing operators in RxJS that modify animation progress for smoother transitions. Understand percentDone and easingMap operators to control timing and acceleration, and compose these for advanced animated effects.

Easing

Now that the observable stream emits progressive values, we can run them through a converter to create different styles of transitioning between two points (also known as easing).

Each possible easing relies on an equation that answers the question, “Given that we are halfway through, where should the center be rendered?” For instance, the following starts slowly, and then accelerates to catch up at the end:

TypeScript 3.3.4
function easingSquared(percentDone) {
return percentDone * percentDone;
}
console.log( easingSquared(.25) ); // 0.25^2 = 0.0625
console.log( easingSquared(.50) ); // 0.50^2 = 0.25
console.log( easingSquared(.75) ); // 0.75^2 = 0.5625
console.log( easingSquared(.9) ); // 0.9^2 = 0.81
console.log( easingSquared(1) ); // 1^2. = 1

Easing operators

Many other easing functions exist, but it’s a lot more fun to see them in action than to drearily read code.

Let’s put everything together from this section to create a tool that allows moving elements with an arbitrary easing function to see a few of these in action.

percentDone operator

First, we’ll create a percentDone operator that takes a duration in milliseconds and returns a percentage of how far the animation has gone.

TypeScript 3.3.4
function percentDone(durationSec) {
return function ($obs) {
let startTime = animationFrame.now();
// let endTime = animationFrame.now() + (durationSec * 1000);
// let durationMs = endTime - startTime;
return $obs.pipe(
map(() => (animationFrame.now() - startTime) / (durationSec * 1000))
);
};
}

startTime variable

While this function creates a startTime variable, rest assured that the inner function won’t be called until something subscribes to the observable. This is another advantage of lazy observables. This means that startTime won’t be set until the animation begins.

easingMap operator

The next operator uses a map that takes an easing function. Right now, this is just a wrapper around the map operator.

TypeScript 3.3.4
function easingMap(easingFn) {
return function ($obs) {
return $obs.pipe(map(easingFn));
};
}

This snippet may seem superfluous (why not just use map?), but it helps communicate intent. Later, you can add more concrete types to your operator with TypeScript, so that it will allow only certain types of functions.

finalTween function

Finally, let’s create a new tween function that takes advantage of these two new operators you created.

TypeScript 3.3.4
function finalTween(easingFn, element, endPoint, duration) {
let startPoint = element.style.left;
let pixelsToMove = endPoint - startPoint;
interval(1000 / 60, animationFrame)
.pipe(
percentDone(duration),
takeWhile(percentDone => percentDone <= 1),
easingMap(easingFn)
)
.subscribe((movePercent: number) => {
element.style.left = startPoint + (pixelsToMove * movePercent) + 'px';
});
}

We can then compose these together to demonstrate what several different types of easing operators look like:

C++
let linear = p => p;
let quadraticIn = p => p * p;
let quadraticOut = p => p * (2 - p);
finalTween(linear, redSquare, 500, 5);
finalTween(quadraticIn, blueSquare, 500, 5);
finalTween(quadraticOut, greenSquare, 500, 5);

MultiTween animation

Now, let’s see all of our easing operators into action.

Bud1
x.html
index.htmlIlocblob;(ÿÿÿÿÿÿpackage-lock.jsonIlocblob©(ÿÿÿÿÿÿpackage.jsonIlocblob(ÿÿÿÿÿÿpackage.json.sb-a48e3b48-LE4G1wIlocblob˜ÿÿÿÿÿÿstopwatch-complete.tsIlocblob…(ÿÿÿÿÿÿstopwatch.tsIlocblobó(ÿÿÿÿÿÿ
tsconfig.jsonIlocblob;˜ÿÿÿÿÿÿwebpack.config.jsIlocblob©˜ÿÿÿÿÿÿ @€ @€ @€ @E
DSDB `€ @€ @€ @
multiTween animation