Unformed Building

CSS Transformsについてのメモ

公開:
更新:

パーマリンク

既に色々なデモページが作られていたり、実用されていたりするCSS Transformsですが、ちゃんと理解したかったので調べてみました。
まだそんなに自信はありませんが、以前よりは理解できた気がします。
というわけなので、自分が分かる範囲で説明してみたいと思います。

CSS Transformsは2Dと3Dとで別になっていますが、将来的には SVG Transforms と合わせて一つの仕様になるそうです
今はまだ別々なので、それぞれにリンクをしておきます。括弧内は現時点での最新の日付です。

新しくなった仕様が出ていましたので、リンクを追加しておきます。最新版へのリンクです。

CSS Transforms

記事内容に若干の不安があるので、他力本願で申し訳ないですが間違っていたら教えていただけると助かります。

CSS 2D Transforms

transformプロパティ

どのような変形を行うかを指定します。指定はTransform関数で行います。
関数は空白区切りにより複数指定できます。
各関数は指定した順に適用されます。

初期値はnoneです。

translate(tx, ty)

translate()では、要素の移動を行います。
1つ目のパラメータでX方向への移動距離、2つ目のパラメータでY方向への移動距離を指定します。
2つ目のパラメータtyが省略された場合、ty0として計算されます。
指定には<length>形式、または<percentage>を使用します。

以下はサンプルです。
灰色の部分が変形適用前のもの、赤い部分が適用後のものとなります。

transform: translate() sample

.sample1 {
  transform: translate(0);
}

.sample2 {
  transform: translate(20px);
}

.sample3 {
  transform: translate(20px, 20px);
}
デモ「transform: translate() sample」実行結果
translateX(), translateY()

translate()を個別に指定する関数です。
translateX()はX方向へ、translateY()はY方向への移動距離を指定します。

scale(sx, sy)

要素の拡大縮小を行います。
1つ目のパラメータでX方向の比率を、2つ目のパラメータでY方向の比率を指定します。
2つ目のパラメータsyが省略された場合、syは1つ目のパラメータsxと同じ値として計算されます。
また、指定には<number>形式を使用します。

transform: scale() sample

.sample1 {
  transform: scale(0);
}

.sample2 {
  transform: scale(1.2);
}

.sample3 {
  transform: scale(1.2, 0.8);
}
デモ「transform: scale() sample」実行結果
scaleX(), scaleY()

scale()を個別に指定する関数です。
scaleX()でX方向、scaleY()でY方向の比率を指定します。
仮にscaleX()しか指定されていない場合、残るscaleY()の値は1となります。これは逆の場合でも同じです。

rotate()

要素を回転させます。回転の方向は時計回りです。
回転の中心点はtransform-origin(後述)プロパティで指定されたポイントとなります。
パラメータは1つだけで、<angle>形式で指定します。

transform: rotate() sample

.sample1 {
  transform: rotate(0);
}

.sample2 {
  transform: rotate(15deg);
}

.sample3 {
  transform: rotate(-15deg);
}
デモ「transform: rotate() sample」実行結果

skewX(), skewY()

要素を傾斜させます。
skewX()はX軸に沿って指定された分の傾斜を与えます。同様に、skewY()はY軸に沿います。
パラメータは1つだけで、<angle>形式で指定します。

transform: skewX() & transform: skewY() sample

.sample1 {
  transform: skewX(0);
}

.sample2 {
  transform: skewX(15deg);
}

.sample3 {
  transform: skewY(15deg);
}
デモ「ransform:skewX() & transform: skewY()」実行結果

matrix(a, b, c, d, e, f)

行列を使用して変形を指定します。これ1つで上記の指定をまとめて行うこともできます。
非常にややこしいのでCSSしか書かない人が使う機会は少ないかもしれませんが、JavaScriptで変形を操作するときには必要になってきたりします。CSS でrotate()scale()などを使って書いたとしても、JavaScriptからtransformの値を取得すると、このmatrix()になるからです。

この関数には6つのパラメータがあり、変形なしの状態だとmatrix(1, 0, 0, 1, 0, 0)となります。

使用される行列は3×3ですが、うち3つは固定です。

matrix()関数の行列

変形なしの状態の行列はこうなります。

matrix()関数の行列の初期値

この行列が何をするのかですが、この行列を使って座標を算出します。
要素の4つの頂点(要素は矩形なので)を、それぞれ(X, Y, 1)とし、これを行列に乗算して座標を出します。

2D Transforms 行列の計算 1

これを計算します。

2D Transforms 行列の計算 2

このx’y’が変形後の座標になります。
これを各頂点ごとに行い、それぞれの結果に応じて要素の頂点の座標を移動することで変形が行われます。

たとえば、100×100の要素をtranslate(10, 20)すると、次のようになります。
上から順に、左上、右上、左下、右下の頂点です。

2D Transforms 行列の計算 3 - 座標の算出

次に、ここまで出てきた関数を行列に変換してみます。

translate()からmatrix()

translate(tx, ty)matrix(1, 0, 0, 1, tx, ty)で表せます。
translateX(tx), translateY(ty)でも同様です。

translate()をmatrix()で表す
scale()からmatrix()

scale(sx, sy)matrix(sx, 0, 0, sy, 0, 0)で表せます。
scaleX(sx), scaleY(sy)でも同様です。

scale()をmatrix()で表す
rotate()からmatrix()

rotate(a)matrix(cos(a), sin(a), -sin(a), cos(a), 0, 0)で表せます。
aは角度で、度数法(degree)ではなく弧度法(radian)で指定します。
たとえば90degの場合、90 * π / 180 = 1.570796325 [rad]となります。

rotate()をmatrix()で表す
skewX(), skewY()からmatrix()

skewX(a)matrix(1, 0, tan(a), 1, 0, 0)で表せます。
arotate()のときと同じです。

skewX()をmatrix()で表す

また、skewY(a)matrix(1, tan(a), 0, 1, 0, 0)で表せます。

skewY()をmatrix()で表す
複数指定されている場合

仮に、rotate(30deg) scale(2) translate(10px, 10px)といった指定があったとしましょう。
この場合には、次のようになります。
(四捨五入しています)

rotate(30deg) scale(2) translate(10px, 10px) から matrix() へ

matrix()でまとめて指定するとmatrix(1.73205, 1, -1, 1.73205, 7.32051, 27.32051)となります。
計算方法はめんどくさいので行列の乗算の方法を調べてください。
計算するだけなら「Web 2.0 scientific calculator」などを使うと楽です。

実際に試して見ましょう。

transform: matrix() sample 1

.sample1 {
  transform: rotate(30deg) scale(2) translate(10px, 10px);
}

.sample2 {
  transform: matrix(1.73205, 1, -1, 1.73205, 7.32051, 27.32051);
}
デモ「transform: matrix() sample 1」の実行結果

これを重ねてみます。

transform: matrix() sample 2

デモ「transform: matrix() sample 2」の実行結果

transform-originプロパティ

transform-originでは、変形の原点を指定できます。
指定には、<percentage><length>、キーワードが使えます。
キーワードは、水平方向の場合left, center, rightで、垂直方向の場合はtop, center, bottomとなります。

値が1つしかない場合、2つ目の値はcenterとして扱われます。

値が2つの場合、その値がキーワードでなければ、1つ目の値は水平方向、2つ目の値は垂直方向のものとして扱われます。
<percentage>と<length>は、要素のボーダーボックスの左上隅からのオフセットを表します。

値が3つ、または4つの場合、<percentage>や<length>の前にキーワードが先行する必要があります。
たとえば、transform-origin: bottom 10px right 20pxは、垂直方向が要素の下端から10px、水平方向が右端から20pxの位置になります。値が3つしかない場合、未指定のオフセットは0であるとみなされます。

正の値は、ボーダーボックスの端から内側へのオフセットを、負の値はボーダーボックスの端から外側へのオフセットを表します。

このプロパティの初期値は50% 50%です。

次のサンプルは、赤い部分をtransform: rotate(15deg)し、transform-originの値を変更したものです。

transform-origin sample

.sample1 {
  transform-origin: 50% 50%;
}

.sample2 {
  transform-origin: 0 0;
}

.sample3 {
  transform-origin: 50% -20px;
}
デモ「transform-origin sample」の実行結果

2D Transformsについては以上です。
CSSアニメーションとの絡み、DOMでの扱いなどは仕様に載っていますので参照してください。

CSS 3D Transforms

Transformsを使うことで、要素を2次元または3次元へ移動・回転・スケール変更することができます。
Perspective transformは、奥行きのある表現を可能にします。

3次元空間ではZ軸が追加され、その正の値はウィンドウからユーザー側に向かって伸びており、負の値はユーザー側からウィンドウに向かってより奥に伸びています。
Z軸を真横から見た場合、ユーザー側が正の値、その逆が負の値となります。

ちょっと斜め上から見たらこんな感じです。文字が書かれているほうが負になります。
(文字も回転しちゃってますが気にしないでください)

CSS 3D Transformsで使われる3つの軸

それと、ここから先で使うサンプルですが、変形の確認に使う基本のHTMLとCSSはこんな感じになっています。

<div class="drawing">
  <h2>...</h2>
  <div class="box">
    <div class="base"></div>
    <div class="sample"></div>
  </div>
</div>
.box {
  position : relative;
  width    : 200px;
  transform: rotateX(-10deg) rotateY(-20deg);
}

.base,
.base + div {
  position: absolute;
  top     : 50px;
  left    : 50px;
  width   : 100px;
  height  : 100px;
}

また、正面からだと変形したことが分かりにくいものもありますので、斜め上から見ているような感じにしていますが、画面上のほうにあるボタンで切り替えられます。
軸も表示していますが、これも表示・非表示を切り替えられます。

transformプロパティ

2D Transformsと同様、どのような変形を行うかを指定します。指定はTransform関数で行います。
関数は空白区切りにより複数指定できます。
また、各関数は指定した順に適用されます。

初期値はnoneです。

translate3d(tx, ty, tz)

3Dでの要素の移動を行います。
指定には<length>形式、または<percentage>を使用しますが、Z方向では<length>のみが使用できます。

以下はサンプルです。
灰色の部分が変形適用前のもの、赤い部分が適用後のものとなります。

transform: translate3d() sample

.sample1 {
  transform: translate3d(20px, 0, 0);
}

.sample2 {
  transform: translate3d(0, 20px, 0);
}

.sample3 {
  transform: translate3d(0, 0, 50px);
}
デモ「transform: translate3d() sample」の実行結果(斜め上から)
デモ「transform: translate3d() sample」の実行結果(正面から)
translateX(tx), translateY(ty), translateZ(tz)

移動を個別に指定します。
translateX(tx)translate3d(tx, 0, 0)と同じです。
同様に、translateY(ty)translate3d(0, ty, 0)と同じ、translateZ(tz)translate3d(0, 0, tz)と同じになります。

パラメータには<length>または<percentage>ですが、translateZ()の場合<percentage>は使えません。

scale3d(sx, sy, sz)

3Dでの要素の拡大縮小を行います。指定には<number>形式を使用します。

transform: scale3d() sample 1

.sample1 {
  transform: scale3d(1.5, 1, 1);
}

.sample2 {
  transform: scale3d(1, 1.5, 1);
}

.sample3 {
  transform: scale3d(1, 1, 1.5);
}
transform:scale3d() sample 1-1
デモ「transform: scale3d() sample 1」の実行結果(斜め上から)
transform:scale3d() sample 1-2
デモ「transform: scale3d() sample 1」の実行結果(正面から)

X方向、Y方向への比率が変化しているのが分かります。ですが、Z軸については何も変化していないように見えます。Z方向へ拡大するからといって、要素に厚みが出るわけではありません。
では、Z方向へ拡大したものをY軸を中心に少し回転させてみましょう。
(回転については次の次のセクションからやります)

  1. 回転のみで、比率の変化はなし
  2. Z方向への拡大
  3. X方向とZ方向への拡大

と、なっています。

transform: scale3d() sample 2

.sample1 {
  transform: scale3d(1, 1, 1) rotateY(45deg);
}

.sample2 {
  transform: scale3d(1, 1, 1.5) rotateY(45deg));
}

.sample3 {
  transform: scale3d(1.5, 1, 1.5) rotateY(45deg);
}
デモ「transform: scale3d() sample 2」の実行結果(斜め上から)
デモ「transform: scale3d() sample 2」の実行結果(正面から)

回転されたものが、Z方向にも変化しているのが分かります。
scale3d(1, 1, sz)はZ方向に変化のある要素のZ方向の比率が変更される、ということです。

scaleX(sx), scaleY(sy), scaleZ(sz)

scale3d()の各パラメータを個別に指定する関数です。
scaleX(sx)scale3d(sx, 1, 1)と、scaleY(sy)scale3d(1, sy, 1)と、scaleZ(sz)scale3d(1, 1, sz)と同じになります。

rotate3d(x, y, z, a)

要素を3D回転させます。
x, y, zで方向ベクトルを指定し、aで回転の角度を指定します。回転は時計回りに行われます。
回転はtransform-originで指定したポイントを基準に行われまれ、角度の指定は <angle>形式で記述します。

またこの関数は、matrix3d(1+(1-cos(angle))*(x*x-1), -z*sin(angle)+(1-cos(angle))*x*y, y*sin(angle)+(1-cos(angle))*x*z, 0, z*sin(angle)+(1-cos(angle))*x*y, 1+(1-cos(angle))*(y*y-1), -x*sin(angle)+(1-cos(angle))*y*z, 0, -y*sin(angle)+(1-cos(angle))*x*z, x*sin(angle)+(1-cos(angle))*y*z, 1+(1-cos(angle))*(z*z-1), 0, 0, 0, 0, 1)と等しくなります。

rotate3d()の行列

transform: rotate3d() sample

.sample1 {
  transform: rotate3d(1, 0, 0, 45deg);
}

.sample2 {
  transform: rotate3d(0, 1, 0, 45deg);
}

.sample3 {
  transform: rotate3d(0, 0, 1, 45deg);
}
デモ「transform: rotate3d() sample」の実行結果(斜め上から)
デモ「transform: rotate3d() sample」の実行結果(正面から)
rotateX(a), rotateY(a), rotateZ(a)

各ベクトルごとに回転を指定します。パラメータは<angle>形式で記述します。
rotate3d()でまとめて指定しようとするとややこしいので、CSSを書く人はこちらをメインに使うことになるかと思います。

rotateX(a)では、aに指定された角度だけ、X軸を基準に要素を時計回りに回転させます。
rotateX(a)rotate3d(1, 0, 0, a)と同じ変形となります。

同様にrotateY(a)rotate3d(0, 1, 0, a)rotateZ(a)rotate3d(0, 0, 1, a)と同じ変形となります。
回転の基準となる軸もそれぞれY軸・Z軸となります。

回転の中心となるポイントはtransform-originで指定した座標になります。

transform: rotateX() rotateY() rotateZ() sample

.sample1 {
  transform: rotateX(45deg);
}

.sample2 {
  transform: rotateY(45deg);
}

.sample3 {
  transform: rotateZ(45deg);
}
デモ「transform: rotateX() rotateY() rotateZ() sample」の実行結果(斜め上から)
デモ「transform: rotateX() rotateY() rotateZ() sample」の実行結果(正面から)

skew(ax, ay)

要素を傾斜させます。
1つ目のパラメータでX方向の、2つ目のパラメータでY方向の傾斜を指定します。
2つ目のパラメータが指定されていない場合、0として扱われ、Y方向の変形は行われません。 パラメータは<angle>形式で記述します。

transform: skew() sample

.sample1 {
  transform: skew(30deg, 0);
}

.sample2 {
  transform: skew(0, 30deg);
}

.sample3 {
  transform: skew(30deg, 30deg);
}
デモ「transform: skew() sample」の実行結果(斜め上から)
デモ「transform: skew() sample」の実行結果(正面から)
skewX(a), skewY(a)

要素の傾斜をX方向、Y方向と個別に指定する関数です。
パラメータは<angle>形式で記述します。

skewX(a)skew(a, 0)skewY(a)skew(0, a)と同じ変形となります。

perspective(d)

透視投影による指定をします。これを指定することにより、変形に遠近効果をつけることができます。遠くのものはより小さく、近くのものはより大きく、といった感じです。

仕組みとしては、視点のポイントから要素の4つの各頂点を線で結び、その線のz=0の位置に描画されるものがブラウザで閲覧する変形後の要素となります。
たとえばZの位置が小さいほど、線同士が作る角度は狭くなるので、より小さく描画されます。
このperspective()z=0から視点ポイントまでの距離を指定します。

綺麗な図が描けなかったので真横からの図になりますが、次のような感じです。
CSS TransformsでいうところのtranslateZ()だけを指定した要素で、結ばれている線は実際には頂点ごとに、つまり4本あると思ってください。
「Z方向に対し正(負)の方向に移動した要素」自体の大きさは同じです。

perspective()関数の仕組み:Z軸に対して正の方向に移動した要素は、本来の大きさより拡大されて表示される
perspective()関数の仕組み:Z軸に対して負の方向に移動した要素は、本来の大きさより縮小されて表示される

関数の指定については<length>形式で記述するとなっていますが、px以外で指定しているものを見たことがないです……。
数値の大小による見え方の違いですが、数値が小さいほど遠近感が強く、大きいほど遠近感が弱くなります。

指定する値は0より大きい値である必要があります。

また、perspective()が指定されていない場合、要素の各頂点からz=0への線がz=0に対して垂直になるように投影されます。

transform: perspective() sample

.sample1 {
  transform: translateZ(50px);
}

.sample2 {
  transform: perspective(200px) translateZ(50px);
}

.sample3 {
  transform: perspective(500px) translateZ(50px);
}

.sample4 {
  transform: rotateY(45deg)
}

.sample5 {
  transform: perspective(200px) rotateY(45deg)
}

.sample6 {
  transform: perspective(500px) rotateY(45deg)
}
デモ「transform: perspective() sample」の実行結果(斜め上から)
デモ「transform: perspective() sample」の実行結果(正面から)

matrix3d(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4)

行列による指定を行います。この行列は座標変換を行うためのものです。
座標変換のやり方はは2Dのときと同じです。

この関数は16個のパラメータがあり、変形なしの状態だとmatrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)となります。

matrix3d()では 4×4の次のような行列を使います。

matrix3d()関数の行列

変形なしだとこうなります。

matrix3d()関数の行列の初期値

ちなみに、matrix(a, b, c, d, e, f)matrix3d()で表すと、matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, e, f, 0, 1)となります。

matrix()関数をmatrix3d()関数で表す
translate3d()からmatrix3d()

translate3d(tx, ty, tz)matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1)で表せます。
translateX(tx)translate3d(tx, 0, 0)なので、同じように当てはめるといいです。これはtranslateY(ty), translateZ(tz)でも同様です。

translate3d()をmatrix3d()で表す
scale3d()からmatrix3d()

scale3d(sx, sy, sz)matrix3d(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1)で表せます。
scaleX(sx)scale3d(sx, 1, 1)なので、同じように当てはめるといいです。これはscaleY(sy), scaleZ(sz)でも同様です。

scale3d()をmatrix3d()で表す
rotate3d()からmatrix3d()

rotate3d(x, y, z, a)は、その関数の項目で書いたようにmatrix3d(1+(1-cos(angle))*(x*x-1), -z*sin(angle)+(1-cos(angle))*x*y, y*sin(angle)+(1-cos(angle))*x*z, 0, z*sin(angle)+(1-cos(angle))*x*y, 1+(1-cos(angle))*(y*y-1), -x*sin(angle)+(1-cos(angle))*y*z, 0, -y*sin(angle)+(1-cos(angle))*x*z, x*sin(angle)+(1-cos(angle))*y*z, 1+(1-cos(angle))*(z*z-1), 0, 0, 0, 0, 1)となります。

rotate3d()をmatrix3d()で表す
rotateX(), rotateY(), rotateZ()からmatrix3d()

これらを計算するときには、角度aをradianで計算します。
2Dのmatrix()でも触れましたが、degreeaa * π / 180でradianに変換できます。

rotateX()matrix3d(1, 0, 0, 0, 0, cos(a), sin(a), 0, 0, -sin(a), cos(a), 0, 0, 0, 0, 1)となります。

rotateX()をmatrix3d()で表す

rotateY()matrix3d(cos(a), 0, sin(a), 0, 0, 1, 0, 0, -sin(a), 0, cos(a), 0, 0, 0, 0, 1)となります。

rotateY()をmatrix3d()で表す

rotateZ()matrix3d(cos(a), sin(), 0, 0, -sin(a), cos(a), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)となります。

rotateZ()をmatrix3d()で表す
skew()からmatrix3d()

skew(ax, ay)は、matrix3d(1, tan(ay), 0, 0, tan(ax), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)となります。
skewX(ax), skewY(ay)で個別に指定するときも同様です。

skew()をmatrix3d()で表す
perspective()からmatrix3d()

perspective(d)は、matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1/d, 0, 0, 0, 1)となります。

perspective()をmatrix3d()で表す

transform-originプロパティ

変形の原点を指定します。原点の座標は、要素の左上(0, 0, 0)からの距離となります。
初期値は50% 50% 0です。

指定は2D Transformsのときと同様ですが、Z方向場合にはパーセンテージとキーワードは使用できません。

次のサンプルは、赤い部分をtransform: rotateY(45deg)し、transform-originの値を変更したものです。

transform-origin sample

.sample1 {
  transform-origin: 50% 0 0;
}

.sample2 {
  transform-origin: 0 50% 0;
}

.sample3 {
  transform-origin: 0 0 50px;
}
デモ「transform-origin sample」の実行結果

transform-styleプロパティ

このプロパティは、指定した子要素が3D空間上で平面として描画されるか、立体として表示されるかを指定します。

指定できる値はflatまたはpreserve-3dで、初期値はflatです。
flatを指定すると、3D空間上で平面として描画され、preserve-3dを指定すると、立体的に表示されます。

次のサンプルは、各サンプルの親要素にtransform-styleプロパティでflat, preserve-3dを指定したものです。

transform-style sample

transform-style sample
デモ「transform-style sample」の実行結果

flatを指定した1つ目の例ではZ方向のものも平面になっているのが分かります。

perspectiveプロパティ

transformプロパティで使うperspective()関数と同じ意味です。
違いは、perspective()関数が要素そのものの深度を指定するのに対し、このperspectiveプロパティでは、指定した要素の子要素全体の深度を指定することにあります。

同様に、指定には<length>形式を用い、0より大きい値を指定しなければなりません。

perspective-originプロパティ

perspectiveプロパティの視点ポイントの位置を指定します。値は2つあり、X方向Y方向の座標を指定します。
値にはtransform-originと同様に、<percentage>と<length>、キーワードが使えます。
キーワードは、水平方向の場合left, center, rightで、垂直方向の場合はtop, center, bottomとなります。
0 0は要素の左上の座標です。

初期値は50% 50%です。

2つ目の値が省略され、1つ目の指定がキーワードでなければ、もう1つの値は50%として扱われます。
1つ目がX方向のキーワードの場合は、Y方向は50%となり、Y方向のキーワードの場合はX方向が50%として扱われます。

仮に、Z軸の負の方向に移動した要素の親要素にperspectiveを指定し、perspective-originのY方向の値を増やすと、次のような図になります。

Z軸の負の方向に移動した要素の親要素にperspectiveプロパティで深度を指定し、perspective-originプロパティでY軸方向の値を増やすと、要素は本来の大きさより小さく、そして本来の位置より下のいちに表示される。

以下のサンプルは、赤い色のボックスがtransform: translateZ(-100px)されたもので、全ての親要素に、perspective: 500が指定されており、さらに、親要素にperspective-originがそれぞれ50% 50%, 0 0, 100% 100%と指定されたものです。

perspective-origin sample

デモ「perspective-origin sample」の実行結果(斜め上から)

正面から見ると次のようになります。

デモ「perspective-origin sample」の実行結果(正面から)

backface-visibilityプロパティ

backface-visibilityプロパティは、要素の裏側を表示するかどうかを指定するプロパティです。

値にはhiddenvisibleが使用可能で、初期値はvisibleとなっています。
hiddenを指定すると、要素の表面が見えなくなった(たとえばrotateY(180deg)された)場合に、その要素は見えなくなります。
visibleの場合は、その要素を裏側から透かして見るような感じになります。

transform: rotateY(180deg)した要素のbackface-visibilityプロパティに、それぞれhidden, visibleを指定すると次のようになります。

backface-visibility sample

デモ「backface-visibility sample」の実行結果

transformプロパティでの注意点

このプロパティに指定する各関数ですが、指定するときは順番に注意したほうがいいかと思います。
変形の最終的な座標は行列によって計算されるわけですが、仮にtransform: 関数A 関数Bという変形を行うとすると、関数Aの行列 * 関数Bの行列となり、そこから座標が計算されるということです。

基本的に行列の乗算には交換法則が成り立ちませんので、行列ABがあるとき、A*BB*Aは別物となります。

とりあえずは、transformプロパティに指定する関数は書いた順に適用される、と覚えておけばいいと思います。

3Dでの回転について

上の関数の書き順にも関係してくるのですが、変形のない状態の場合、見た目の上ではX軸は左から右へ、Y軸は上から下へ、Z軸は奥から手前へ、と数値が増えていきます。
ですが3D Transformsにおいて回転変形を行うと、その要素の各軸は回転に伴って移動します。たとえば、rotateY(180deg)すると、その要素は裏返したような状態になりますが、このときX軸は右から左へ、Z軸は手前から奥へ、となります。
transform: rotate(180deg)したものを奥にあるように見せるには、rotate(180deg) translateZ(100px)とします。translateZ(100px) rotate(180deg)では手前側に移動した後に回転となるので、手前側にあるように見えます。
translateZ()を先に書くなら、translateZ(-100px) rotate(180deg)とします。

回転する立方体を作ってみる

練習を兼ねてアニメーションしながら回転する立方体を作ってみます。
まず、ベースとなる部分から。

実際にはプロパティごとに各ベンダープレフィクスが必要となりますが、記事内には書きません。
あまり関係ない部分も省略しています。

<div class="drawing">
  <div class="cube">
    <div class="panel front">1</div>
    <div class="panel back">2</div>
    <div class="panel left">3</div>
    <div class="panel right">4</div>
    <div class="panel top">5</div>
    <div class="panel bottom">6</div>
  </div>
</div>

.drawing {
  width : 200px;
}

.cube {
  position       : relative;
  width          : 200px;
  height         : 200px;
  transform-style: preserve-3d;
}

.panel {
  position   : absolute;
  border     : 1px solid #f00;
  width      : 200px;
  height     : 200px;
  box-sizing : border-box;
}

.front {
  background: rgba(255, 0, 0, 0.7);
}

.back {
  background: rgba(0, 0, 255, 0.7);
}

.left {
  background: rgba(255, 255, 0, 0.7);
}

.right {
  background: rgba(0, 255, 255, 0.7);
}

.top {
  background: rgba(0, 255, 0, 0.7);
}

.bottom {
  background: rgba(255, 0, 255, 0.7);
}

3D変形させるのはdiv.panelの6つなので、div.cubetransform-style: preserve-3dを指定します。
この時点では次のようになります。

CSS Cube 1

デモ「CSS Cube 1」の実行結果

次に、各パネルを回転させ、立方体になったとき、表が外側に来るようにします。

  • .frontは最初から外を向いているので変更なし
  • .backはY軸を中心に180度
  • .leftはY軸を中心に90度
  • .rightはY軸を中心に-90度
  • .topはX軸を中心に90度
  • .bottomはX軸を中心に-90度
.front {
  background: rgba(255, 0, 0, 0.7);
}

.back {
  background: rgba(0, 0, 255, 0.7);
  transform : rotateY(180deg);
}

.left {
  background: rgba(255, 255, 0, 0.7);
  transform : rotateY(90deg);
}

.right {
  background: rgba(0, 255, 255, 0.7);
  transform : rotateY(-90deg);
}

.top {
  background: rgba(0, 255, 0, 0.7);
  transform : rotateX(90deg);
}

.bottom {
  background: rgba(255, 0, 255, 0.7);
  transform : rotateX(-90deg);
}

CSS Cube 2

デモ「CSS Cube 2」の実行結果

正面からだとあまり変化がないように見えますが、ちょっと斜め上から見ると次のようになります。

デモ「CSS Cube 2」の実行結果(斜め上から見た場合)

それでは、立方体にするために各パネルを移動します。
前に説明したように、回転済みのものを移動するときには要素と共に回転した軸を考慮する必要があります。ここで、各パネルの表が外側を向くように回転させていますので、どのパネルも移動する方向はZ軸の正の方向になります。

.front {
  background: rgba(255, 0, 0, 0.7);
  transform : translateZ(100px);
}

.back {
  background: rgba(0, 0, 255, 0.7);
  transform : rotateY(180deg) translateZ(100px);
}

.left {
  background: rgba(255, 255, 0, 0.7);
  transform : rotateY(90deg) translateZ(100px);
}

.right {
  background: rgba(0, 255, 255, 0.7);
  transform : rotateY(-90deg) translateZ(100px);
}

.top {
  background: rgba(0, 255, 0, 0.7);
  transform : rotateX(90deg) translateZ(100px);
}

.bottom {
  background: rgba(255, 0, 255, 0.7);
  transform : rotateX(-90deg) translateZ(100px);
}

CSS Cube 3

デモ「CSS Cube 3」の実行結果
デモ「CSS Cube 3」の実行結果(斜め上から見た場合)

立方体ができました。
それでは、これをアニメーションで回転させます。アニメーションの指定については、以前書いた「CSSアニメーションの基礎」を参考にしてもらえればと思います。

.cube {
  position       : relative;
  width          : 200px;
  height         : 200px;
  transform-style: preserve-3d;
  animation      : rotation 4s linear infinite;
}

...

@keyframes rotation {

  from {
    transform: rotateY(0);
  }

  to {
    transform: rotateY(360deg);
  }

}

CSS Cube 4

デモ「CSS Cube 4」の実行結果

アニメーションができました。
次は遠近感を出すためにPerspectiveを指定します。

.cube {
  position       : relative;
  width          : 200px;
  height         : 200px;
  transform-style: preserve-3d;
  perspective    : 500px;
  animation      : rotation 4s linear infinite;
}

CSS Cube 5

デモ「CSS Cube 5」の実行結果
デモ「CSS Cube 5」の実行結果(アニメーション開始後)

何か変ですね。ではなぜこうなったのか考えてみます。

パネルが立方体を作るための変形(回転と移動)をする際、親要素で指定されたPerspectiveが加わります。それは全ての子要素から見て同じ位置であり、親要素のz=0から視点までの距離です。
この親要素を回転させると、Z軸も一緒に回転するわけで、それ伴ってPerspectiveでの視点も移動します。子要素はその回転する視点に合わせてを変形するので、このようなことになってしまったと考えられます。

ではどうすればいいのかというと、.cubeが回転する際にPerspectiveの視点が一緒に動いてしまわないようにすればいいのです。
つまり、.cubeの親、.drawingperspectiveプロパティによる指定をしてしまえば、その視点は動かないわけですから、遠近感のある立方体ができあがるはずです。

.drawing {
  width      : 200px;
  perspective: 500px;
}

.cube {
  position       : relative;
  width          : 200px;
  height         : 200px;
  transform-style: preserve-3d;
  animation      : rotation 4s linear infinite;
}

CSS Cube 6

デモ「CSS Cube 6」の実行結果
デモ「CSS Cube 6」の実行結果(アニメーション開始後)

期待通りの動きになりました。
また、transformperspective()関数で指定する場合には、回転の前に書かなければなりません。
ここではキーフレームの指定の中にあります。

/* CSS Cube 7(これは上手くいかない) */
@keyframes rotation {

  from {
    transform: rotateY(0) perspective(500px);
  }

  to {
    transform: rotateY(360deg) perspective(500px);
  }

}

/* CSS Cube 8(期待通り) */
@keyframes rotation {

  from {
    transform: perspective(500px) rotateY(0);
  }

  to {
    transform: perspective(500px) rotateY(360deg);
  }

}

CSS Cube 7

CSS Cube 8

これで無事に回転する立方体が完成しました。
もしパネルの裏面を表示したくなければ、.panelbackface-visibility: hiddenを指定してください。

CSS Cube

最後に

いつものことですが、この記事を鵜呑みにせず、自分でも仕様を確認したり、実際に試してブラウザの調査ツールなどで調べてみたりしてください。
仕様が変更されるかもしれませんし、そもそも記事に誤りがある可能性もあります。

最後まで読んでいただき、ありがとうございました。