立方体を描画してみよう！CSSだけで3D表現【フロントエンド】

#はじめに
今回は，WebGL等を使わないで，単純な3DオブジェクトをCSSだけで描画する方法についてご紹介しようと思います．

#CSSでの3D表現の特徴
確かに複雑なオブジェクトを作るのであれば，WebGLを使うのが好ましいでしょう．
しかし，CSSでやる良さもあるので，主なメリットとデメリットを以下に記述します．

- CSSで3Dをやるメリット
    - 直感的に作れる．
    - イベントなどを設定しやすい．
    - 見た目をCSSで簡単に変更できる．

- CSSで3Dをやるデメリット
    - 複雑な立体は作るのが大変．
    - 曲線のある立体は作るのが難しい．
    - 複雑なものを作ると時間が溶ける．

####立方体のサンプル
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="AO2324" data-slug-hash="jOEWgEJ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="3D Box">
  <span>See the Pen <a href="https://codepen.io/AO2324/pen/jOEWgEJ">
  3D Box</a> by AO2324 (<a href="https://codepen.io/AO2324">@AO2324</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
####円柱のサンプル
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="AO2324" data-slug-hash="rNaeNyP" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="3D Cylinder">
  <span>See the Pen <a href="https://codepen.io/AO2324/pen/rNaeNyP">
  3D Cylinder</a> by AO2324 (<a href="https://codepen.io/AO2324">@AO2324</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
####クリックイベントをつけた立体のサンプル
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="AO2324" data-slug-hash="eYmZYoq" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="3D Box has event">
  <span>See the Pen <a href="https://codepen.io/AO2324/pen/eYmZYoq">
  3D Box has event</a> by AO2324 (<a href="https://codepen.io/AO2324">@AO2324</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
#使用するCSSプロパティ

- `transform-style`
- `transform`
- `perspective`

###transform-style (描画形式)
子要素の変形を立体的に描画するか平面的に描画するかを指定します．

```css:平面的(初期設定)
transform-style:flat;
```

```css:立体的
transform-style:preserve-3d;
```
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="AO2324" data-slug-hash="qBEZbqr" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="3D Box (Flat / 3d )">
  <span>See the Pen <a href="https://codepen.io/AO2324/pen/qBEZbqr">
  3D Box (Flat / 3d )</a> by AO2324 (<a href="https://codepen.io/AO2324">@AO2324</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

###transform (要素の移動や回転)
`transform`プロパティを使用することで，要素を平行移動や回転，拡大など，変形させることができます．

指定方法は，`transform`の後にどう変形させるかを続けて記述します．
例 `transform: 変形１ 変形２;`
変形の書き方
`変形の種類+基本とする軸(値)`

- 変形の種類
    - `translate` : 平行移動
    - `rotate` : 回転
    - `scale` : 大きさ
    - `skew` : 傾斜
- 基本とする軸
    - `X` : x軸
    - `Y` : y軸
    - `Z` : z軸
    - なし : ()の中にx軸とy軸をカンマ(,)で区切って表記
    - `3d` : ()の中にx軸とy軸とz軸をカンマ(,)で区切って表記

```css:サンプル
transform: translateX(50px) translateY(100px) rotateZ(90deg);
/* x軸方向に50px，y軸方向に100px平行移動したのち，z軸を中心に90度回転 */
transform: translate(50vw, 50vh);
/* x軸方向に50vw，y軸方向に50vh平行移動 */
```


###perspective (遠近感)
3Dのオブジェクトに遠近感をもたせます．
数値が小さいほど魚眼レンズのようになり，大きければ大きいほど全ての幅が均等になります．
<img width="200" alt="スクリーンショット 2019-12-10 23.35.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530349/5dd908ee-53f7-df83-9b32-2fa8e57e2016.png">
`perspective:200px;`
<img width="200" alt="スクリーンショット 2019-12-10 23.20.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530349/5a6ee89b-ef68-5d50-1da9-ba22ee960520.png">
`perspective:10000px;`
数値が小さい(200px)と上のような見え方に，数値が大きい(10000px)と下のような見え方になります．
個人的には 800px ~ 1000px くらいがちょうどいい気がします．
<img width="200" alt="スクリーンショット 2019-12-10 23.20.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530349/0c7c118b-becd-1ee1-1a16-0adad955e343.png">
`perspective:1000px;`

#実際に立方体を作ってみよう！

###Step1
まず，HTMLで立体の面となる要素を定義してあげます．

```html:html
<div id="scene">
  <div class="boxBase">
    <div class="top"></div>
    <div class="bottom"></div>
    <div class="front"></div>
    <div class="back"></div>
    <div class="left"></div>
    <div class="right"></div>
  </div>
</div>
```
ここで，`scene`は3Dオブジェクトを表示する場所，`boxBase`は立方体，その中の要素は立方体のそれぞれの面を表しています．
これをCSSの`transform`を使って変形することで，ペーパークラフトのように立体を作ることができます．

###Step2
何も指定しないと平面的に描画してしまうので，`transform-style:preserve-3d;`をboxBaseに設定して，子要素を立体的に表示するようにしてあげましょう．
ここで，一辺の長さも決めてしまいましょう．

```css:css(1/4)
.boxBase {
  transform-style:preserve-3d;
  width:200px;
  height:200px;
}
```

###Step3
立方体の面となる要素の設定を行い，ついでに色や縁にもスタイルを適応させましょう．

```css:css(2/4)
.boxBase > div {
  position:absolute;
  width:100%;
  height:100%;
  background-color:rgba(100, 100, 100, 0.7); /* 面の色 */
  border:0.5px solid black; /* 縁 */
}
```

###Step4
それぞれの面を変形して，立方体を組み立てていきましょう．

```css:css(3/4)
.boxBase > .top {
  transform:translateY(100px) rotateX(-90deg);
}
.boxBase > .bottom {
  transform:translateY(-100px) rotateX(90deg);
}
.boxBase > .front {
  transform:translateZ(100px);
}
.boxBase > .back {
  transform:translateZ(-100px) rotateX(180deg);
}
.boxBase > .left {
  transform:translateX(-100px) rotateY(-90deg);
}
.boxBase > .right {
  transform:translateX(100px) rotateY(90deg);
}
```

###Step5
`perspective`で遠近感を調整しましょう．

```css:css(4/4)
#scene {
  perspective: 1000px;
}
```

###立方体の完成
```html:立方体（htmlファイル）
<div id="scene">
  <div class="boxBase">
    <div class="top"></div>
    <div class="bottom"></div>
    <div class="front"></div>
    <div class="back"></div>
    <div class="left"></div>
    <div class="right"></div>
  </div>
</div>

<style>
#scene {
  perspective: 1000px;
}
.boxBase {
  transform-style:preserve-3d;
  width:200px;
  height:200px;
}
.boxBase > div {
  position:absolute;
  background-color:rgba(100, 100, 100, 0.7);
  border:0.5px solid black;
  width:100%;
  height:100%;
}
.boxBase > .top {
  transform:translateY(100px) rotateX(-90deg);
}
.boxBase > .bottom {
  transform:translateY(-100px) rotateX(90deg);
}
.boxBase > .front {
  transform:translateZ(100px);
}
.boxBase > .back {
  transform:translateZ(-100px) rotateX(180deg);
}
.boxBase > .left {
  transform:translateX(-100px) rotateY(-90deg);
}
.boxBase > .right {
  transform:translateX(100px) rotateY(90deg);
}
</style>
```

アニメーションをつけてあげると立体になっている事がよくわかるので，ぜひ試してみてください．

```css:立方体のアニメーション
#scene {
  perspective: 1000px;
  width:200px;
  height:200px;
  position:relative;
  top:50vh;
  left:50vw;
  transform:translateX(-50%) translateY(-50%);
}
.boxBase {
  transform-style:preserve-3d;
  width:200px;
  height:200px;
  animation: turnAround 30s linear 0s infinite normal none running;
}
@keyframes turnAround {
  0%{
    transform:rotateX(0deg) rotateY(0deg);
  }
  100%{
    transform:rotateX(360deg) rotateY(360deg);
  }
}
```
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="AO2324" data-slug-hash="jOEWgEJ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="3D Box">
  <span>See the Pen <a href="https://codepen.io/AO2324/pen/jOEWgEJ">
  3D Box</a> by AO2324 (<a href="https://codepen.io/AO2324">@AO2324</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

#最後に
今回はCSSでの3D表現のざっくりとした説明と，簡単な立体の作り方を書かせていただきました．
これだけだと，実際のWebページやアプリ開発でどのように活かせるかわからないかもしれません．
ですが，発想次第ではいろいろな事ができるので，ぜひCSSでもっともっと遊んでみてください．
<img width="250" alt="スクリーンショット 2019-12-11 1.34.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530349/f6b8d8a8-bd8e-2958-1edd-a22f28df1988.png">
↑人型の立体物　↓Webサイトのナビゲーション
<img width="250" alt="スクリーンショット 2019-12-11 1.32.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530349/a21195c9-811a-b98b-0a5b-866acb33e68d.png">

#参考
[CSS3D！まるでWebGLのような3Dを超気軽に実装するCSS技 (中矢 雄介／Creator)](https://dev.oro.com/posts/2017/09/design/css_3d/)