N2
NanToo
CSS cubic-bezier() の数学 — なぜ x を [0,1] に制限し、Newton-Raphson で t を解くのか
GDESIGN
デザイン9 分で読める

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#cubic-bezier#Bézier曲線#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 段階の計算が必要です:

  1. 逆関数: 与えられた x に対して x(t) = x を満たす t を求める
  2. 順関数: 求めた 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) の実装では、概ね以下の戦略が取られています:

  1. 初期推定値 t₀ = x_target (線形近似)
  2. Newton-Raphson を最大 8 回反復
  3. 導関数が小さすぎる (接線がほぼ水平) 場合は二分法にフォールバック
  4. 収束判定: |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 次

参考文献・ソース

記事作成に関する注記

本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。

🔧 関連ツール

📚 関連記事