![CSS cubic-bezier() の数学 — なぜ x を [0,1] に制限し、Newton-Raphson で t を解くのか](/_next/image?url=%2Fblog%2Fcss-cubic-bezier-math-newton-raphson.png&w=3840&q=75)
CSS cubic-bezier() の数学 — なぜ x を [0,1] に制限し、Newton-Raphson で t を解くのか
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) であることを保証します。つまり曲線は制御点の凸包からはみ出しません。CSS における x(t) が P₁ と P₂ の x 座標に囲まれる (飛び出さない) のもこの性質のおかげです。
なぜ 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 次方程式の求根問題です。Cardano の公式で解析解を求めることも可能ですが、浮動小数点演算では数値的に不安定で、実用的ではありません。そこで主要ブラウザは反復法を使います。
Newton-Raphson 法 — 8 回の反復で十分
もっとも広く使われる手法が Newton-Raphson 法 (ニュートン法) です。求めたいのは x(t) - x_target = 0 の根で、反復式は:
t_{n+1} = t_n - (x(t_n) - x_target) / x'(t_n)
ここで x'(t) は先述の導関数です。x₁, x₂ ∈ [0,1] の制約により x(t) は単調増加、x'(t) > 0 が (端点以外で) 保証されるため、Newton-Raphson は安全に収束します。
Chromium (Blink) の実装では、概ね以下の戦略が取られています:
- 初期推定値 t₀ = x_target (線形近似)
- Newton-Raphson を最大 8 回反復
- 導関数が小さすぎる (接線がほぼ水平) 場合は二分法にフォールバック
- 収束判定: |x(t) - x_target| < ε (通常 ε ≒ 1e-7)
60fps アニメーションでは毎秒 60 回この計算が走りますが、8 回の乗算・加算で済むため高速です。GPU 上のシェーダーで実行されることもあります。
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 次を選んだのは、1990 年代初頭のハードウェアで高速にラスタライズするためです。2 次は de Casteljau の再帰が 1 段少なく、整数演算で処理しやすい。一方 Adobe は印刷品質を優先して 3 次を採用し、複雑な曲率変化を少ないセグメントで表現できます。
現在の OpenType 規格 (ISO/IEC 14496-22) は両方をサポートしています。Web フォントとしてブラウザがレンダリングする際も、内部では Bézier の de Casteljau アルゴリズムが走っています。
de Casteljau vs 直接展開 — 数値安定性
Bézier 曲線を計算する方法は大きく 2 つあります:
1. 多項式の直接展開 (Horner 法):
x(t) = ((−x₁ + 3x₂ − 3)t + (3x₁ − 6x₂ + 3))t + 3x₂)t
// 係数を事前計算し Horner 法で 3 回の乗算 + 3 回の加算
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() 実装では、Newton-Raphson の各ステップで Horner 法を使い (速度優先)、フォントのラスタライザでは de Casteljau を使う (精度優先) というケースが一般的です。
まとめ
- CSS cubic-bezier() は Bézier 曲線を「形」ではなく「時間→進行度の関数」として使う
- P₀=(0,0)、P₃=(1,1) 固定、ユーザーは P₁ と P₂ の座標のみ指定
- x₁, x₂ ∈ [0,1] の制約は x(t) の単調増加 (時間が戻らない) を保証するため
- y₁, y₂ は制限なし — [0,1] 外の値で overshoot (弾む) アニメーションを表現
- ブラウザは毎フレーム Newton-Raphson 法で x → t の逆関数を解く (8 反復、ε ≒ 1e-7)
- Bernstein 基底 (1912) の非負性・分配律が凸包性と数値安定性の数学的根拠
- CSS 標準の ease / ease-in / ease-out / ease-in-out / linear は cubic-bezier() のエイリアス
- フォントアウトラインでも Bézier: TrueType = 2 次、OpenType CFF = 3 次
参考文献・ソース
- W3C CSS Easing Functions Level 2 §2.2 Cubic Bézier easing functions ↗
- W3C CSS Easing Functions Level 1 (初版定義) ↗
- Bernstein S.N. "Démonstration du théorème de Weierstrass" (1912) ↗
- Bézier P. "Définition Numérique des Courbes et Surfaces" Automatisme XI (1966) ↗
- Apple TrueType Reference Manual — Digitizing Letterform Designs ↗
- Adobe Type 1 Font Format — Charstrings (Cubic Bézier) ↗
記事作成に関する注記
本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。


