こんにちは。
Eightでフロントエンドエンジニアをしている鳥山(@pvcresin)です。
気づいたら開発者体験(DX)向上の取り組みばかり記事にしていたので、たまには実装の記事も書いていこうと思います。
今回は、ローディングを CSS アニメーションで実装した話になります。
ローディング
システムが何か処理をしている状態や、それをユーザーに知らせる UI のことをローディングといいます。 ユーザーが何かアクションを起こしても画面に変化がなかった場合、ユーザーにはシステムが何も処理をしていないか、フリーズしているように見えてしまいます。 そこでローディングを画面上で表現し、処理の最中であることをユーザーに知らせることで、ユーザーの離脱を軽減することができます。
Web サービスでは、ローディングを手軽に表現できるものとして、ローディングアニメーションがよく用いられます。 ローディングアニメーションの呼び名はいくつかありますが、Loader や Progress Indicator と呼ばれることが多い印象です。 また、その中でも円形でくるくる回るものは Spinner、線形で進捗を表すものは Progress Bar など、種類によって個別の呼び名があります。
Eight オリジナルのローディング
Eight ではロゴを使ったオリジナルの Spinner、EightSpinner が以前から使われていました。 例えば、ダイレクトリクルーティングサービス「Eight Career Design」では、候補者からのメッセージの一覧を取得する Inbox 画面で EightSpinner を使っています。
EightSpinner はアニメーションの時間が長めで演出も派手なため、時間がかかる処理の最中や画面上に大きな空白が存在する場面で使われています。
では、これを拡大して背景色を黒く設定してみましょう。
おわかりいただけただろうか...
アウトラインがギザギザしています。
お察しの通り、この EightSpinner は GIF です。
そのため、一定以上の拡大縮小に耐えられず、色の変更もできないという少し使い勝手の悪いものとなっていました。
おそらく何らかのソフトウェアでアニメーションを作成し、GIF に書き出されたものなので、
元データがあればもっとたくさんのサイズに書き出しができます。
しかし、かなり昔に作られたもののようで、その元データは行方不明とのことでした 😇
そこで、この GIF 版 EightSpinner を、拡大縮小に耐えられるように CSS アニメーションで再現することにしました。
ちなみに、実現方法としては Lottie も候補に挙がりました。 Lottie は Adobe の After Effects で作成されたアニメーションをクロスプラットフォームで再現できるツールです。 しかし、再現したいアニメーションがそこまで複雑でないことに加え、GIF 版 EightSpinner が Web でしか使われていなかったことから、CSS アニメーションで実装することにしました。
CSS アニメーション
CSS アニメーションとは、CSS を使って要素にアニメーションを加えることができる機能のことを指します。
例えば、box というクラス名がついた div 要素があるとします。
<div class="box"></div>
これを赤から青に色を変えつつ180 度回転させるには、
.box { width: 100px; height: 100px; animation: rotation 2s ease infinite; } @keyframes rotation { 0% { background: red; } 100% { background: blue; transform: rotate(180deg); } }
のように書きます。 適用すると、以下のようになります。
CSS アニメーションを使うことで、手軽にアニメーションを実現できます。
実装
では本題の実装についてです。
まずは実装の方針を決めるため、再現したい元の GIF アニメーションをよく観察してみました。
すると、
- A: 2 つの円が互いに近づいたり離れたりする
- B: 2 つの円を含む要素全体が回転する
という 2 つの動き(A, B)に分解できそうだと考えました。 そこで構造として、
<div class="base"> <div class="circle1"></div> <div class="circle2"></div> </div>
のような形をとることにし、それぞれの動きを大まかに実装してみます。
まずは、要素のサイズの定義や配置を行います。
.base { width: 72px; height: 72px; position: relative; display: flex; justify-content: center; } .circle1, .circle2 { position: absolute; top: 25%; box-sizing: border-box; width: 50%; height: 50%; border: 3px solid; border-radius: 50%; } .circle1 { border-color: red; transform: translate(0, 50%); } .circle2 { border-color: blue; transform: translate(0, -50%); }
最初は、以下のような状態です。
ここから、2 つの円が互いに近づいたり離れたりする動きを実現するには、 2つの円の .base の中心からの距離を変化させます。
.circle1 { border-color: red; animation: circle1-motion ease 2s infinite; /* 修正 */ } .circle2 { border-color: blue; animation: circle2-motion ease 2s infinite; /* 修正 */ } /* 以下、追記 */ @keyframes circle1-motion { 0% { transform: translate(0, 0); } 50% { transform: translate(0, 50%); } 90% { transform: translate(0, 0); } } @keyframes circle2-motion { 0% { transform: translate(0, 0); } 50% { transform: translate(0, -50%); } 90% { transform: translate(0, 0); } }
するとこのようになりました。
これで動き A は OK です。
次に、最初の状態に戻した上で、今度は .base を回転させてみます。
.base { width: 72px; height: 72px; position: relative; display: flex; justify-content: center; animation: base-motion linear 2s infinite; /* 追記 */ } /* 以下、追記 */ @keyframes base-motion { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
するとこのようになりました。
これで動き B も OK です。
あとは、色やサイズ、アニメーションの時間などを根気強く目でデバッグしながら調節していきます。
最後に 2 つの動きを組み合わせると...
上手く GIF の動きを再現できました 🎉
CSS 版 EightSpinner の完成です。
これで拡大縮小や色変更にも対応できる形に生まれ変わりました。
トータルの作業時間は約 2 時間でした。
Tips
CSS アニメーションのデバッグには Google Chrome の開発者ツールにある Animations タブが有用でした。 アニメーションのタイミングを時系列順に並べて可視化することや、アニメーションを実際よりもゆっくり再生することができます。
また、easing の調節には cubic-bezier.com というサイトが役立ちました。 このサイトでは、動きを確認しながらベジェ曲線を用いた easing の調節を行うことができます。
まとめ
今回は GIF しか残されていなかった Eight オリジナルのローディングを CSS アニメーションで再現した話をしました。
CSS アニメーションを使いこなせば、かなり複雑な動きも実現することができます。
CSS アニメーションをサービスに組み込んでみたいけど、なかなか使う場面がないという方は、ローディングから始めてみてはいかがでしょうか?