Value · 02
Cubic Bézier easing
三次贝塞尔缓动 · ease-cubic-bezierFour points define a timing curve; moving the two control points changes its feel.
A cubic Bézier puts time on x and progress on y. The control points' x coordinates determine when acceleration happens; their y coordinates can even move progress beyond an endpoint. Pair the curve with a moving block to judge its start and landing.
Formula
B(t) = (1−t)³P0 + 3(1−t)²tP1
+ 3(1−t)t²P2 + t³P3
find t where Bx(t) = time; output By(t)Parameters
| This page | Mature libraries | |
|---|---|---|
| P1, P2 | (.2, .8), (.2, 1) | Motion cubicBezier(x1,y1,x2,y2)Keep x in 0…1 so time maps to one progress value. |
| solver | Newton → bisection | Motion: bracketed Newton, cap at i = 40A Newton step can leave the bracket near a flat slope; bisect instead. |
Doing it well
- Treat x as time and y as progress. Swapping the coordinates changes the easing.motion/packages/motion-utils/src/easing/cubic-bezier.ts:20–29
- Guard the numerical solve near flat regions. Motion brackets Newton steps and bisects when a step leaves the bracket.motion/packages/motion-utils/src/easing/cubic-bezier.ts:39–61
- Record the actual control points in a motion spec, so an
ease-outlabel cannot silently become another curve.www-motion/rewrite/portfolio-photographer/app.js:5
In our cases
24 cases use cubic Bézier easing. portfolio-photographer spells out cubic-bezier(.16,1,.3,1) for the card's return from hover, separate from its direct pointer tracking.
Used in 24 cases
Reference implementation · basics.js
/** CSS cubic-bezier(x1, y1, x2, y2). Newton for speed, bisection when the slope is flat. */
export function cubicBezier(x1, y1, x2, y2) {
const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
const X = t => ((ax * t + bx) * t + cx) * t;
const Y = t => ((ay * t + by) * t + cy) * t;
const dX = t => (3 * ax * t + 2 * bx) * t + cx;
function solve(x) {
let t = x;
for (let i = 0; i < 8; i++) {
const err = X(t) - x, slope = dX(t);
if (Math.abs(err) < 1e-7) return t;
if (Math.abs(slope) < 1e-6) break; // flat: Newton would shoot off
t -= err / slope;
if (t < 0 || t > 1) break; // left the curve: fall back
}
let lo = 0, hi = 1; t = x;
for (let i = 0; i < 50; i++) {
const v = X(t);
if (Math.abs(v - x) < 1e-7) break;
if (v < x) lo = t; else hi = t;
t = (lo + hi) / 2;
}
return t;
}
return x => (x <= 0 ? 0 : x >= 1 ? 1 : Y(solve(x)));
}