Are you a Bezier curve lover like I’m?
Moreover being elegant, Bezier curves have good mathematical properties as a consequence of their definition and development. No surprise they’re broadly utilized in so many areas:
- As a drawing/design software: They’re usually refered to as “paths” in vector drawing software program.
- As a format of representing curves: They’re utilized in SVG, fonts and lots of different vector graphic codecs.
- As a mathematical operate: Usually used to regulate animation timing.
Now, how about utilizing Bezier curves as movement paths with CSS?
Fast Recap
Relying on the context, when referring to a “Bezier curve”, we frequently assume a 2D cubic Bezier curve.
Such a curve is outlined by 4 factors:
Observe: On this article, we usually seek advice from P0
and P3
as endpoints, P1
and P2
as management factors.
The phrase “cubic” means the underlying operate of the curve is a cubic polynomial. There are additionally “quadratic” Bezier curves, that are comparable, however with one fewer management level.
The Downside
Say you’re given an arbitrary 2D cubic Beizer curve, how would you animate a component with pure CSS animation, such that it strikes exactly alongside the curve?
For instance, how would you recreate this animation?
On this article we’ll discover three strategies with totally different flavors. For every answer we’ll current an interactive demo, then clarify the way it works. There are many mathematical calculations and proofs behind the scene, however don’t fear, we won’t go very deep.
Let’s begin!
Technique 1: Time Warp
Right here’s the essential concept:
- Arrange
@keyframes
to maneuver the component from one endpoint of the curve to the opposite. - Distort the time for every coordinate individually, utilizing
animation-timing-function
.
Observe: There are many examples and explanations in Temani Afif’s article (2021).
Utilizing the cubic-bezier()
operate with appropriate parameters, we are able to create a movement path of any cubic Bezier curve:
This demo exhibits a pure CSS animation. But canvas and JavaScript are used, which serve two functions:
- Visualize the underlying Bezier curve (pink curve).
- Permit adjusting the curve with the standard “path” UI.
You’ll be able to drag the 2 endpoints (black dots) and the 2 management factors (black squares). The JavaScript code will replace the animation accordingly, by updating just a few CSS variables.
Observe: Right here’s a pure CSS model for reference.
The way it works
Suppose the specified cubic Bezier curve is outlined by 4 factors: p0
, p1
, p2
, and p3
. We arrange CSS guidelines as following:
/* pseudo CSS code */
div {
animation-name: move-x, move-y;
/*
Outline:
f(x, a, b) = (x - a) / (b - a)
qx1 = f(p1.x, p0.x, p3.x)
qx2 = f(p2.x, p0.x, p3.x)
qy1 = f(p1.y, p0.y, p3.y)
qy2 = f(p2.y, p0.y, p3.y)
*/
animation-timing-function:
cubic-bezier(1/3, qx1, 2/3, qx1),
cubic-bezier(1/3, qy1, 2/3, qy2);
}
@keyframes move-x {
from {
left: p0.x;
}
to {
left: p3.x;
}
}
@keyframes move-y {
from {
prime: p0.y;
}
to {
prime: p3.y;
}
}
The @keyframes
guidelines move-x
and move-y
decide the beginning and ending areas of the component. In animation-timing-function
we have now two magical cubic-bezier()
features, the parameters are calculated such that each prime
and left
at all times have the proper values at any time.
I’ll skip the maths, however I drafted a short proof right here, to your curious math minds.
Discussions
This technique ought to work nicely for many instances. You’ll be able to even make a 3D cubic Bezier curve, by introducing one other animation for the z
worth.
Nonetheless there are just a few minor caveats:
- It doesn’t work when each endpoints lie on a horizontal or vertical line, due to the division-by-zero error.
Observe: In observe, you possibly can simply add a tiny offset as a workaround.
- It doesn’t help Bezier curves with an order increased than
3
. - Choices for animation timing are restricted.
- We use
1/3
and2/3
above to attain a linear timing. - You’ll be able to tweak each values to regulate the timing, however it’s restricted in contrast with different strategies. Extra on this later.
- We use
Technique 2: Competing Animations
As a warm-up, think about a component with two animations:
div {
animation-name: move1, move2;
}
What’s the movement path of the component, if the animations are outlined as following:
@keyframes move1 {
to {
left: 256px;
}
}
@keyframes move2 {
to {
prime: 256px;
}
}
As you could have guessed, it strikes diagonally:
Now, what if the animations are outlined like this as an alternative:
@keyframes move1 {
to {
remodel: translateX(256px);
}
}
@keyframes move2 {
to {
remodel: translateY(256px);
}
}
“Aha, you can’t trick me!” you may say, as you seen that each animations are altering the identical property, “move2
should override move1
like this:”
Nicely, earlier I had thought so, too. However truly we get this:
The trick is that move2
doesn’t have a from
body, which implies the beginning place is animated by move1
.
Within the following demo, the beginning place of transfer
2 is visualized because the transferring blue dot:
Quadratic Bezier Curves
The demo proper above resembles the development of a quadratic Bezier curve:
However they appear totally different. The development has three linearly transferring dots (two inexperienced, one black), however our demo has solely two (the blue dot and the goal component).
Truly the movement path within the demo is a quadratic Bezier curve, we simply have to tune the keyframes rigorously. I’ll skip the maths and simply reveal the magic:
Suppose a quadratic Bezier curve is outlined by factors p0
, p1
, and p2
. With a view to transfer a component alongside the curve, we do the next:
/* pseudo-CSS code */
div {
animation-name: move1, move2;
}
@keyframes move1 {
from {
remodel: translate3d(p0.x, p0.y, p0.z);
}
/* outline q1 = (2 * p1 - p2) */
to {
remodel: translate3d(q1.x, q1.y, q1.z);
}
}
@keyframes move2 {
to {
remodel: translate3d(p2.x, p2.y, p2.z);
}
}
Just like the demo of Technique 1, you possibly can view or alter the curve. Moreover, the demo additionally exhibits two extra items of data:
- The mathematical development (grey transferring components)
- The CSS animations (blue components)
Each could be toggled utilizing the checkboxes.
Cubic Bezier Curves
This technique works for cubic Bezier curves as nicely. If the curve is outlined by factors p0
, p1
, p2
, and p3
. The animations ought to be outlined like this:
/* pseudo-CSS code */
div {
animation-name: move1, move2, move3;
}
@keyframes move1 {
from {
remodel: translate3d(p0.x, p0.y, p0.z);
}
/* outline q1 = (3 * p1 - 3 * p2 + p3) */
to {
remodel: translate3d(q1.x, q1.y, q1.z);
}
}
@keyframes move2 {
/* outline q2 = (3 * p2 - 2 * p3) */
to {
remodel: translate3d(q2.x, q2.y, q2.z);
}
}
@keyframes move3 {
to {
remodel: translate3d(p3.x, p3.y, p3.z);
}
}
Extensions
What about 3D Bezier Curves? Truly, the reality is, all of the earlier examples have been 3D curves, we simply by no means bothered with the z
values.
What about higher-order Bezier curves? I’m 90% certain that the strategy could be naturally prolonged to increased orders. Please let me know if in case you have labored out the formulation for fourth-order Bezier curves, and even higher, a generic formulation for Bezier curves of order N.
Technique 3: Normal Bezier Curve Development
The mathematical development of Bezier Curves already offers us a very good trace.
Step-by-step, we are able to decide the coordinates of all transferring dots. First, we decide the situation of the inexperienced dot that’s transferring between p0
and p1
:
@keyframes green0 {
from {
--green0x: var(--p0x);
--green0y: var(--p0y);
}
to {
--green0x: var(--p1x);
--green0y: var(--p1y);
}
}
Extra inexperienced dots could be constructed in an analogous method.
Subsequent, we are able to decide the situation of a blue dot like this:
@keyframes blue0 {
from {
--blue0x: var(--green0x);
--blue0y: var(--green0y);
}
to {
--blue0x: var(--green1x);
--blue0y: var(--green1y);
}
}
Rinse and repeat, finally we’ll get the specified curve.
Just like Technique 2, with this technique we are able to simply construct a 3D Bezier Curve. Additionally it is intuitive to increase the strategy for higher-order Bezier curves.
The one draw back is the utilization of @property
, which isn’t supported by all browsers.
Animation Timing
All of the examples to date have the “linear” timing, what about easing or different timing features?
Observe: By “linear” we imply the variable t
of the curve linearly modifications from 0 to 1. In different phrases, t
is identical as animation progress.
animation-timing-function
isn’t utilized in Technique 2 and Technique 3. Like different CSS animations, we are able to use any supported timing operate right here, however we have to apply the identical operate for all animations (move1
, move2
, and move3
) on the similar time.
Right here’s an instance of animation-timing-function: cubic-bezier(1, 0.1, 0, 0.9)
:
And right here’s the way it appears like with animation-timing-function: steps(18, finish)
:
Alternatively, Technique 1 is trickier, as a result of it already makes use of a cubic-bezier(u1, v1, u2, v2)
timing operate. Within the examples above we have now u1=1/3
and u2=2/3
. Actually we are able to tweak the timing by altering each parameters. Once more, all animations (e.g., move-x
and move-y
) should have the identical values of u1
and u2
.
Right here’s the way it appears like when u1=1
and u2=0
:
With Technique 2, we are able to obtain precisely the identical impact by setting animation-timing-function
to cubic-bezier(1, 0.333, 0, 0.667)
:
Actually, it really works in a extra basic method:
Suppose that we’re given a cubic Bezier curve, and we created two animations for the curve with Technique 1 and Technique 2 respectively. For any legitimate values of u1
and u2
, the next two setups have the identical animation timing:
- Technique 1 with
animation-timing-function: cubic-bezier(u1, *, u2, *)
. - Technique 2 with
animation-timing-function: cubic-bezier(u1, 1/3, u2, 2/3)
.
Now we see why Technique 1 is “restricted”: with Technique 1 we are able to solely cubic-bezier()
with two parameters, however with Technique 2 and Technique 3 we are able to use any CSS animation-timing-function
.
Conclusions
On this article, we mentioned 3 totally different strategies of transferring parts exactly alongside a Bezier curve, utilizing solely CSS animations.
Whereas all 3 strategies are roughly sensible, they’ve their very own professionals and cons:
- Technique 1 could be extra intuitive for these accustomed to the timing operate hack. However it’s much less versatile with animation timing.
- Technique 2 has quite simple CSS guidelines. Any CSS timing operate could be utilized immediately. Nonetheless, it could possibly be exhausting to recollect the formulation.
- Technique 3 make extra sense for these accustomed to the maths development of Bezier curves. Animation timing can also be versatile. Alternatively, not all fashionable browsers are supported, due the utilization of
@property
.
That’s all! I hope you discover this text attention-grabbing. Please let me know your ideas!