Analog Studio

CSSのbox-shadowを滑らかにtransitionさせる方法【カクカクさせない】

概要

カードデザインというデザインを検討したことがある方は多いのではないでしょうか?
レシポンシブなデザインに対応した EC サイトやブログなどの一覧表示でよく使われていますね。
一つの商品や記事などをカード状の要素で構成してそれを並べることでどんな幅のディスプレイにも馴染みが良く、それぞれの要素の幅は固定なのでスタイリングやしやすいという特徴があります。
この Tips も一覧表示にはカードデザインを採用しています。

そんなカードデザインなどでマウスカーソルを合わせた時、ちょっとした演出として影を付けたいことがあります。
:hover 疑似クラスで box-shadow を設定するだけなのですが、transition でアニメーションを付けるとカクカクすることがあります。
今回はこれを滑らかにスムーズなアニメーションにするちょっとしたひと手間を紹介します。

実際どうなるの?動作を確認しよう!

実際にどのような動作になっているのかを確認しましょう。
通常はここまでゆっくり変化して大きく濃い影は付けませんが、分かりやすいように設定しています。

<HTML> <div class="sample"></div> <CSS> .sample::before{ content : 'Hover me!'; display : block; margin : 20px; width : 100px; height : 100px; text-align : center; line-height : 100px; background : #FFD77A; border-radius : 10px; transition : 2.5s; } .sample:hover::before{ box-shadow : 0 0 20px #000; }

如何でしょうか!?こうやって見ると気になりますよね。カクカクカクっと影が付いていくことが分かると思います。
※スマホや 4K ディスプレイは画素ピッチが狭く (後述) 滑らかにアニメーションします。
※中にはある程度は滑らかに動くブラウザもあるかもしれませんが、未確認です。

これをちょっと一工夫するだけで以下のように滑らかな動作にすることができます。

滑らかにする方法

では、滑らかにアニメーションする方法を以下に示します。

<HTML> <div class="sample"></div> <CSS> .sample::before{ content : 'Hover me!'; display : block; margin : 20px; width : 100px; height : 100px; text-align : center; line-height : 100px; background : #FFD77A; border-radius : 10px; transition : 2.5s; /* 透明で同じ大きさのbox-shadowを追加する! */ box-shadow : 0 0 20px #0000; /* 色はrgba(0,0,0,0)やtransparentなどでもOK */ } .sample:hover::before{ box-shadow : 0 0 20px #000; }

とても簡単なことで、hover 前の状態にも box-shadow を設定するだけです。
但し、見えてしまうと意味がないので色を透明にしておきます。

たった、これだけで滑らかなアニメーションが実現できてしまいます。ぜひお試し下さい。

なぜ、滑らかになるのか?

では、なぜ滑らかになるのでしょうか?
それはアニメーションで変化する箇所が影の濃度だけになり、ほぼ無段階で値が徐変できるからです。

hover 前の状態に何も指定しないと box-shadow は初期値の none を取ります。
当然、影のぼかし具合も 0 (0px) となります。

この初期状態から影を付けるとぼかす範囲が段々と広くなっていきます。
ぼかす方法に依るのですが、仕様書ではガウスぼかしに準ずる (±5% 以下) ようなことが書かれていますので、多くのブラウザではガウスぼかし (ガウシアンフィルタとも) を採用していると思います。

A non-zero blur radius indicates that the resulting shadow should be blurred, such as by a Gaussian filter. The exact algorithm is not defined; however the resulting shadow must approximate (with each pixel being within 5% of its expected value) the image that would be generated by applying to the shadow a Gaussian blur with a standard deviation equal to half the blur radius

Note this means for a long, straight shadow edge, the blur radius will create a visibly apparent color transition approximately the twice length of the blur radius that is perpendicular to and centered on the shadow's edge, and that ranges from almost the full shadow color at the endpoint inside the shadow to almost fully transparent at the endpoint outside it.

ガウスぼかしではピクセル単位で処理をするため、小数点以下の大きさは無視されてステップ的にぼかし (=影) の大きさが変化することになります。
その為、CSS アニメーションさせると影がカクカクと変化してしまうと考えられます。
スマホや 4K ディスプレイのように画素ピッチが十分に狭ければ、滑らかに影が伸びるように見えます。

その証拠に、ブラウザの拡大機能を使って 500% 程度まで大きくすると先ほどのアニメーションは滑らかに動くことが確認できます。
CSS アニメーション (transition や animation) は GPU で処理されるので、HTML 上のピクセルサイズは関係なく表示を大きく拡大した状態であればステップが十分に大きく取れて滑らかになるというわけです。

一方、工夫したコードでは元々の影の範囲を広げてあるので transition で変化するのは色だけということになります。
色はディスプレイやグラフィックボードに依りますが、概ね 8bit 以上は確保されているでしょうから滑らかに変化させることができるというわけです。

まとめ

以上、box-shadow を transition させた時にカクカクさせない小技でした。
細かいことですが、きっとユーザはこういった違いを感じ取ってくれると思います。