CSSだけで球体！？錯視トリックで世界を騙せ

この記事は [CSS Advent Calendar 2019](https://qiita.com/advent-calendar/2019/css) 14日目の記事です。

# 伝えたいこと

本来、CSSだけでは球体の表現は難しく、グラデーションでそれっぽくするほかありませんでした。
この課題、アニメーションと周辺環境による錯視のトリックで解決します。

# 深く考えないでね

ここで紹介するテクニックは実務に応用できません。
CSSアニメーションのジョーク記事としてお楽しみください。
タイトルもジョークです...笑

# 先に完成図

まずはこのサンプルをご覧ください。
お急ぎの方は、このサンプルを見ておしまいです！
Qiita上では少し動作が重いので、サンプル右上のCodePenロゴをクリックしてご覧ください。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="result" data-user="YusukeNakaya" data-slug-hash="OJJpQyL" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Only CSS: Particles Following Camera 1">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/OJJpQyL">
  Only CSS: Particles Following Camera 1</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

小さな**球体**が大量に、グルグルと旋回しているように見えませんか？
**影も落ちてるし。**

このトリックを解説します。
読み進めると、**何かわかった感じになります。**

# 下準備

まずは簡単に円を描き、グラデーションでそれっぽいテクスチャを与えます。
とても妥協すれば球体に見えなくもないですが、より確実に「球体」と認識できるよう、チューニングします。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="abzZLVV" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step1">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/abzZLVV">
  Camera following: Step1</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

# 錯視のテクニック

それっぽいテクスチャだけを与えられ、ちょっと嘘っぽい、ただの円。
「3D空間にある球体」として誤認させるため、3DTransformsでアニメーションを与えます。

ただし、このコントロールには少し癖があります。
CSSの場合、**ひとつの要素にはひとつのtransformしか与えることができない。**
この制約をうまく回避して実装する必要があります。

# 2つのアプローチ

ポイントは、円のテクスチャを常に正面を向けながら移動すること。
これをコントロールするアニメーションのアプローチとして、2つ考えられます。

1つは、そもそもrotate系を使わない手法。
比較的簡単ですが、円運動など、複雑な立体機動には向きません。

もう1つは、親要素にrotateを指定し、子要素には逆方向のrotateを指定して打ち消す手法。
こちらのアプローチはコントロールが難しいですが、複雑な立体機動が可能です。

今回は、後者のアプローチで実装します。
**迷ったときは難しい道を選べって親父が言ってた。**

# Y軸回転してみよう

まずは回転専用の要素を追加し、rotateY（縦を軸に回転）で回転させます。
円はコインのように、その場でクルクルと回っています。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="wvBWpqX" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step2">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/wvBWpqX">
  Camera following: Step2</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

当たり前ですが、この時点ではまったく球体に見えません。

# Z軸移動してみよう

次に、移動専用の要素を追加し、translateZ（奥行き方向に移動）させます。
円は、大きな旋回の軌道で、手前から奥へと立体的にグルグル廻っています。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="VwYjMBV" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step3">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/VwYjMBV">
  Camera following: Step3</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

奥行きは感じますが、まだ球体には見えません。

# 周回軌道を打ち消すY軸回転を追加しよう

**本記事のメインです。**
回転打ち消し専用の要素を追加し、旋回とは逆方向にrotateYさせます。
旋回はそのままに、円が常に正面を向くようになります。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="xxbOXyj" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step4">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/xxbOXyj">
  Camera following: Step4</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

**おや？
ひょっとして球体に見えなくもない？**

# X軸回転で傾けてみよう

ここからは、円をとりまく周辺環境も整えます。
傾き専用の要素を追加し、rotateX（横を軸に回転）で傾きをつけます。
やや斜め上から見下ろしているような視点になります。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="yLyJzmX" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step5">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/yLyJzmX">
  Camera following: Step5</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

残念、球体には見えなくなりました。

# 傾きを打ち消すX軸回転を追加しよう

傾き打ち消し専用の要素を追加し、傾きとは逆方向にrotateXします。
ふたたび、円が正面を向くようになります。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="VwYjyBz" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step6">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/VwYjyBz">
  Camera following: Step6</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

**傾きでより強い立体感を演出できました。**

# グラフィックを調整しよう

ボールに影を落としたり、背景にグラデーションを敷いたりして、全体のグラフィックを整えます。
物理や光源処理、素材感、床の反射率など、リアリティに気を配ります。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="zYxBpmq" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step7">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/zYxBpmq">
  Camera following: Step7</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

これを見た10人中、7〜8人は球体に見えるのでは。

# 円を増やしてみよう

**トドメだ。**
円を3つに増やして、それぞれ旋回のアニメーションにディレイを与えます。
円同士が近すぎるとバレやすいので、ギリギリのところで追いかけているように調整します。

<!-- codepen -->
<p class="codepen" data-height="500" data-theme-id="30348" data-default-tab="css,result" data-user="YusukeNakaya" data-slug-hash="GRgqQqw" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera following: Step8">
  <span>See the Pen <a href="https://codepen.io/YusukeNakaya/pen/GRgqQqw">
  Camera following: Step8</a> by Yusuke Nakaya (<a href="https://codepen.io/YusukeNakaya">@YusukeNakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

**これを見た10人中、9.5人くらいは球体に見えるのでは。**

# まとめ

擬似的な球体表現を紹介しました。
大切なポイントは2つ。

- 円を常に正面に向ける（回転を打ち消す）
- ひとつの動きに、ひとつの要素

うまく活用できれば、本来は難しいはずの**球体表現という圧倒的アドバンテージ**を得られるでしょう。
なるほどこれは、マジックですね。

<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# フォローミー！

- [twitter](https://twitter.com/s14garnet)よく使います！
- [CodePen](https://codepen.io/YusukeNakaya)めっちゃ使います！
- [dribbble](https://dribbble.com/yusukeMX5)これから頑張りまーす！

楽しんでいただけたら、フォローしてくれると嬉しいです！🤩
また、[Meguro.css](https://megurocss.connpass.com/)というCSS勉強会も運営しているので、よかったら遊びに来てね！