CSS変形モジュール レベル2

W3C作業草案,

この文書の詳細
このバージョン:
https://www.w3.org/TR/2021/WD-css-transforms-2-20211109/
最新公開バージョン:
https://www.w3.org/TR/css-transforms-2/
編集者草案:
https://drafts.csswg.org/css-transforms-2/
過去のバージョン:
履歴:
https://www.w3.org/standards/history/css-transforms-2
課題管理:
CSSWG課題リポジトリ
仕様内インライン
編集者:
Tab Atkins Jr. (Google)
L. David Baron (Google)
(Apple Inc)
(Apple Inc)
(Apple Inc)
フィードバック:
フィードバックは GitHubで課題を提出(推奨)、 件名に仕様コード「css-transforms」を含め、例えば 「[css-transforms] …コメントの要約…」のようにしてください。 すべての課題とコメントはアーカイブされています。 または、(アーカイブ済み)の公開メーリングリストwww-style@w3.orgへ送信することもできます。
この仕様への編集提案:
GitHubエディター
デルタ仕様:
はい

概要

CSS変形は、CSSでスタイル付けされた要素を二次元または三次元空間で変形することを可能にします。

この仕様では、三次元変形のための新しい変形関数およびプロパティ、そして簡単な変形のための便利な関数を追加します。

CSSは、構造化文書(HTMLやXMLなど)のレンダリングを 画面や紙などに記述するための言語です。

この文書のステータス

このセクションでは、公開時点におけるこの文書のステータスについて説明します。 現在のW3C公開文書の一覧や、この技術レポートの最新版は W3C技術レポート一覧(https://www.w3.org/TR/) で確認できます。

この文書は CSS作業グループによって 作業草案として 勧告トラックを用いて公開されました。

作業草案として公開されたことは、W3Cおよびそのメンバーからの承認を意味するものではありません。

この文書は草案であり、いつでも更新・置換・廃止される可能性があります。 この文書を進行中の作業以外のものとして引用することは適切ではありません。

この文書は2021年11月2日版W3Cプロセス文書に従って管理されています。

この文書はW3C特許ポリシーの下で運営されているグループによって作成されました。 W3Cは、グループの成果物に関連して提出された特許開示の公開リストを管理しています。 そのページには特許開示の方法も記載されています。 個人が、必須クレームを含むと考える特許を実際に知っている場合、 W3C特許ポリシー第6節に従って情報を開示しなければなりません。

1. はじめに

本仕様は、[css-transforms-1]を拡張するデルタ仕様であり、著者が要素を三次元空間で変形できるようにします。 transformプロパティ用の新しい変形関数は三次元変形を可能にし、 追加のプロパティによって三次元変形の扱いが容易になり、入れ子の三次元変形要素同士の相互作用を著者が制御できるようになります。

  1. perspectiveプロパティは、子要素に追加のパースペクティブ変換を与えることができます。perspective-originプロパティは、パースペクティブが適用される原点を制御し、「消失点」の位置を効果的に変更します。

  2. transform-styleプロパティは、3D変形された要素とその3D変形された子孫が共通の三次元空間を共有できるようにし、三次元オブジェクトの階層構造を構築できます。

  3. backface-visibilityプロパティは、要素が三次元変形によって裏返され、裏面が表示される場合に関係します。この状況で要素を非表示にしたい場合は、このプロパティにhidden値を設定することで実現できます。

注: transformプロパティの一部の値は要素を三次元座標系で変形できますが、要素自体は三次元オブジェクトではありません。要素は二次元平面(フラットな面)上に存在し、奥行きはありません。

本仕様はさらに、scaletranslaterotateという3つの便利なプロパティも追加し、単純な変形の記述やアニメーションが容易になります。

1.1. モジュール間の相互作用

ここで定義される3D変形関数は、transformプロパティの関数セットを拡張します。

perspectivetransform-stylebackface-visibilityの一部の値は、すべての子孫の包含ブロック積み重ねコンテキストの生成につながります。

三次元変形は要素の視覚的な重なり順に影響し、Appendix E[CSS21])で記述されている前後の描画順序を上書きします。

1.2. 値の定義

本仕様はCSSプロパティ定義規約[CSS21])に従い、値定義構文[CSS-VALUES-3])を使用します。 本仕様で定義されていない値型はCSS Values & Units [CSS-VALUES-3]で定義されます。他のCSSモジュールと組み合わせることでこれらの値型定義が拡張される場合があります。

各プロパティ定義に記載されたプロパティ固有の値に加え、 この仕様で定義されるすべてのプロパティはCSS全域キーワードもプロパティ値として受け付けます。 読みやすさのため、明示的な繰り返し記載はしていません。

2. 用語

3D変形要素

transformプロパティの算出値に3D変形関数のいずれかが含まれる要素

3D行列

4x4行列で、2D行列の要件を満たさないもの。

恒等変形関数

CSS Transformsの恒等変形関数に加え、恒等変形関数の例としては translate3d(0, 0, 0)translateZ(0)scaleZ(1)rotate3d(1, 1, 1, 0)rotateX(0)rotateY(0)rotateZ(0)matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)などがあります。 特殊なケースとしてperspective: perspective(none)が挙げられます。 この場合、m34の値が極めて小さくなり、変形関数は恒等行列とみなされます。

パースペクティブ行列

perspectiveおよびperspective-originプロパティの値から算出される行列(詳細は下記参照)。

累積3D変換行列

3Dレンダリングコンテキストのルートに対する要素ごとの行列(詳細は下記参照)。

3Dレンダリングコンテキスト

共通の三次元座標系を共有する共通の祖先を持つ要素群(詳細は下記参照)。

2.1. 算出値<transform-list>のシリアライズ

<transform-list>算出値)は、下記アルゴリズムにより<matrix()>または<matrix3d()>関数のいずれかとしてシリアライズされます:

  1. transformを恒等行列で初期化した4x4行列とする。transformの要素m11m22m33m441に、その他は0に設定する。

  2. <transform-function><transform-list>内で全て後置乗算でtransformに適用する。

  3. <matrix()>または<matrix3d()>のどちらでシリアライズするか選択:

    transform2D行列の場合
    transform<matrix()>関数としてシリアライズする。
    それ以外の場合
    transform<matrix3d()>関数としてシリアライズする。

この文言はCSS Transforms 1の本文に追加するよう修正が必要です。

3. 二次元サブセット

UAは常に三次元変形をレンダリングできるとは限らず、そうした場合は本仕様の二次元サブセットのみをサポートします。この場合、三次元変形transform-styleperspectiveperspective-originbackface-visibilityはサポートされません。Section 3D変形レンダリングは適用されません。行列分解は「Graphics Gems II, edited by Jim Arvo」の"unmatrix"メソッドから技術を使い、2D用に簡略化しています。Section 変形関数の数学的記述は引き続き有効ですが、a=m11、b=m12、c=m21、d=m22、e=m41、f=m42(2Dの3x2行列、6パラメーター)を使った3x3変換行列に簡略化できます。

$$\begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix}$$

二次元変形用の3x3行列。

著者はUAが三次元変形をサポートしない場合も容易にフォールバックを指定できます。次の例ではtransformプロパティが2つ定義されています。最初は2つの二次元変形関数のみ、2番目は二次元と三次元の変形関数が混在しています。

div {  transform: scale(2) rotate(45deg);
  transform: scale(2) rotate3d(0, 0, 1, 45deg);
}

3Dサポートがある場合は2番目の定義が最初の定義を上書きします。3Dサポートがない場合は2番目の定義が無効となり、UAは最初の定義にフォールバックします。

4. 変形レンダリングモデル

この仕様は CSS Transforms 1 § 3 変形レンダリングモデル を拡張し、三次元変形関数の存在、transform-origin のZ値、perspectiveプロパティ、そして transform-style プロパティの使用値が preserve-3d の場合に適用される新しい3Dレンダリングモデルに対応しています。

三次元変形関数は概念的に座標空間を三次元に拡張し、スクリーンの平面に垂直なZ軸を追加します。Z軸は画面奥から手前(ユーザー側)に向かって増加します。

座標空間の初期状態のデモ

座標空間の初期状態のデモ。

3D変形では transform-origin のZ成分が結果に影響するため、変換行列transformtransform-origin プロパティから以下の手順で計算されます:

  1. 恒等行列で開始する。

  2. transform-origin の算出X,Y,Zで平行移動する

  3. transform プロパティの変形関数それぞれを左から右へ乗算する

  4. transform-origin の算出X,Y,Zの負値で平行移動する

4.1. 3D変形レンダリング

通常、要素は平面としてレンダリングされ、積み重ねコンテキストと同じ平面に描画されます。しばしばこれはページ全体が共有する平面です。二次元変形関数は要素の見た目を変えることができますが、その要素は依然として積み重ねコンテキストと同じ平面に描画されます。

三次元変形を持つ要素が 3Dレンダリングコンテキスト に含まれていない場合、適切な変形が適用された状態で描画されますが、他の要素と交差しません。この場合の三次元変形は二次元変形と同様に単なるペインティング効果とみなせます。同様に、変形は描画順序に影響しません。例えば、Z方向に正の値で平行移動した変形は要素を大きく見せますが、その要素がZ変換なしの要素の前面に描画されることはありません。

入れ子の3D変形要素のレンダリング方法(数式含む?)を記述すること

この例は前の説明とつながらない

この例は三次元変形が要素に適用された場合の効果を示します。

<style>
div {
    height: 150px;
    width: 150px;
}
.container {
    border: 1px solid black;
}
.transformed {
    transform: rotateY(50deg);
}
</style>

<div class="container">
    <div class="transformed"></div>
</div>
rotateY変形されたdiv。

変形は垂直(Y軸)周りに50度回転です。青いボックスが細く見える点に注目してください。ただし三次元的には見えません。

4.1.1. パースペクティブ

パースペクティブを使うことで、Z軸上で高い(ユーザーに近い)要素ほど大きく、遠いものほど小さく描画され、奥行き感を演出できます。拡大率は d/(dZ) で、dperspective プロパティ値、すなわち描画面から視点の距離です。

パースペクティブの効果は3D変形要素に2通りで適用できます。1つ目は、要素の 'transform function list' に perspective() 関数を含めることで、要素の 'current transformation matrix' に計算されます。

2つ目は、perspectiveperspective-origin プロパティを要素に適用して、その3D変形された子要素の描画に影響を与え、同じ三次元シーンに存在するような印象を与えるパースペクティブを共有させます。

スケールとZ位置の関係の図

perspectiveプロパティとZ位置によるスケーリングの違いを示す図。上の図ではZがdの半分。元の円(実線)がZ(破線円)位置に表示されるようにするため、円が2倍に拡大されて水色円になる。下の図では円が1/3に縮小されて元の位置より奥に表示される。

通常、視点の位置は描画の中心にあります。この位置は、例えば複数の描画が同じパースペクティブを共有すべき場合など、perspective-originを設定することで変更できます。

異なるperspective-originの図

perspective-originを上方に動かした効果の図。

パースペクティブ行列は次のように計算されます:

  1. 恒等行列で開始。

  2. perspective-originの算出X,Yで平行移動。

  3. perspective()変形関数から得られる行列を、perspectiveプロパティの値を長さとして乗算する。

  4. perspective-originの算出X,Yの負値で平行移動。

この例は、パースペクティブを使うことで三次元変形がより現実的に見えることを示します。

<style>
div {
  height: 150px;
  width: 150px;
}
.container {
  perspective: 500px;
  border: 1px solid black;
}
.transformed {
  transform: rotateY(50deg);
}
</style>

<div class="container">
  <div class="transformed"></div>
</div>
rotateY変形とperspectiveを持つdiv

内側の要素は前の例と同じ変形ですが、今回は親要素のperspectiveプロパティによってレンダリングが影響されます。perspectiveはZ座標が正(ユーザーに近い)頂点のX,Yを拡大し、奥(負のZ座標)は縮小することで奥行き感を生みます。

4.1.2. 3Dレンダリングコンテキスト

この節では、3D 変形および transform-style プロパティを使用する内容のレンダリングモデルを規定する。このモデルを説明するため、 「3D レンダリングコンテキスト」という概念を導入する。

3D レンダリング コンテキストとは、共通の祖先を根とし、3D 変形のレンダリングにおいて 共通の三次元座標系を共有するとみなされる要素の集合である。3D レンダリングコンテキスト内の要素を前面から背面へ レンダリングする順序は、その三次元空間内の z 位置に依存し、さらに、 それらの要素の 3D 変形によって要素同士が交差する場合は、交差した状態でレンダリングされる。

その三次元空間内における各要素の位置は、指定された要素から 3D レンダリングコンテキストを確立する要素まで、変換行列を累積することによって決定される。

要素は次のように 3D レンダリングコンテキストを確立し、それに参加する。

一部の CSS プロパティには、「グループ化」を強制するとみなされる値がある。これらの値では、要素と その子孫を、他の要素と合成する前にグループとしてレンダリングする必要がある。これには、不透明度、 フィルター、およびクリッピングに影響するプロパティが含まれる。該当するプロパティ値は、グループ化プロパティ値に一覧されている。したがって、transform-style:preserve-3d の要素に使用されると、使用値が flat に変更され、その要素が 3D レンダリングコンテキストを生成または拡張することを妨げる。

3D レンダリングコンテキストでは、要素のレンダリングおよび並べ替えを次のように行う。

  1. 3D レンダリングコンテキストを確立する要素と、 その 3D レンダリングコンテキストに参加するその他の各 3D 変形要素は、 それぞれ独自の平面にレンダリングされる。 この平面には、 要素の背景、ボーダー、その他のボックス装飾、内容、 および子孫要素が含まれるが、 独自の平面を持つ子孫要素(およびその子孫)は除外される。 このレンダリングは、CSS 2.1、 付録 E、E.2 節「描画順序」に従って行われる。

  2. この平面の集合に対して、 ニューウェルのアルゴリズムに従い、 累積 3D 変換行列によって平面を変形したうえで交差処理を行う。 同一平面上にある3D 変形要素は、描画順序に従ってレンダリングされる。

2D変形要素を独自の平面にしなくても問題ないか?

注: この仕様では以前、確立要素の背景・枠線などの装飾が3Dシーン全体の背後に描画されると定義していましたが、#6238で変更されました。今後3Dレンダリングコンテキストの定義が変更された場合は元に戻す検討もありえます。

なお、変形で負のz成分を持つ要素は、確立要素のコンテンツや未変形の子孫の背後に描画され、3D変形要素は未変形要素や内容と相互に貫通する場合があります。

注: 3Dレンダリングコンテキスト内の3D変形要素はすべて奥行き順でソート・交差するため、兄弟のように描画されます。transform-style: preserve-3dの効果は、3Dレンダリングコンテキスト内の3D変形要素を確立要素まで持ち上げて描画するが、累積3D変換行列で変形したまま描画される、というイメージです。

<style>
div {
  height: 150px;
  width: 150px;
}
.scene {
  background-color: rgba(0, 0, 0, 0.3);
  border: 1px solid black;
  perspective: 500px;
}
.container {
  transform-style: preserve-3d;
}
.container > div {
  position: absolute;
  left: 0;
}
.container > :first-child {
  transform: rotateY(45deg);
  background-color: orange;
  top: 10px;
  height: 135px;
}
.container > :last-child {
  transform: translateZ(40px);
  background-color: rgba(0, 0, 255, 0.6);
  top: 50px;
  height: 100px;
}
</style>

<div class="scene">
  <div class="container">
    Lorem ipsum dolor sit amet, consectetaur adipisicing elit…
    <div></div>
    <div></div>
  </div>
</div>

この例は、3Dレンダリングコンテキスト内で要素同士が交差できることを示しています。container要素は自身とその2つの子要素のために3Dレンダリングコンテキストを確立し、scene要素はそのコンテキストにパースペクティブを追加します。子要素同士が交差し、オレンジ色の要素はcontainerとも交差します。

交差する兄弟要素。
<style>
div {
  height: 150px;
  width: 150px;
}
.container {
  perspective: 500px;
  border: 1px solid black;
}
.transformed {
  transform: rotateY(50deg);
  background-color: blue;
}
.child {
  transform-origin: top left;
  transform: rotateX(40deg);
  background-color: lime;
}
</style>

<div class="container">
  <div class="transformed">
    <div class="child"></div>
  </div>
</div>

この例は入れ子の3D変形のレンダリングを示します。青いdivは前の例と同じ変形で、親要素のperspectiveで描画が影響されます。ライム色要素もX軸回転(transform-originで上端固定)で3D変形されていますが、ライム要素は親の平面に描画されており、同じ3Dレンダリングコンテキストには属していません。そのためライム要素は短くなるだけで、青い要素から「飛び出して」見えることはありません。

入れ子の3D変形(フラット化)

4.1.3. 変形要素の階層構造

デフォルトでは、変形要素3Dレンダリングコンテキスト を作らず、内容を平坦化して描画します。しかし、共通の三次元空間を共有する変形オブジェクトの階層構造を作るのは有用なため、transform-style プロパティに preserve-3d を指定すればこの平坦化挙動を上書きできます。これにより、変形要素の子孫も同じ3Dレンダリングコンテキストを共有できます。そうした要素の非3D変形子孫は前述Cのステップで要素の平面に描画されますが、同じ3Dレンダリングコンテキストの3D変形要素は独自の平面に「飛び出して」描画されます。

<style>
div {
  height: 150px;
  width: 150px;
}
.container {
  perspective: 500px;
  border: 1px solid black;
}
.transformed {
  transform-style: preserve-3d;
  transform: rotateY(50deg);
  background-color: blue;
}
.child {
  transform-origin: top left;
  transform: rotateX(40deg);
  background-color: lime;
}
</style>

この例は前の例と同じですが、青い要素に transform-style: preserve-3d が追加されています。青い要素はcontainerの3Dレンダリングコンテキストを拡張します。これで青・ライム両方の要素が共通の三次元空間を共有し、ライム要素が親から傾いて飛び出すように描画され、containerのperspectiveの影響も受けます。

入れ子の3D変形(preserve-3dあり)

4.1.4. 累積3D変換行列の計算

要素を3Dレンダリングコンテキストでレンダリングする際に使用される最終的なtransform値は、以下の手順で累積3D変換行列を計算することで得られます:

  1. transformを恒等行列とする。

  2. current elementを変形された要素とする。

  3. parent elementを変形された要素の親要素とする。

  4. current elementが変形された要素の3Dレンダリングコンテキスト内の要素である間、以下を繰り返す:

    1. current elementtransform値がnoneでない場合、current element変換行列transformに前乗算する。

    2. current elementparent elementからのオフセット(スクロールオフセット含む)を表す平行移動行列を計算し、それをtransformに前乗算する。

    3. parent elementperspective値がnoneでない場合、parent elementパースペクティブ行列transformに前乗算する。

    4. current elementparent elementとする。

    5. parent elementcurrent elementの親要素とする。

注: ここで述べるように、累積3D変換行列は、変形された要素やその祖先チェーンにおいて視覚フォーマットモデルによって発生するオフセット(スクロールオフセット含む)を考慮します。これは、その要素が属する3Dレンダリングコンテキストを確立する要素まで含めて処理されます。

4.1.5. 裏面の可視性

三次元変形を使用すると、要素の裏面が 見えるように変形できる。3D 変形された要素は両面に同じ内容を表示するため、裏面は 表面の鏡像のように見える(要素がガラス板に投影されているかのように)。通常、裏面が 観察者の方を向いている要素も表示されたままとなる。しかし、backface-visibility プロパティを使用すると、 裏面が観察者の方を向いているときに要素を不可視にできる。この挙動は「動的」である。 backface-visibility: hidden の要素が、 表面と裏面が交互に見えるようにアニメーションしている場合、その要素は、 表面が観察者の方を向いているときにのみ表示される。

要素の裏面が見えるかどうかは、累積 3D 変換行列を使用して判断されるため、 3D レンダリングコンテキストを確立する要素の親を基準とする。

注記: このプロパティは、トランプを作成する場合のように、 2 つの要素を背中合わせに配置するときに役立つ。このプロパティがない場合、カードを裏返すアニメーションの途中で、 表面と裏面の要素の位置が入れ替わることがある。別の例として、6 個の要素から箱を作成し、 その箱の内側の面だけを表示したい場合が挙げられる。

この例は、クリックで裏返る「カード」要素の作り方を示します。#cardの"transform-style: preserve-3d"指定に注目してください。これは裏返した時に平坦化されるのを防ぐために必要です。

<style>
.body { perspective: 500px; }
#card {
  position: relative;
  height: 300px; width: 200px;
  transition: transform 1s;
  transform-style: preserve-3d;
}
#card.flipped {
  transform: rotateY(180deg);
}
.face {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: silver;
  border-radius: 40px;
  backface-visibility: hidden;
}
.back {
  transform: rotateY(180deg);
}
</style>
<div id="card" onclick="this.classList.toggle('flipped')">
  <div class="front face">Front</div>
  <div class="back face">Back</div>
</div>

backface-visibilityが非変形要素や2D変形要素に与える影響は?独自の平面に分離されて交差するのか?

4.2. パースペクティブ変形ボックスの処理

これは、与えられた行列を使って要素を厳密にどう変形するかを仕様化しようとする最初の試みです。理想的ではないかもしれませんので、実装者からのフィードバックを歓迎します。#912も参照。

累積3D変換行列は、perspectiveプロパティと、transformプロパティに含まれるperspective()変形関数の両方の影響を受けます。

この累積3D変換行列は4×4行列ですが、変形対象は二次元ボックスです。ボックスの各コーナー(a, b)を変形するには、まず(a, b, 0, 1)に行列を適用し、結果として4次元点(x, y, z, w)を得ます。これを以下のように3次元点(x′, y′, z′)に変換します:

w > 0 の場合、(x′, y′, z′) = (x/w, y/w, z/w)。

w = 0 の場合、(x′, y′, z′) = (xn, yn, zn)。nは実装依存の値であり、可能ならx′やy′がビューポートサイズより十分大きくなる値を選ぶべきです。例えば、(5px, 22px, 0px, 0)は(5000px, 22000px, 0px)になるようn=1000など。ただし(0.1px, 0.05px, 0px, 0)にはこのnは小さすぎます。本仕様ではnの値は厳密には定めません。概念的には(x′, y′, z′)は(x, y, z)方向に無限遠です。

変形後のボックスの4つのコーナー全てでw < 0の場合、そのボックスは描画されません。

変形後のボックスのコーナーのうち1〜3個がw < 0の場合、その部分を切り取った多角形に置き換える必要があります。一般的には頂点が3〜5個の多角形となり、ちょうど2つがw = 0、残りはw > 0になります。これらの頂点は前述の規則で3次元点に変換されます。概念的にはw < 0の点は「視点の背後」なので表示すべきではありません。

.transformed {
  height: 100px;
  width: 100px;
  background: lime;
  transform: perspective(50px) translateZ(100px);
}

このボックスの全てのコーナーのz座標がperspectiveより大きくなります。つまりボックスは視点の背後となり、表示されません。数学的には、点(x, y)はまず(x, y, 0, 1)となり、(100px)平行移動で(x, y, 100, 1)となり、perspective適用で(x, y, 100, −1)となります。w座標が負なので表示されません。w < 0のケースを特別扱いしない実装の場合、(−x, −y, −100)として描画され、ボックスが左右反転することになります。

.transformed {
  height: 100px;
  width: 100px;
  background: radial-gradient(yellow, blue);
  transform: perspective(50px) translateZ(50px);
}

この場合、ボックスは視点位置まで移動されます。これはボックスを視点にどんどん近づけて、視野全体を埋めるようなものです。デフォルトのtransform-originはボックスの中心(黄色)なので、画面全体が黄色に覆われます。

数学的には、(x, y)はまず(x, y, 0, 1)となり、(50px)平行移動で(x, y, 50, 1)、perspective適用後は(x, y, 50, 0)となります。中心がtransform-originの場合、左上隅は(−50, −50)なので(−50, −50, 50, 0)となり、非常に左上遠く(例:−5000, −5000, 5000)に変換されます。他のコーナーも同様に遠くへ。ラジアルグラデーションはボックス全体に引き伸ばされ、可視部分は中心ピクセル(黄色)となります。ただしボックスは無限大ではないので、ユーザーはスクロールで青い部分も見ることができます。

.transformed {
  height: 50px;
  width: 50px;
  background: lime;
  border: 25px solid blue;
  transform-origin: left;
  transform: perspective(50px) rotateY(-45deg);
}

ボックスはユーザー側に向けて回転し、左端は固定され、右端が手前に動きます。右端はz = 70.7px付近となり、perspective 50pxより近くなります。そのため右端は消失し(視点の背後)、残りの部分は右方向に無限遠まで伸びます。

数学的には、右上頂点はtransform-originから見て(100, −50)。これを(x, y, 0, 1)に拡張し、変形後はおよそ(70.71, −50, 70.71, −0.4142)となります。w = −0.4142 < 0なので、w < 0部分を切り取る必要があります。新しい右上頂点は(50, −50, 50, 0)となり、さらに遠く(例:5000, −5000, 5000)に変換されます。他のコーナーも同様。結果としてボックスは画面右端を大きく超えて伸びます。

レンダリングされるボックスは有限なので、ユーザーはスクロールで全体を見ることができますが、右端約30pxは表示されません。青い枠は25px幅なので、左・上・下には表示されますが右端には出ません。

1つまたは3つの頂点がw < 0の場合でも同様の手順で処理され、切り取った結果が三角形や五角形になることもあります。

5. 個別変形プロパティ:translatescalerotateプロパティ

translaterotatescaleプロパティは、 著者が単純な変形を個別に指定できるようにします。 これは一般的なユーザーインターフェースの使い方に合った方法であり、 transformtranslate()rotate()scale()の順序を覚えておく必要なく、 それらを独立して画面座標上で動作させることができます。

名前: translate
値: none | <length-percentage> [ <length-percentage> <length>? ]?
初期値: none
適用対象: 変形可能要素
継承: no
パーセンテージ: (最初の値は)reference boxの幅、(2番目は)高さに対して相対
算出値: キーワードnoneまたは算出済み<length-percentage>2つと絶対長さのペア
正規順序: 文法に従う
アニメーション型: 算出値で、ただしnoneは下記参照

translateプロパティは1~3個の値を受け付け、 それぞれX、Y、Z軸での移動量を指定します。 2番目と3番目の値が省略された場合、 デフォルトは0pxです。

3番目の値が省略または0の場合、 これは2次元の移動指定となり、 translate()関数と等価です。 それ以外の場合、 これは3次元の移動指定となり、 translate3d()関数と等価です。

注: 解決値としてのtranslateプロパティは 算出値であり、 getComputedStyle() の結果にはパーセンテージ値も含まれます。

名前: rotate
値: none | <angle> | [ x | y | z | <number>{3} ] && <angle>
初期値: none
適用対象: 変形可能要素
継承: no
パーセンテージ: 該当なし
算出値: キーワードnone、または<angle>と軸(三つの<number>のリスト)
正規順序: 文法に従う
アニメーション型: SLERPで、ただしnoneは下記参照

rotateプロパティは、 要素を回転させる角度と、 オプションで回転軸を受け付けます。

軸はxy、 またはzキーワードで指定でき、 その軸周りに回転します。 これはrotateX()rotateY()rotateZ() 変形関数と等価です。 また、軸は原点中心のベクトル(x,y,z成分の三つの数値)で明示的に指定することもでき、 rotate3d()関数と等価です。

単なる<angle>指定と、 z軸周り(zキーワードや x,y成分が0でz成分が正のベクトル指定)の回転の間に挙動の差はありません。 いずれもrotate()関数と等価な2D回転です。 例えば、rotate: 30degrotate: z 30degrotate: 0 0 1 30degは全て同じ意味です。

名前: scale
値: none | [ <number> | <percentage> ]{1,3}
初期値: none
適用対象: 変形可能要素
継承: no
パーセンテージ: 該当なし
算出値: キーワードnone、または3つの<number>リスト
正規順序: 文法に従う
アニメーション型: 算出値で、ただしnoneは下記参照

scale プロパティは1~3個の値を受け付け、 それぞれX、Y、Z軸の拡大率を指定します。

Y値が指定されない場合、 X値と同じになります。

Z値が指定されない場合、 デフォルトは1です。

3番目の値が省略、1100%のいずれかの場合、 これは2次元の拡大指定で、 scale()関数と等価です。 それ以外の場合は 3次元の拡大指定となり、 scale3d()関数と等価です。

3番目の値が省略でも、1100%でも挙動に違いはありません。

<percentage><number>と等価です。 例えばscale: 100%scale: 1と等価です。 シリアライズ時は数値が使われます。


3つのプロパティはいずれも (初期値としても) none を受け付け、 これは変形を全く行いません。 特にこの値は 積み重ねコンテキストやすべての子孫の包含ブロックを作りませんが、 それ以外の値 (translate: 0pxのような「恒等」変形も含む)は 通常通り積み重ねコンテキストやすべての子孫の包含ブロックを作ります。

translaterotatescaleがアニメートやトランジションしていて、from値またはto値(両方でない)がnoneの場合、noneは同等の恒等値(translateは0px、rotateは0deg、scaleは1)に置き換えられます。

5.1. シリアライズ

これらのプロパティは、「変形なし」と「変形あり」という2つの異なる挙動モードがあるため、 シリアライズ時はその点を考慮する必要があります:

translateの場合

移動が指定されている場合、 プロパティは1個〜3個の値でシリアライズされます。 (通常通り、2番目と3番目の値が0px(デフォルト)なら、あるいは3番目だけが0pxなら、 それらの0px値はシリアライズ時に省略します)。

元々noneが指定されていた場合に限り、キーワードnoneとしてシリアライズされなければなりません。 (単位変形は対象外です;その場合は0pxとしてシリアライズされなければなりません。)

rotateの場合

z軸周り(2D)の回転が指定されている場合、 プロパティは<angle>のみでシリアライズされます。

その他の回転が指定された場合、 軸も含めてシリアライズされます。 軸がxまたはyと平行な場合は、該当キーワードでシリアライズされます。

元々noneが指定されていた場合に限り、キーワードnoneとしてシリアライズされなければなりません。 (単位変形は対象外です;その場合は0degとしてシリアライズされなければなりません。)

scaleの場合

拡大率が指定されている場合、 プロパティは1個〜3個の値だけでシリアライズされます。 通常通り、3番目の値が1(デフォルト)なら、シリアライズ時に省略します。 3番目が省略され、かつ2番目が1番目と同じ(デフォルト)なら、2番目もシリアライズ時に省略します。

元々noneが指定されていた場合に限り、キーワードnoneとしてシリアライズされなければなりません。 (単位変形は対象外です;その場合は1としてシリアライズされなければなりません。)

6. 現在の変換行列

変換行列の計算手順は以下の通り修正されます:

変換行列は、transformtransform-origintranslaterotatescaleoffsetプロパティから次のように計算されます:

  1. 恒等行列で開始する。

  2. transform-originの算出X、Y、Zで平行移動する。

  3. translateの算出X、Y、Zで平行移動する。

  4. <angle>で指定軸周りに回転する(rotate)。

  5. scaleの算出X、Y、Zで拡大する。

  6. offsetで指定した変形で平行移動・回転する。

  7. transform内の変形関数を左から右に乗算する。

  8. transform-originの算出X、Y、Zの負値で平行移動する。

7. transform-styleプロパティ

名前: transform-style
値: flat | preserve-3d
初期値: flat
適用対象: 変形可能要素
継承: no
パーセンテージ: N/A
算出値: 指定キーワード
正規順序: 文法に従う
アニメーション型: 離散
使用値: グループ化プロパティが存在する場合はflat、それ以外は指定キーワード

transform-stylepreserve-3dとなった変形可能要素は、積み重ねコンテキストとすべての子孫の包含ブロックを両方確立します。 使用値がpreserve-3dの場合、3Dレンダリングコンテキストも確立または拡張します。

7.1. グループ化プロパティ値

次のCSSプロパティ値は、子孫要素を平坦化して描画した後でないと適用できないため、preserve-3dでも使用値をflatに強制します。

8. perspectiveプロパティ

名前: perspective
値: none | <length [0,∞]>
初期値: none
適用対象: 変形可能要素
継承: no
パーセンテージ: N/A
算出値: キーワードnoneまたは絶対長さ
正規順序: 文法に従う
アニメーション型: 算出値で
<length [0,∞]>

投影中心までの距離。

投影中心までの距離で正しいか要検証。

非常に小さい<length> 値は奇妙な描画結果や変形計算の精度問題を引き起こすため、 1px未満の値はレンダリング上は1pxとして扱う必要があります。 (この制限は値そのものには影響せず、 スタイルシート内でperspective: 0;と指定しても シリアライズ時は0となります。)

none

パースペクティブ変形は適用されません。効果は数学的には無限大<length> 値と同様です。すべてのオブジェクトはキャンバス上で平坦に見えます。

このプロパティをnone以外で使うと積み重ねコンテキストが確立されます。 また、すべての子孫の包含ブロックも確立されます(transformプロパティと同様)。

perspectiveで積み重ねコンテキスト・包含ブロックが必要かは微妙だが、Web互換性のため変更できないかも。

perspectiveおよびperspective-originの値は、上述の通りパースペクティブ行列の計算に使用されます。

9. perspective-originプロパティ

perspective-originプロパティはperspectiveプロパティの原点を定義します。これは、要素の子に対して、視点(X・Y座標)がどこにあるかを設定するものです。

名前: perspective-origin
値: <position>
初期値: 50% 50%
適用対象: 変形可能要素
継承: no
パーセンテージ: reference boxのサイズを参照
算出値: background-positionを参照
正規順序: 文法どおり
アニメーション型: 算出値による

perspectiveperspective-originの値は、上記で述べたパースペクティブ行列の計算に使われます。

perspective-originの値は、reference boxの左上隅からのオフセットを表します。

<percentage>

水平方向のパーセンテージはreference boxの幅に対し、垂直方向のパーセンテージはreference boxの高さに対して相対です。水平・垂直の値は共に、reference boxの左上隅からのオフセットを表します。

<length>

長さ値は固定長のオフセットを与えます。水平・垂直ともに、reference boxの左上隅からのオフセットです。

top

1つまたは2つ値が指定された場合、垂直位置は0%となり、それ以外の場合は次のオフセットの基準が上端となります。

right

1つまたは2つ値が指定された場合、水平方向は100%となり、それ以外の場合は次のオフセットの基準が右端となります。

bottom

1つまたは2つ値が指定された場合、垂直方向は100%となり、それ以外の場合は次のオフセットの基準が下端となります。

left

1つまたは2つ値が指定された場合、水平方向は0%となり、それ以外の場合は次のオフセットの基準が左端となります。

center

水平方向が指定されていない場合は50%left 50%)、垂直方向が指定されていれば50%top 50%)となります。

perspective-originプロパティはheightと同じく解決値の特別扱いプロパティです。[CSSOM]

10. backface-visibilityプロパティ

名前: backface-visibility
値: visible | hidden
初期値: visible
適用対象: 変形可能要素
継承: no
パーセンテージ: N/A
算出値: 指定キーワード
正規順序: 文法どおり
アニメーション型: 離散

backface-visibility: hiddenが指定された要素の可視性は、次のように判定されます:

  1. 要素の累積3D変換行列を計算する。

  2. 行列の3行3列目の成分が負の場合、その要素は非表示にする。そうでなければ表示する。

backface-visibilityはm33だけでは判定できません。#917参照。

注: この定義の理由は以下です。要素は厚みが無視できる長方形と仮定します。未変形状態の前面は(x, y, ε)、背面は(x, y, −ε)です(εは非常に小さい値)。変形後、前面がユーザー側(z値大)か背面がユーザー側かを判定します。前面のz座標は m13x + m23y + m33ε + m43、背面は m13x + m23y − m33ε + m43です。前者が後者より大きいのは m33 > 0 のときのみです(0の場合は前面・背面が等距離で、たとえば90度回転で不可視となるので気にしなくてよい)。

11. SVGと3D変形関数

本仕様では、三次元変形関数が以下のコンテナ要素に適用されることを明示的に要求します: a, g, svg, すべてのグラフィック要素、すべてのグラフィック参照要素、SVGの foreignObject 要素。

三次元変形関数と、perspectiveperspective-origintransform-stylebackface-visibilityは、以下の要素では使用できません: clipPath, linearGradient, radialGradient および pattern。 変形リストに三次元変形関数が含まれている場合、変形リスト全体は無視されます。これらのプロパティ値もすべて無視されます。これらの要素に含まれる変形可能要素は三次元変形関数を持てます。 clipPath, mask, pattern 要素は、子孫要素を平坦化して描画した後で適用する必要があり、そのためtransform-style: preserve-3dの挙動を上書きします。

vector-effectプロパティがnon-scaling-strokeに設定され、かつオブジェクトが3Dレンダリングコンテキスト内にある場合、ストロークには影響しません。

SVGにおける3D変形関数の構文を正式に記述すること(2D関数の仕様のように)。

12. 変形関数

transformプロパティの値は、<transform-function>のリストです。許可される変形関数のセットは以下です。本仕様で<angle>が使われている場合、0に等しい<number>も許可され、0度の角度と同じように扱われます。水平方向の移動のパーセンテージはreference boxの幅に、垂直方向はreference boxの高さに対して相対です。 scale関数のパーセンテージは数値と等価で、指定値では数値としてシリアライズされます。 例:scale3d(50%, 100%, 150%)scale3d(0.5, 1, 1.5)としてシリアライズされます。

12.1. 2D変形関数

[css-transforms-1]で定義されたscale関数は、パーセンテージも対応するようになりました。

scale() = scale( [ <number> | <percentage> ]#{1,2} )
scaleX() = scaleX( [ <number> | <percentage> ] )
scaleY() = scaleY( [ <number> | <percentage> ] )

css-transforms-1で定義された通りですが、上記の説明のようにパーセンテージも受け付けます。

12.2. 3D変形関数

以下の3d変形関数において、<zero>0degと同じように扱われます(単位なし0角度はレガシー互換性のため保持)。

matrix3d() = matrix3d( <number>#{16} )

列優先順の 16 個の値からなる 4×4 同次行列として 3D 変形を指定する。

translate3d() = translate3d( <length-percentage> , <length-percentage> , <length> )

ベクトル [tx,ty,tz] による 3D 平行移動を指定する。tx、ty および tz は、それぞれ 1 番目、2 番目、および 3 番目の平行移動値パラメーターである。

translateZ() = translateZ( <length> )

指定された Z 方向の量を持つベクトル [0,0,tz] による 3D 平行移動を指定する。

scale3d() = scale3d( [ <number> | <percentage> ]#{3} )

3 個のパラメーターによって記述される拡大縮小ベクトル [sx,sy,sz] による 3D 拡大縮小演算を指定する。

scaleZ() = scaleZ( [ <number> | <percentage> ] )

拡大縮小ベクトル [1,1,sz] を使用する 3D 拡大縮小演算を指定する。ここで、 sz はパラメーターとして指定される。

rotate3d() = rotate3d( <number> , <number> , <number> , [ <angle> | <zero> ] )

最初の 3 個のパラメーターによって記述される [x,y,z] 方向ベクトルを軸として、最後のパラメーターで 指定された角度による 3D 回転を指定する。[0,0,0] など、 正規化できない方向ベクトルの場合、回転は適用されない。

注記: ベクトルの終点から 原点に向かって見たとき、回転は時計回りになる。

rotateX() = rotateX( [ <angle> | <zero> ] )

rotate3d(1, 0, 0, <angle>) と同じである。

rotateY() = rotateY( [ <angle> | <zero> ] )

rotate3d(0, 1, 0, <angle>) と同じである。

rotateZ() = rotateZ( [ <angle> | <zero> ] )

rotate3d(0, 0, 1, <angle>) と同じであり、これは 2D 変形 rotate(<angle>) と等価な 3D 変形である。

perspective() = perspective( <length [0,∞]> | none )

透視投影行列を指定する。この行列は、 各点の Z 値に基づいて X および Y 方向に拡大縮小し、正の Z 値を持つ点を 原点から遠ざかるように、負の Z 値を持つ点を原点に近づくように拡大縮小する。z=0 平面上の点は変更されない。 パラメーターは、z=0 平面から観察者までの距離を表す。値が小さいほど、 ピラミッドはより平坦になり、その結果、遠近効果がより顕著になる。たとえば、1000px という値では 適度な遠近短縮が生じ、200px という値では極端な遠近短縮が生じる。

深度値が 1px 未満の場合、 レンダリングの目的、 transform解決値の計算、 および補間の端点として使用される場合には、 1px として扱わなければならない。

注記: 1px 未満の値に関する上記の規則は、 perspective() 関数を 行列に変換する必要がある場合を対象とすることを意図している。

12.3. 変形関数のプリミティブと派生

一部の変形関数は、より汎用的な変形関数で表現できます。これらの変形関数は派生変形関数と呼び、汎用的な変形関数はプリミティブ変形関数と呼びます。三次元プリミティブとその派生変形関数は以下の通りです:

translate3d()
<translateX()><translateY()>translateZ()<translate()>のためのものです。
scale3d()
<scaleX()><scaleY()>scaleZ()<scale()>のためのものです。
rotate3d()
<rotate()>rotateX()rotateY()rotateZ()のためのものです。

二次元プリミティブと三次元プリミティブの両方を持つ派生変形関数の場合、使用されるプリミティブは文脈によって決まります。詳細はプリミティブと派生変形関数の補間を参照してください。

13. 行列の補間

2つの行列を補間する場合、各行列は対応する移動量、回転、拡大率、歪み(skew)、(3D行列の場合は)パースペクティブに分解されます。分解された各行列の対応する成分を数値的に補間し、最終ステップで再度行列に合成します。

13.1. 3D行列の補間

13.1.1. 3D行列の分解

以下の疑似コードは「Graphics Gems II(Jim Arvo編集)」の"unmatrix"手法を基にしていますが、オイラー角の代わりにクォータニオンを用いることでギンバルロック問題を回避しています。

次の疑似コードは4x4同次行列に対して動作します:

Input:  matrix      ; a 4x4 matrix
Output: translation ; a 3 component vector
        scale       ; a 3 component vector
        skew        ; skew factors XY,XZ,YZ represented as a 3 component vector
        perspective ; a 4 component vector
        quaternion  ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can


// Normalize the matrix.
if (matrix[3][3] == 0)
    return false

for (i = 0; i < 4; i++)
    for (j = 0; j < 4; j++)
        matrix[i][j] /= matrix[3][3]

// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix

for (i = 0; i < 3; i++)
    perspectiveMatrix[i][3] = 0

perspectiveMatrix[3][3] = 1

if (determinant(perspectiveMatrix) == 0)
    return false

// First, isolate perspective.
if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
    // rightHandSide is the right hand side of the equation.
    rightHandSide[0] = matrix[0][3]
    rightHandSide[1] = matrix[1][3]
    rightHandSide[2] = matrix[2][3]
    rightHandSide[3] = matrix[3][3]

    // Solve the equation by inverting perspectiveMatrix and multiplying
    // rightHandSide by the inverse.
    inversePerspectiveMatrix = inverse(perspectiveMatrix)
    transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
    perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
else
    // No perspective.
    perspective[0] = perspective[1] = perspective[2] = 0
    perspective[3] = 1

// Next take care of translation
for (i = 0; i < 3; i++)
    translate[i] = matrix[3][i]

// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
for (i = 0; i < 3; i++)
    row[i][0] = matrix[i][0]
    row[i][1] = matrix[i][1]
    row[i][2] = matrix[i][2]

// Compute X scale factor and normalize first row.
scale[0] = length(row[0])
row[0] = normalize(row[0])

// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = dot(row[0], row[1])
row[1] = combine(row[1], row[0], 1.0, -skew[0])

// Now, compute Y scale and normalize 2nd row.
scale[1] = length(row[1])
row[1] = normalize(row[1])
skew[0] /= scale[1];

// Compute XZ and YZ shears, orthogonalize 3rd row
skew[1] = dot(row[0], row[2])
row[2] = combine(row[2], row[0], 1.0, -skew[1])
skew[2] = dot(row[1], row[2])
row[2] = combine(row[2], row[1], 1.0, -skew[2])

// Next, get Z scale and normalize 3rd row.
scale[2] = length(row[2])
row[2] = normalize(row[2])
skew[1] /= scale[2]
skew[2] /= scale[2]

// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip.  If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = cross(row[1], row[2])
if (dot(row[0], pdum3) < 0)
    for (i = 0; i < 3; i++)
        scale[i] *= -1;
        row[i][0] *= -1
        row[i][1] *= -1
        row[i][2] *= -1

// Now, get the rotations out
quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][1] - row[2][2], 0))
quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][1] - row[2][2], 0))
quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][1] + row[2][2], 0))
quaternion[3] = 0.5 * sqrt(max(1 + row[0][0] + row[1][1] + row[2][2], 0))

if (row[2][1] > row[1][2])
    quaternion[0] = -quaternion[0]
if (row[0][2] > row[2][0])
    quaternion[1] = -quaternion[1]
if (row[1][0] > row[0][1])
    quaternion[2] = -quaternion[2]

return true

13.1.2. 分解した3D行列値の補間

分解したtranslation、scale、skew、perspectiveの各成分は、元の行列と移動先行列の対応する成分同士で線形補間されます。

注: 例えば、ソース行列のtranslate[0]と、デスティネーション行列のtranslate[0]は数値的に補間され、その結果がアニメーションする要素の移動量に設定されます。

分解したソース行列のクォータニオンは、分解したデスティネーション行列のクォータニオンと球面線形補間(Slerp)で補間されます。詳細は下記の疑似コード:

Input:  quaternionA   ; a 4 component vector
        quaternionB   ; a 4 component vector
        t             ; interpolation parameter with 0 <= t <= 1
Output: quaternionDst ; a 4 component vector


product = dot(quaternionA, quaternionB)

// Clamp product to -1.0 <= product <= 1.0
product = min(product, 1.0)
product = max(product, -1.0)

if (abs(product) == 1.0)
   quaternionDst = quaternionA
   return

theta = acos(product)
w = sin(t * theta) / sqrt(1 - product * product)

for (i = 0; i < 4; i++)
  quaternionA[i] *= cos(t * theta) - product * w
  quaternionB[i] *= w
  quaternionDst[i] = quaternionA[i] + quaternionB[i]

return

13.1.3. 3D行列への再合成

補間後、得られた値はユーザースペースで要素を変形するために使用されます。これらの値を使って4x4行列に再合成する方法の一例を下記の疑似コードに示します:

Input:  translation ; a 3 component vector
        scale       ; a 3 component vector
        skew        ; skew factors XY,XZ,YZ represented as a 3 component vector
        perspective ; a 4 component vector
        quaternion  ; a 4 component vector
Output: matrix      ; a 4x4 matrix

Supporting functions (matrix is a 4x4 matrix):
  matrix  multiply(matrix a, matrix b)   returns the 4x4 matrix product of a * b

// apply perspective
for (i = 0; i < 4; i++)
  matrix[i][3] = perspective[i]

// apply translation
for (i = 0; i < 4; i++)
  for (j = 0; j < 3; j++)
    matrix[3][i] += translation[j] * matrix[j][i]

// apply rotation
x = quaternion[0]
y = quaternion[1]
z = quaternion[2]
w = quaternion[3]

// Construct a composite rotation matrix from the quaternion values
// rotationMatrix is a identity 4x4 matrix initially
rotationMatrix[0][0] = 1 - 2 * (y * y + z * z)
rotationMatrix[0][1] = 2 * (x * y - z * w)
rotationMatrix[0][2] = 2 * (x * z + y * w)
rotationMatrix[1][0] = 2 * (x * y + z * w)
rotationMatrix[1][1] = 1 - 2 * (x * x + z * z)
rotationMatrix[1][2] = 2 * (y * z - x * w)
rotationMatrix[2][0] = 2 * (x * z - y * w)
rotationMatrix[2][1] = 2 * (y * z + x * w)
rotationMatrix[2][2] = 1 - 2 * (x * x + y * y)

matrix = multiply(matrix, rotationMatrix)

// apply skew
// temp is a identity 4x4 matrix initially
if (skew[2])
    temp[2][1] = skew[2]
    matrix = multiply(matrix, temp)

if (skew[1])
    temp[2][1] = 0
    temp[2][0] = skew[1]
    matrix = multiply(matrix, temp)

if (skew[0])
    temp[2][0] = 0
    temp[1][0] = skew[0]
    matrix = multiply(matrix, temp)

// apply scale
for (i = 0; i < 3; i++)
  for (j = 0; j < 4; j++)
    matrix[i][j] *= scale[i]

return

14. プリミティブおよび派生変形関数の補間

同じ名前かつ同じ引数数を持つ2つの変形関数は、変換なしに数値的に補間されます。計算された値は、同じ変形関数型・同じ引数数になります。<matrix()><matrix3d()><perspective()>には特別なルールが適用されます。

<matrix()>matrix3d()perspective()の変形関数は、まず4x4行列に変換され、その後行列の補間の節で定義された方法で補間されます。

プリミティブrotate3d()で補間する場合、変形関数の方向ベクトルをまず正規化します。正規化ベクトルが一致せず、かつ両方の回転角が非ゼロの場合、変形関数はまず4x4行列に変換され、その後行列の補間の節で定義された方法で補間されます。そうでなければ、回転角だけ数値的に補間され、非ゼロ角の回転ベクトル、または両方ゼロの場合は(0, 0, 1)が使われます。

translate(0)translate(100px)は同じ型・同じ引数数なので数値的に補間できます。translateX(100px)は型が異なり、translate(100px, 0)は引数数が異なるので、これらの変形関数は変換なしでは補間できません。

同じプリミティブを共有する型違いの変形関数、または同じ型で引数数が異なる変形関数も補間可能です。両方の変形関数はまず共通のプリミティブに変換され、その後数値的に補間されます。算出値はプリミティブ型+補間された引数となります。

次の例は、divボックスにマウスオーバーした際にtranslateX(100px)からtranslateY(100px)へ3秒で遷移する様子を示します。両方の変形関数は同じプリミティブtranslate()由来なので補間可能です。

div {
  transform: translateX(100px);
}

div:hover {
  transform: translateY(100px);
  transition: transform 3s;
}

遷移中は両方の変形関数が共通のプリミティブに変換されます。translateX(100px)translate(100px, 0)に、translateY(100px)translate(0, 100px)に変換され、以降数値的に補間できます。

両方の変形関数が二次元空間のプリミティブを共有する場合は、両方とも二次元プリミティブに変換されます。一方または両方が三次元変形関数なら、共通の三次元プリミティブが使われます。

この例では、二次元変形関数が三次元変形関数へアニメーションされます。共通プリミティブはtranslate3d()です。

div {
  transform: translateX(100px);
}

div:hover {
  transform: translateZ(100px);
  transition: transform 3s;
}

まずtranslateX(100px)translate3d(100px, 0, 0)に、translateZ(100px)translate3d(0, 0, 100px)に変換されます。その後、両方の変形関数が数値的に補間されます。

15. 変形リストの加算と累積

2つの変形リスト VaVbの加算は、リスト結合として定義され、 VresultVbVa追加されたものとなります。
2つの変形リスト VaVbの累積は、補間と同じ手順に従い、 変形関数の一致を含めてリストを恒等変形関数でパディングしたり、 none恒等変形関数に変換したり、 両方の引数を必要に応じて行列に変換したりします(CSS Transforms 1 § 11 変形の補間参照)。 ただし、個々のパラメータの補間の代わりに、 算術的加算で値を組み合わせます—ただし恒等変形関数で値が1となるパラメータ(例:scaleの値や行列要素m11, m22, m33, m44)は oneベース値の累積で合成します:

Vresult = Va + Vb - 1

上記定義は、累積の意図を維持します。つまりVbVaからの差分として機能し、次のようなアニメーションが:
div.animate(
  { transform: ['scale(1)', 'scale(2)'] },
  {
    duration: 1000,
    easing: 'ease',
  }
);

次のように拡張した場合も期待通りに動作します:

div.animate(
  { transform: ['scale(1)', 'scale(2)'] },
  {
    duration: 1000,
    easing: 'ease',
    iterations: 5,
    iterationComposite: 'accumulate',
  }
);

15.1. 加算のニュートラル要素

一部のアニメーションでは加算のニュートラル要素が必要です。変形関数の場合、これは0のスカラーまたは0スカラーのリストです。例としてはtranslate(0)translate3d(0, 0, 0)translateX(0)translateY(0)translateZ(0)scale(0)scaleX(0)scaleY(0)scaleZ(0)rotate(0)rotate3d(vx, vy, vz, 0)(vは文脈依存ベクトル)、rotateX(0)rotateY(0)rotateZ(0)skew(0, 0)skewX(0)skewY(0)matrix(0, 0, 0, 0, 0, 0)matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)perspective(none)などが挙げられます。

注: 加算のニュートラル要素へのアニメーションや、そこからのアニメーションでは、<matrix()>matrix3d()perspective()は離散アニメーションにフォールバックします(§ 13 行列の補間参照)。

16. 変形関数の数学的記述

数学的には、すべての変形関数は次の形の4x4変換行列として表現できます:

$$\begin{bmatrix} m11 & m21 & m31 & m41 \\ m12 & m22 & m32 & m42 \\ m13 & m23 & m33 & m43 \\ m14 & m24 & m34 & m44 \end{bmatrix}$$

行列上の1単位の移動は、要素のローカル座標系における1ピクセルに相当します。

17. SVGの transform 属性

本仕様では、新たな提示属性として transform-originperspectiveperspective-origintransform-stylebackface-visibilityも導入されます。

新しく導入された提示属性の値は、SVGデータ型 [SVG11] の構文規則に従ってパースされます。

18. SVGアニメーション

18.1. animate 要素および set 要素

導入された提示属性 perspectiveperspective-origintransform-stylebackface-visibilityはアニメーション可能です。transform-stylebackface-visibilityは非加算型です。

19. その他の課題

https://lists.w3.org/Archives/Public/www-style/2015Mar/0371.html の通り、WGは変形を統一的な「scale」に分解する式を追加することを決定しました (仕様ではscaleX/Y/Zへの分解方法は既に定義済み)。 これはSVGのnon-scaling stroke仕様などで利用されます。式の定義はこちら

20. セキュリティおよびプライバシーに関する考慮事項

この仕様では新たなセキュリティやプライバシーの考慮事項は導入されません。

変更履歴

最近の変更

2020年3月3日WDからの重要な変更点:

適合性

文書の規約

適合要件は、記述的な断定とRFC 2119用語の組み合わせで表現されます。重要語「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」は、この文書の規範部分ではRFC 2119の定義通りに解釈されます。 ただし、可読性のため、これらの単語は本仕様ではすべて大文字ではありません。

この仕様の本文は、明示的に非規範とされた部分、例、注記を除きすべて規範です。[RFC2119]

この仕様の例は「for example」で始まるか、class="example"で本文から分離されて示されます:

これは情報提供のための例です。

情報提供的注記は「Note」で始まり、class="note"で本文から分離されます:

注:これは情報的な注記です。

勧告文は規範セクション内で特別な注意を促すようスタイルされ、他の規範本文とは<strong class="advisement">で分離されます。例: UAはアクセシビリティ代替を必ず提供しなければなりません。

適合クラス

本仕様への適合は、3つの適合クラスで定義されます:

スタイルシート
CSS スタイルシート
レンダラー
UA(ユーザーエージェント)は、スタイルシートの意味を解釈し、使用された文書をレンダリングします。
オーサリングツール
UA(ユーザーエージェント)は、スタイルシートを作成します。

スタイルシートが本仕様に適合するには、 このモジュールで定義された構文を使用するすべての記述が、ジェネリックCSS構文および本モジュールで定義された各機能の個別構文に従い有効である必要があります。

レンダラーが本仕様に適合するには、 スタイルシートを適切な仕様に従って解釈することに加え、 本仕様で定義されたすべての機能を正しくパースし、 文書を適切にレンダリングする必要があります。ただし、デバイスの制限によりUAが文書を正しくレンダリングできない場合でも、UAは非適合とはなりません(例:モノクロモニターで色をレンダリングする必要はありません)。

オーサリングツールが本仕様に適合するには、 ジェネリックCSS構文および本モジュールで定義された各機能の個別構文に従い構文的に正しいスタイルシートを書き、本モジュールで定義されるスタイルシートのその他の適合要件も満たす必要があります。

部分的な実装

著者がフォールバック値を割り当てるために前方互換なパース規則を活用できるよう、CSSレンダラーは、利用可能なレベルのサポートがないat-rule、プロパティ、プロパティ値、キーワード、その他の構文を無効とみなし(適切に無視)する必要があります。特に、UAは複数値プロパティ宣言内でサポートされていない値のみ選択的に無視し、サポートされている値のみを有効にするべきではありません:いずれかの値が無効とみなされた場合(サポート外値は必ずそうなります)、CSSでは宣言全体を無視する必要があります。

不安定および独自機能の実装

将来の安定CSS機能との衝突を避けるため、CSSWGはベストプラクティスに従って不安定な機能独自拡張を実装することを推奨します。

非実験的な実装

仕様がCandidate Recommendation段階に到達すると、非実験的な実装が可能となり、実装者は正しく仕様通りに実装できると証明できるCRレベル機能については、プレフィックスなしの実装をリリースすべきです。

CSSの相互運用性を確立・維持するため、CSSWGは、非実験的CSSレンダラーがCSS機能のプレフィックスなし実装をリリースする前に、W3Cに実装報告(必要に応じてその報告で使用したテストケースも)を提出するよう要請しています。W3Cに提出されたテストケースはCSSWGによってレビュー・修正される場合があります。

テストケースや実装報告提出の詳細は、CSSWGのWebサイト(https://www.w3.org/Style/CSS/Test/)で確認できます。 質問はpublic-css-testsuite@w3.orgメーリングリストへ。

索引

この仕様で定義された用語

参照によって定義された用語

参考文献

規範参考文献

[COMPOSITING-1]
Rik Cabanier; Nikos Andronikos. Compositing and Blending Level 1. 2015年1月13日. CR. URL: https://www.w3.org/TR/compositing-1/
[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2021年7月26日. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2021年10月15日. WD. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley. CSS Color Module Level 4. 2021年6月1日. WD. URL: https://www.w3.org/TR/css-color-4/
[CSS-CONTAIN-1]
Tab Atkins Jr.; Florian Rivoal. CSS Containment Module Level 1. 2020年12月22日. REC. URL: https://www.w3.org/TR/css-contain-1/
[CSS-MASKING-1]
Dirk Schulze; Brian Birtles; Tab Atkins Jr.. CSS Masking Module Level 1. 2021年8月5日. CR. URL: https://www.w3.org/TR/css-masking-1/
[CSS-OVERFLOW-3]
David Baron; Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 2020年6月3日. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-TRANSFORMS-1]
Simon Fraser; 他. CSS Transforms Module Level 1. 2019年2月14日. CR. URL: https://www.w3.org/TR/css-transforms-1/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2019年6月6日. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2021年10月16日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS21]
Bert Bos; 他. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011年6月7日. REC. URL: https://www.w3.org/TR/CSS21/
[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021年8月26日. WD. URL: https://www.w3.org/TR/cssom-1/
[FILTER-EFFECTS-1]
Dirk Schulze; Dean Jackson. Filter Effects Module Level 1. 2018年12月18日. WD. URL: https://www.w3.org/TR/filter-effects-1/
[HTML]
Anne van Kesteren; 他. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MOTION-1]
Dirk Schulze; 他. Motion Path Module Level 1. 2018年12月18日. WD. URL: https://www.w3.org/TR/motion-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SVG11]
Erik Dahlström; 他. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 2011年8月16日. REC. URL: https://www.w3.org/TR/SVG11/
[SVG2]
Amelia Bellamy-Royds; 他. Scalable Vector Graphics (SVG) 2. 2018年10月4日. CR. URL: https://www.w3.org/TR/SVG2/

参考情報

[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. 2020年12月16日. WD. URL: https://www.w3.org/TR/css-contain-2/

プロパティ索引

名前 初期値 適用対象 継承 %ages アニメーション型 正規順序 算出値 使用値
backface-visibility visible | hidden visible 変形可能要素 no N/A 離散 文法どおり 指定キーワード
perspective none | <length [0,∞]> none 変形可能要素 no N/A 算出値で 文法どおり キーワードnoneまたは絶対長さ
perspective-origin <position> 50% 50% 変形可能要素 no reference boxのサイズ参照 算出値で 文法どおり background-position参照
rotate none | <angle> | [ x | y | z | <number>{3} ] && <angle> none 変形可能要素 no n/a SLERP、ただしnoneは下記参照 文法どおり キーワードnone、または軸(三つの<number>リスト)付き<angle>
scale none | [ <number> | <percentage> ]{1,3} none 変形可能要素 no n/a 算出値で、ただしnoneは下記参照 文法どおり キーワードnoneまたは3つの<number>リスト
transform-style flat | preserve-3d flat 変形可能要素 no N/A 離散 文法どおり 指定キーワード グループ化プロパティが存在する場合はflat、そうでなければ指定キーワード
translate none | <length-percentage> [ <length-percentage> <length>? ]? none 変形可能要素 no 最初の値はreference boxの幅、2番目は高さに対して相対 算出値で、ただしnoneは下記参照 文法どおり キーワードnoneまたは算出済み<length-percentage>値2つと絶対長さ

課題索引

この文言はCSS Transforms 1の本文に追加するよう修正が必要です。
入れ子の3D変形要素のレンダリング方法(数式含む?)を記述すること
この例は前の説明とつながらない
2D変形要素を独自の平面にしなくても問題ないか?
backface-visibilityが非変形要素や2D変形要素に与える影響は?独自の平面に分離されて交差するのか?
これは、与えられた行列を使って要素を厳密にどう変形するかを仕様化しようとする最初の試みです。理想的ではないかもしれませんので、実装者からのフィードバックを歓迎します。#912も参照。
投影中心までの距離で正しいか要検証。
perspectiveで積み重ねコンテキスト・包含ブロックが必要かは微妙だが、Web互換性のため変更できないかも。
backface-visibilityはm33だけでは判定できません。#917参照。
SVGにおける3D変形関数の構文を正式に記述すること(2D関数の仕様のように)。
https://lists.w3.org/Archives/Public/www-style/2015Mar/0371.html の通り、WGは変形を統一的な「scale」に分解する式を追加することを決定しました (仕様ではscaleX/Y/Zへの分解方法は既に定義済み)。 これはSVGのnon-scaling stroke仕様などで利用されます。式の定義はこちら