![CSS cubic-bezier() の数学 — xを[0,1]に制限する理由と逆関数の数値解法](/_next/image?url=%2Fblog%2Fcss-cubic-bezier-math-newton-raphson.png&w=3840&q=75)
CSS cubic-bezier() の数学 — xを[0,1]に制限する理由と逆関数の数値解法
CSS アニメーションの transition-timing-function に書く cubic-bezier(0.25, 0.1, 0.25, 1.0) — この 4 つの数値が何を意味するか、正確に理解していますか? Bézier 曲線を「描画」に使う SVG とは異なり、CSS では Bézier を「時間→進行度のマッピング関数」として使います。そのために P₀=(0,0)、P₃=(1,1) を固定し、x₁/x₂ を [0,1] に制限するという制約があります。本記事では W3C CSS Easing Functions Level 2 を一次資料に、この制約の数学的根拠と、逆関数を求めるNewton-Raphson法・二分法を整理します。仕様はブラウザ内部の具体的な求解アルゴリズムまでは規定していません。
CSS における Bézier 曲線の役割 — 描画ではなく「関数」
SVG の <path> では Bézier 曲線は 2D 平面上の形状を描きます (参考: SVG path の Bézier と楕円弧)。一方 CSS では全く異なる使い方をします:
- 横軸 x: 正規化された経過時間 (0 = 開始、1 = 終了)
- 縦軸 y: 正規化されたプロパティの進行度 (0 = 開始値、1 = 終了値)
つまり cubic-bezier(x1, y1, x2, y2) は時間→進行度の 1 次元関数 f(x) を定義しています。「曲線を描く」のではなく「アニメーションの緩急をコントロールする」ために Bézier を使っているのです。
W3C CSS Easing Functions Level 2 §2.2 では、この関数を以下のように定義しています: P₀ = (0, 0)、P₃ = (1, 1) を固定し、ユーザーが指定するのは P₁ = (x₁, y₁) と P₂ = (x₂, y₂) のみ。
3 次 Bézier のパラメトリック方程式と Bernstein 基底
3 次 Bézier 曲線は、パラメータ t ∈ [0, 1] を変化させたとき、4 つの制御点 P₀, P₁, P₂, P₃ から以下の式で点 B(t) を生成します:
B(t) = (1-t)³ P₀ + 3(1-t)² t P₁ + 3(1-t) t² P₂ + t³ P₃
この式の各係数 (1-t)³, 3(1-t)²t, 3(1-t)t², t³ は Bernstein 基底多項式 B_{i,3}(t) と呼ばれ、1912 年に Sergei Bernstein が発表した近似理論に由来します。Bernstein 基底は以下の性質を持ちます:
- 非負性: すべての i について B_{i,n}(t) ≥ 0 (t ∈ [0,1])
- 分配律: Σ B_{i,n}(t) = 1 (重みの合計が常に 1)
- 対称性: B_{i,n}(t) = B_{n-i,n}(1-t)
この「非負で合計 1」の性質は、曲線上の点が制御点の凸結合 (convex combination) であることを保証します。つまり曲線は4制御点全体の凸包からはみ出しません。CSSではP₀.x=0、P₃.x=1、P₁.xとP₂.xも0〜1なので、x(t)も0〜1の範囲に保たれます。
なぜ x₁ と x₂ を [0, 1] に制限するのか — 単調性の保証
CSS の cubic-bezier() で P₀ = (0,0), P₃ = (1,1) を固定すると、x 座標の式は:
x(t) = 3(1-t)² t · x₁ + 3(1-t) t² · x₂ + t³
この関数が「時間」を表すには、x(t) が t について単調増加でなければなりません。もし x(t) が途中で減少すると「時間が戻る」ことになり、同じ時刻 x に対して複数の t が対応してしまいます (関数として成立しない)。
x(t) の単調増加条件は、導関数 x'(t) ≥ 0 (全 t ∈ [0,1]) です:
x'(t) = 3(1-t)² x₁ + 6(1-t)t(x₂ - x₁) + 3t²(1 - x₂)
= 3[(1-t)² x₁ + 2(1-t)t(x₂ - x₁) + t²(1 - x₂)]
この 2 次式が [0,1] 上で非負であるための十分条件は x₁ ∈ [0,1] かつ x₂ ∈ [0,1] です。直感的には: P₁ と P₂ の x 座標が P₀ と P₃ の x 座標の範囲内にあれば、「時間軸方向で曲線が折り返さない」ことが保証されます。
一方 y₁ と y₂ には制限がありません。y は「進行度」であり、CSS では cubic-bezier(0.68, -0.55, 0.27, 1.55) のように y が [0,1] を超える値も許されます。これが「行き過ぎ (overshoot)」アニメーションを表現し、弾むような動きになります。
ブラウザの課題 — x から t を求める逆問題
アニメーションの各フレームで、ブラウザは「現在の経過時間 x が与えられたとき、進行度 y を求める」必要があります。しかし Bézier 曲線は t → (x, y) のパラメトリック関数であり、x → y の直接式はありません。
そこで 2 段階の計算が必要です:
- 逆関数: 与えられた x に対して
x(t) = xを満たす t を求める - 順関数: 求めた t を
y(t)に代入して進行度を得る
ステップ 1 は 3 次方程式の求根問題です。解析解を使う方法もありますが、実装では精度、計算量、境界での安定性を考慮して反復法を選べます。CSS仕様は特定の求解アルゴリズムを要求していません。
Newton-Raphson 法と二分法 — 逆関数を数値的に解く
代表的な数値解法が Newton-Raphson 法 (ニュートン法) です。求めたいのは x(t) - x_target = 0 の根で、反復式は:
t_{n+1} = t_n - (x(t_n) - x_target) / x'(t_n)
ここで x'(t) は先述の導関数です。ただし制御点が端点へ重なる場合は導関数が非常に小さくなり、Newton法の推定値が区間外へ逸脱することがあります。そのため実装では反復値を監視し、収束しない場合に区間を保つ二分法へ切り替える方法があります。
二分法は各反復で t の候補区間を半分にします。Newton法より反復回数は増えますが、x₁とx₂が0〜1ならx(t)の順序を利用でき、候補を常に0〜1へ保てます。本ツールは境界制御点でも結果を有限に保つため、40回の二分法でプレビュー値を求めています。
CSS 標準キーワードと cubic-bezier() の対応
W3C CSS Easing Functions Level 2 では、5 つのキーワードが cubic-bezier() のエイリアスとして定義されています:
表が収まらない場合は、左右にスクロールできます。
| キーワード | cubic-bezier() | 特徴 |
|---|---|---|
ease | (0.25, 0.1, 0.25, 1.0) | ゆっくり始まり、急加速、ゆっくり終わる |
ease-in | (0.42, 0, 1.0, 1.0) | ゆっくり始まり、加速して終わる |
ease-out | (0, 0, 0.58, 1.0) | 素早く始まり、減速して終わる |
ease-in-out | (0.42, 0, 0.58, 1.0) | ゆっくり始まり、ゆっくり終わる (対称) |
linear | (0, 0, 1.0, 1.0) | 等速 (制御点が対角線上) |
ease がデフォルト値です。linear は P₁ = (0,0)、P₂ = (1,1) で「4 制御点がすべて対角線上に並ぶ」ため、曲線は直線 y = x になります。
一方、Penner の easings.net で公開されている 30 種類の easing (easeInSine, easeInOutExpo など) は、それぞれ近似する cubic-bezier() 値が知られています。ただし一部 (easeInBounce など) は cubic-bezier() では正確に再現できず、CSS の linear() 関数 (Level 2 で追加) や JavaScript の requestAnimationFrame が必要です。
フォントアウトラインと Bézier — TrueType は 2 次、OpenType CFF は 3 次
Bézier 曲線が「CSS のアニメーション」だけでなく「文字の形」にも使われていることは、あまり意識されません。
表が収まらない場合は、左右にスクロールできます。
| フォーマット | Bézier 次数 | 制御点数/セグメント | 設計者 |
|---|---|---|---|
| TrueType (Apple/MS, 1991) | 2 次 (Quadratic) | 3 点 (始・制御・終) | Apple |
| OpenType CFF / Type 1 (Adobe) | 3 次 (Cubic) | 4 点 (始・制御₁・制御₂・終) | Adobe |
TrueTypeアウトラインは2次Bézier、CFF/Type 1アウトラインは3次Bézierを採用しています。現在のOpenTypeは両方のアウトライン方式を扱えますが、ラスタライザー内部の具体的な近似・分割アルゴリズムは実装ごとに異なり得ます。
de Casteljau vs 直接展開 — 数値安定性
Bézier 曲線を計算する方法は大きく 2 つあります:
1. 多項式の直接展開 (Horner 法):
x(t) = ((3x₁ − 3x₂ + 1)t + (−6x₁ + 3x₂))t + 3x₁)t
// 係数を事前計算し Horner 法で 3 回の乗算 + 2 回の加算
2. de Casteljau アルゴリズム (線形補間の再帰):
// t での点を 3 段の lerp で求める
Q₀ = lerp(P₀, P₁, t) // (1-t)P₀ + tP₁
Q₁ = lerp(P₁, P₂, t)
Q₂ = lerp(P₂, P₃, t)
R₀ = lerp(Q₀, Q₁, t)
R₁ = lerp(Q₁, Q₂, t)
B = lerp(R₀, R₁, t)
速度は Horner 法が優れますが (乗算 3 回 vs 6 回)、de Casteljau は数値安定性に優れ、tが0や1付近でも丸め誤差を抑えやすい方法です。用途と必要精度に応じて、直接展開と再帰補間を使い分けます。
まとめ
- CSS cubic-bezier() は Bézier 曲線を「形」ではなく「時間→進行度の関数」として使う
- P₀=(0,0)、P₃=(1,1) 固定、ユーザーは P₁ と P₂ の座標のみ指定
- x₁, x₂ ∈ [0,1] の制約は x(t) の単調増加 (時間が戻らない) を保証するため
- y₁, y₂ は制限なし — [0,1] 外の値で overshoot (弾む) アニメーションを表現
- x → t の逆問題はNewton-Raphson法や二分法などの数値解法で求める
- Bernstein 基底 (1912) の非負性・分配律が凸包性と数値安定性の数学的根拠
- CSS 標準の ease / ease-in / ease-out / ease-in-out / linear は cubic-bezier() のエイリアス
- フォントアウトラインでも Bézier: TrueType = 2 次、OpenType CFF = 3 次
参考文献・ソース
記事作成に関する注記
本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。




