注目の記事

HTML5 × CSS3 × jQueryを真面目に勉強 – #10.1 CSS3 Transforms(3D)

2012.12.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

そんな訳で前回の記事にてTransformの2Dについてまとめましたが、今回は続きということでCSS3 Transform の3Dについて学習してみるとします。

少し前まで角丸ですら満足に表現できなかったのに、いまや3Dですよ3D。どうですか奥さん?

はじめに - トランスフォーム(変形)の3D機能について

当シリーズにおいて、Transform 2Dと3Dとでは別モノのように記事を分割して紹介していますが、3Dは2Dのプロパティを拡張したモノといった感覚で扱うことが可能となっています。

こちらのブログによると、CSS 2D Transforms, CSS 3D Transforms, SVG Transformsの3仕様がCSS Transforms仕様として統合されることが決定していますとのことです。

要素に対してどのような変形をするかの指定は、Transformプロパティの値に予め用意されているtransform関数を指定する事で実現します。

transform3Dの関数一覧
translate3d(tx, ty, tz) 3次元移動
scale3d(sx, sy, sz) 3次元拡大縮小
rotate3d(x, y, z, d) 3次元回転
perspective(d) 遠近効果
matrix3d
(a1, b1, c1, d1,
a2, b2, c2, d2,
a3, b3, c3, d3,
a4, b4, c4, d4)
行列指定による3D変形

ではサンプルコードを交えながら順に見てくとします。

matrix3dですが、こちらもmatrix 2Dと同様、複雑な理屈で取っ付きにくいうえに、rotateや3dやperspectiveといった関数で充分に代替可能なため、今回の記事では割愛させて頂きます。
m(_ _)m

Transforms 3D - 事始め

端的に言うならば、Transform 3D は要素を3次元に移動ならびに回転、拡大縮小できるというものです。更に後述するperspectiveを使うことで、要素に奥行きのある表現を持たせることが可能となります。

3DではX軸Y軸に加えて、Z軸が追加されます。Z軸はウィンドウからユーザーに向かう方向が正の値で、ユーザーからウィンドウに向かう方向が負の値となります。

以下のような3次元座標をCSSを使って表現してみました。各座標の矢印の方向が正となります。

sampleimg_3dbasic

basic.html

<ul class="box">
  <li class="axis x"><span>X</span></li>
  <li class="axis y"><span>Y</span></li>
  <li class="axis z"><span>Z</span></li>
  <li class="square base"></li>
  <li class="square thumbnail"></li>
</ul>

3dbasic.css

.box {
  list-style: none;
  height: 300px;
  margin: 0 60px;
  padding: 0;
  position: relative;
  width: 300px;
  transform-style: preserve-3d;
  transform: rotateX(-10deg) rotateY(-20deg);
}

.axis {
  border-bottom: 1px solid;
  display: block;
  height: 0;
  line-height: 1;
  position: absolute;
  top: 50%;
  width: 300px;
}
.axis:after {
  border: 8px solid transparent;
  content: "";
  display: block;
  position: absolute;
  right: -8px;
  top: -7px;
}
.axis.x {
  border-bottom-color: #00f;
}
.axis.x:after {
  border-left-color: #00f;
}
.axis.y {
  border-bottom-color: #f00;
  transform: rotate(90deg);
}
.axis.y:after {
  border-left-color: #f00;
}
.axis.z {
  border-bottom-color: #0f0;
  transform: rotateY(-90deg) scaleZ(1);
}
.axis.z:after {
  border-left-color: #0f0;
}
.axis span {
  display: block;
  margin-top: 5px;
}

.square {
  height: 240px;
  margin: 0 30px;
  position: relative;
  top: 30px;
  width: 240px;
}
.square.base {
  background: rgba(58, 58, 58, 0.4);
}
.square.thumbnail {
  background: url(../img/myLogo.jpg) no-repeat;
  background-size: 240px 240px;
  opacity: .6;
  position: absolute;
}

実際に動くサンプルはこちらからご覧いただけます。また、変形の様子がわかりやすくなるように、座標の視点をスライダーで変化させることができます。

当サンプルはすべてChromeおよびSafariでのみご覧いただけます。FirefoxもCSS3 Transformsには対応していますが、スライダーが対応していないために、視点の変更がやや不便となっています。

そんな訳で、こちらのサンプルをベースに各関数を見ていくとします。

translate3d(tx, ty, tz)

要素を3次元移動させることができます。引数の指定方法はLength指定タイプPercentage指定タイプとがありますが、Z軸方向はLengthタイプのみの指定となります。

以下にサンプルを用意しました。スライダーを動かしてtranslate3d()の値を動的に変化させることができます。

sampleimg_translate3d

translate3d.css

.square.thumbnail {
  transform: translate3d(0, 0, 50px);
}

translateZ(tz)

Z軸上の移動距離を個別に指定することができます。引数にはLength指定のみ可能となります。

.square.thumbnail {
  transform: translateZ(50px);
}

scale3d(sx, sy, sz)

要素を3次元に拡大祝縮小させることができます。と聞くとZ軸方向に関しては要素に厚みが発生するのかと思いがちですが、実は全然違ったりするので注意が必要です。

とりあえずサンプルを見ながら確認していくとします。

sampleimg_scale3d1

.square.thumbnail {
  transform: scale3d(1, 1, 5);
}

scaleXとscaleYのスライダーを動かすと、サムネイルが縦横に伸縮するのがわかります。しかしscaleZのスライダーを動かしても、全く変化が見られません。どういうことなの… (´・ω・`)

これの答えはrotateY()を併用すると見えてきます。※rotate3dについては後述します。

CSSを以下のように書き換えます。

.square.thumbnail {
  transform: scale(1, 1, 5) rotateY(45deg);
}

sampleimg_scale3d2

Y軸方向に回転させると、scaleZで指定した値だけZ軸方向に伸縮しているのがわかります。つまりまとめるとこういうことです。

  1. Z軸方向に変化のある要素に対して、Z軸方向への比率が変化する
  2. 要素に厚みが出るわけではない

scaleZ(sz)

Z軸方向への拡大縮小値を個別に指定します。引数にはLength指定のみ可能となります。

.square.thumbnail {
  transform: scaleZ(5);
}

rotate3d(x, y, z, a)

要素を3次元に回転させることができます。最初の3つの引数x, y, zそれぞれの方向ベクトルを指定し、最後の引数で回転させたい角度を指定します。なんだかややこしいです。

rotate3d.css

.square.thumbnail {
  transform: rotate3d(1, 0, 1, 90deg);
}

数学の知識に長けている方ならば、上記の指定からでもどのような変形になるか想像できるかもしれませんが、そうではないという方も少ないかと思います。(※僕は全くわかりませんね…)

rotateZ(a)

ということで、x, y, z個別に回転する角度を指定するという方法が用意されています。こちらのほうが一見してどれだけ回転しているかが判断しやすいかと思います。

例えば以下のコードはどちらも同じ値となります。

// X軸方向に回転
.square.thumbnail {
  transform: rotateX(20deg);
}

.square.thumbnail {
  transform: rotate3d(1, 0, 0, 20deg);
}

以下にサンプルを用意しました。

sampleimg_rotate

.square.thumbnail {
  transform: rotateX(1deg) rotateY(1deg) rotateZ(45deg);
}

ちなみに回転は、transform-originで指定した座標を中心に実行されます。

perspective(d) - 遠近効果

要素に対しtransform効果を適用する際に、奥行き(遠近)を指定する際に使用します。別名透視投影とも言います。写真やアニメ等におけるパースと言えば何となくイメージがつかめるでしょうか?

詳しく知りたいという方は、こちらを参考ください。

で、使い方ですが、対象となる要素からユーザーの視点までの距離を指定します。渡す値が大きいほど視点と要素間の距離が遠くなり、transformの効果は小さく見えます。逆に渡す値が小さいほどtransformの効果は、同じ値でも大きく見えます。どうもまだピンと来ません…。

もう少し身近な例で考えてみましょう。手元にゴムボールがあるとします。そして空を見あげれば太陽が見えます。当然ですが2つの球体の大きさの差は歴然としています。手のひらに乗っかているゴムボールをほんの少し握ってみると形状が変化するのがわかりますね。モノは太陽と比べてとても小さいですが、視点との距離が近いので、小さな変形も認識することができます。

では太陽はどうでしょうか。絶えず爆発を繰り返してガスが大きく噴き出している太陽は、ゴムボールとは比較にならないほど大きな形状変化をしています。しかし視点との距離があまりに開きすぎているために、その変化を認識することは殆どできません。かなり極端な例ですが、perspectiveの概念はこのような感じになります。

サンプルを見て、実際にどう変化するのが確認してみるとします。

sampleimg_perspective

perspective.css

.square.thumbnail {
  transform: perspective(400px) translateZ(50px) rotateY(60deg);
}

translateZとrotateYを適当な値にして、perspectiveスライダーをグリグリ動かしてみてください。数値が大きいほど大して変形が見られず、小さくするとやたら激しく変化していく様子がわかるかと思います。

ちなみにperspective(d)に渡す引数の最小値は1です。0やマイナス値を指定することはできません。また、初期値をセット(※指定をリセット)したい場合は、noneを指定すればOKです。

補足 - perspectiveプロパティ

transformプロパティのperspective(d)関数とは別に、perspectiveプロパティというのもあります。基本的な変形の仕様は同じですが、perspective(d)関数が指定した要素自身に対して効果fが適用されるのに対し、perspectiveプロパティは指定した要素の子要素全体に対して効果を適用します。

指定する値はperspective(d)関数同様、Length指定タイプで、0より大きい値のみ有効となります。

transform-style -描画方法

指定した要素の子要素が3D空間上で平面的に表示されるか、立体的に表示されるかを指定します。指定する値はflatもしくはpreserve-3dの何れかで、初期値はflatとなっています。flatを指定すると、要素は3D空間上で平面状に描画され、preserve-3dを指定すると、要素は立体的にい描画されます。

.box {
  transform-style: flat;  // 平面状に描画
}
.box {
  transform-style: preserve-3d; // 立体状に描画
}

以下に用意したサンプルは、flatpreserve-3dとで要素がどのように変化するかを実際に確認できるものです。

sampleimg_transformstyle1

sampleimg_transformstyle2

flatを指定すると、Z軸方向への変形も平面的に描画されているのがわかります。

backface-visibility - 裏面の描画

指定した要素の裏面を表示するかどうかを指定します。指定する値はvisibleもしくはhiddenの何れかで、初期値はvisibleとなっています。hiddenを指定すると、例えば要素がrotateY(180deg)と指定されて裏面しか見えなくなった場合に、その要素は完全に見えなくなります。visibleとはすなわち、要素を裏側から透かして見ているといったところでしょうか。

以下に用意したサンプルは、backface-visibilityにhiddenが指定されており、裏面が見えなくなっています。

sampleimg_backface1

sampleimg_backface2

transform プロパティの指定順序について

これまで紹介してきたように、transformプロパティには複数の関数を指定して要素を変形させますが、指定した関数は並列に適用されるのではなく、指定した順番に適用されるという仕様になっています。

たとえば以下のように指定したとします。

.square.thumbnail {
  transform: rotateY(37deg) perspective(1000px) translateZ(150px);
}

sampleimg_paramorder1

これは要素に対し、rotateY()関数を適用して、その結果に対してperspective()関数を適用し、そしてtranslateZ()関数を適用するという指定になります。したがって、

.square.thumbnail {
  transform: perspective(1000px) translateZ(150px) rotateY(37deg);
}

sampleimg_paramorder2

と指定したのとでは、各関数に同じ値を指定しても得られる結果はまるで違うものになります。

3D Cubeを作ってみる

よくあるヤツですが、スタンダードなサンプルということで3D Cubeを作ってみたいと思います。完成イメージはこちら。

sampleimg_cube3d

まずはHTMLから書いていきます。

cube3d.html

<div class="container clearfix">
  <div id="stage" class="content">
    <ul class="shape-3d cube backfaces">
      <li class="panel one">1</li>
      <li class="panel two">2</li>
      <li class="panel three">3</li>
      <li class="panel four">4</li>
      <li class="panel five">5</li>
      <li class="panel six">6</li>
      <li class="panel seven wakamsha">7</li>
      <li class="panel eight wakamsha">8</li>
      <li class="panel nine wakamsha">9</li>
      <li class="panel ten wakamsha">10</li>
      <li class="panel eleven wakamsha">11</li>
      <li class="panel twelve wakamsha">12</li>
    </ul>
  </div>
</div>

続いてCSSです。とりあえずベースの部分だけ書いていきます。

cube3d.css

.container {
  width: 100%;
  perspective: 800;
  -o-perspective-origin: 50% 225px;
  perspective-origin: 50% 225px;
}

.content {
  width: 100%;
  transform-style: preserve-3d;
}

.shape-3d {
  height: 200px;
  margin: 0 auto;
  position: relative;
  top: 160px;
  width: 200px;
  transform-style: preserve-3d;
}

.panel {
  background: rgba(48, 141, 191, 0.4);
  border: 1px solid white;
  border-radius: 12px;
  box-sizing: border-box;
  color: #fdfdfd;
  font-family: Times, serif;
  font-size: 11em;
  height: 200px;
  position: absolute;
  opacity: .5;
  text-align: center;
  width: 200px;
}

ここまでで一度ブラウザで確認してみましょう。

とりあえず各パネルの基本的なデザインができました。続いてtransform 3Dを駆使してCube状にしていきます。

/* ---------- cube styles ------------- */
.cube .one {
  transform: scale3d(1.2, 1.2, 1.2) rotateX(90deg) translateZ(100px);
}
.cube .two {
  transform: scale3d(1.2, 1.2, 1.2) translateZ(100px);
}
.cube .three {
  transform: scale3d(1.2, 1.2, 1.2) rotateY(90deg) translateZ(100px);
}
.cube .four {
  transform: scale3d(1.2, 1.2, 1.2) rotateY(180deg) translateZ(100px);
}
.cube .five {
  transform: scale3d(1.2, 1.2, 1.2) rotateY(-90deg) translateZ(100px);
}
.cube .six {
  transform: scale3d(1.2, 1.2, 1.2) rotateX(-90deg) translateZ(100px) rotate(180deg);
}
.cube .seven {
  transform: scale3d(0.8, 0.8, 0.8) rotateX(90deg) translateZ(100px) rotate(180deg);
}
.cube .eight {
  transform: scale3d(0.8, 0.8, 0.8) translateZ(100px);
}
.cube .nine {
  transform: scale3d(0.8, 0.8, 0.8) rotateY(90deg) translateZ(100px);
}
.cube .ten {
  transform: scale3d(0.8, 0.8, 0.8) rotateY(180deg) translateZ(100px);
}
.cube .eleven {
  transform: scale3d(0.8, 0.8, 0.8) rotateY(-90deg) translateZ(100px);
}
.cube .twelve {
  transform: scale3d(0.8, 0.8, 0.8) rotateX(-90deg) translateZ(100px);
}
.cube .wakamsha {
  background: url(../img/myLogo.jpg) no-repeat;
  background-size: 200px 200px;
  color: #666;
  opacity: .8;
}

これで3DのCubeが出来ました。最後にCSS3アニメーションをつかってグルグル回してみたいと思います。

.shape-3d {
  animation: spin 12s linear 0s infinite normal none;
}

@-webkit-keyframes spin {
  0%   { -webkit-transform: rotateY(0); }
  100% { -webkit-transform: rotateY(-360deg); }
}

@-moz-keyframes spin {
  0%   { -moz-transform: rotateY(0); }
  100% { -moz-transform: rotateY(-360deg); }
}

@-ms-keyframes spin {
  0%   { -ms-transform: rotateY(0); }
  100% { -ms-transform: rotateY(-360deg); }
}

@keyframes spin {
  0%   { transform: rotateY(0); }
  100% { transform: rotateY(-360deg); }
}

終わりに

Webコンテンツで3D表現をどのように取り入れるかというのは、今まで以上に柔軟かつ突飛なアイディアが要求されます。未だどう扱って良いのか持て余してしまうなぁという方も少なくないでしょう(※僕もそう思っています…)。とはいえ3D表現をここまでお手軽に実現できるというのに、キワモノと見限って食わず嫌いになるには、あまりにももったいない技術ではないでしょうか。

例えば技術寄りの方ならば、身近にいるビジュアルデザイナーに対し、「こういう技術があるんだけど、どうよ?」と見せつけてやることで、新しい表現のキッカケになるのではないでしょうか。

今まで以上にデザイナーとプログラマの密な連携が求められている時代に、僕達はいるのです。