CSS の transform は個別に分離するのが常に優れているわけではない

## この記事の概要

CSS で要素を変形をさせるとき、昔ながらの書き方は次のようなものです。

```css
.element {
  transform: translate(30px, 20px) rotate(20deg);
}
```

最近はそれぞれの指定を分離して、次のようにも書けます。

```css
.element {
  translate: 30px 20px;
  rotate: 20deg;
}
```

個別に書いた方がアニメーションの管理などはしやすいですし、diff も見やすいです。

ただ、常に個別に書くのが優れているわけでもないので、その点について記事にしました。

## 例示するもの

こういった 3D 表現を実施する際、移動や回転を多く使います。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/82ba6d5b-e7a5-67b2-e482-78be672560f6.png)

この立方体を例にして説明します。

## translate のローカル座標とグローバル座標

例に出した立方体はこのようなコードでできています。

```html
<div class="cube">
  <div class="face front">front</div>
  <div class="face back">back</div>
  <div class="face right">right</div>
  <div class="face left">left</div>
  <div class="face top">top</div>
  <div class="face bottom">bottom</div>
</div>
```

```css
.cube {
  position: relative;
  transform-style: preserve-3d;
  transform: rotateX(-30deg) rotateY(30deg);
}

.face {
  position: absolute;
}

.front {
  transform: translateZ(25dvmin);
}

.back {
  transform: rotateY(180deg) translateZ(25dvmin);
}

.right {
  transform: rotateY(90deg) translateZ(25dvmin);
}

.left {
  transform: rotateY(-90deg) translateZ(25dvmin);
}

.top {
  transform: rotateX(90deg) translateZ(25dvmin);
}

.bottom {
  transform: rotateX(-90deg) translateZ(25dvmin);
}
```

こちらを、書いてある数値のまま各プロパティに分離してみます。
`translate`の場合は 1 つの値だけを指定すると X 軸の移動になってしまうので、3 つの値を指定しています。

```diff_css
  .front {
-   transform: translateZ(25dvmin);
+   translate: 0 0 25dvmin;
  }

  .back {
-   transform: rotateY(180deg) translateZ(25dvmin);
+   rotate: y 180deg;
+   translate: 0 0 25dvmin;
  }

  .right {
-   transform: rotateY(90deg) translateZ(25dvmin);
+   rotate: y 90deg;
+   translate: 0 0 25dvmin;
  }

  .left {
-   transform: rotateY(-90deg) translateZ(25dvmin);
+   rotate: y -90deg;
+   translate: 0 0 25dvmin;
  }

  .top {
-   transform: rotateX(90deg) translateZ(25dvmin);
+   rotate: x 90deg;
+   translate: 0 0 25dvmin;
  }

  .bottom {
-   transform: rotateX(-90deg) translateZ(25dvmin);
+   rotate: x -90deg;
+   translate: 0 0 25dvmin;
  }
```

このコードの場合、次のような見た目になります。

![localhost_3000_ (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/86d2e6c6-71a1-344a-8872-48dda790f557.png)

回転の向きは正しそうですが、すべての面が同じ向き（画面向かって右下）に移動してしまっています。

実は、`translate`を使ってもともとの見た目を再現するためには、このように書く必要があります。

```diff_css
  .front {
    translate: 0 0 25dvmin;
  }

  .back {
    rotate: y 180deg;
-   translate: 0 0 25dvmin;
+   translate: 0 0 -25dvmin;
  }

  .right {
    rotate: y 90deg;
-   translate: 0 0 25dvmin;
+   translate: 25dvmin 0 0;
  }

  .left {
    rotate: y -90deg;
-   translate: 0 0 25dvmin;
+   translate: -25dvmin 0 0;
  }

  .top {
    rotate: x 90deg;
-   translate: 0 0 25dvmin;
+   translate: 0 -25dvmin 0;
  }

  .bottom {
    rotate: x -90deg;
-   translate: 0 0 25dvmin;
+   translate: 0 25dvmin 0;
  }
```

`transform`を使う場合、記載の順番に気を付ける必要はありますがローカル座標で操作できます。
一方で`translate`の場合はグローバル座標での操作となります。

どちらかが優れているというよりは、操作したい内容にあわせて変えた方が良いものだと言えそうです。
例えば幾何形態を考えるときはローカル座標の方が扱いやすいと思っています。

## 複数軸での rotate が複雑

例に出した立方体は X 軸に -30 度、Y 軸に 30 度傾いています。

`transform`を用いた書き方はこちらです。

```css
.element {
  transform: rotateX(-30deg) rotateY(30deg);
}
```

直感に沿っていますね。

次に、`rotate`を使います。
`rotate`の書式は`rotate: x y z angle;`なので、次のように書けば同じ結果が得られそうに思いませんか？

```css
.cube {
  rotate: -1 1 0 30deg;
}
```

ところが、実際にはこのような見た目になります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/dc7184c0-060c-2f14-cbde-320af4e2bd89.png)

もともとの見た目に近づけようと思うと、次のようになります。

```css
.cube {
  rotate: -1 1 -0.3 45deg;
}
```

XYZ のベクトルを合成して、その軸にあわせて回転するという仕組み上、各軸の回転量を個別に扱うのが難しいです。

今回の例でいえば X 軸と Y 軸の回転させたい角度が同じなのでまだ考えられますが、それぞれの値が違う場合はかなり難しいです。

これも、どちらが優れているというわけではなく、場合によって使い分けた方が良さそうです。
例えばアイソメトリック風のビューにしたい場合`transform`での指定は簡単ですが`rotate`を使うと複雑です。[^complicated]

[^complicated]: 複雑というか、私はどの値ならアイソメトリックっぽい見た目になるか、計算できませんでした……。

## 最後に

CSS において、古いプロパティと新しいプロパティがある場合、大抵は新しいプロパティが上位互換です。

ただ`transform`においては一概には言えず、実施したいものにあわせて使い分けるのが良さそうだと思ったので記事にしてみました。

細かなアニメーション制御をしたい場合は個別に、静的に綺麗な配置を実現したい場合は一括、みたいな区分はあるのかもしれません。