Splitting a cubic Bézier curve

In this post, I thought I'd go over splitting Bézier curves into an arbitrary number of smaller Bézier curves. Before diving into the nitty gritty, let's take a step back for a little context:

Let's write a very simple button component where we have a background animation.

There's a very straight-forward hover animation, with a pseudo element that has a transform: translateX(); applied on hover. There's also a simple ease-out applied to the transition so it's nice a smooth.

Things quickly get ugly when you need the button's animation to be split in two or more lines.

The first CSS property that came to mind was box-decoration-break: clone;. Unfortunately I needed the display element to be a block, or an inline-block. So this wouldn't work.

The next step was to split the button in smaller components and to apply multiple transition-delay to its sub-elements to stagger the animations. It's also worth noting that the transition-delay needs to be applied inversely to element:hover {} so the first element animates last when you hover out.

As you can see, because the ease is applied on a per-element basis, the momentum of the animation is lost.

This brings me to the topic at hand: splitting Bézier curves. We want to find a transition-timing-function to be applied to each element, such that the overall animation respects the easing.

Below, we have a quadratic Bézier curve: two points where the line starts/end and two control points that dictates the curvature of the curve.

And here's the quadratic Bézier curve function that returns the point of the curve at t (where t ∈ {0, 1}) given the four points p0, p1, p2, p3:

function CubicBezierCurve(t, p0, p1, p2, p3) { return (1 - t)³ * p0 + 3 * (1 - t)² * t * p1 + 3 * (1 - t) * t² * p2 + t³ * p3 }

There is a simple recursive algorithm that allows you to split a curve in two distinct Bézier curves at any given t called de Casteljau's algorithm. Here's the geometric representation of the algorithm:

Unfortunately, as you can see from the animation above, t doesn't have a direct correlation with the horizontal "time" axis.

The best solution I've found so far is to sample the curve and get an approximate mapping of time to t.

Once we have an approximate mapping of time to t, we can run the geometric algorithm mentioned above and split a Bézier curve in multiple smaller Bézier curves.

With a sample rate of 100, we get the following four distinct bezier curves:

We now know how to split a Bézier curve into multiple smaller Bézier curves.

What we need is to split the bezier curve such that the distance (y-axis) is spilt to match our elements, then find their respective ts.

For most ease-out type of animations we can just apply the previous algorithm on the y-axis. However, we have to account for animations such as bounce or elastic, where the animation can momentarily reverse. Let's use cubic-bezier(0.75, 2, 0, -1) as our example from hereon.