サイトの読み込みが終わらないのでアニメーションを調べてみた

update

この記事は CAMPHOR- Advent Calendar 2019 2日目 の記事です。

こんにちは、ほないです。Advent Calender の記事を書くのは、CAMPHOR- 以外のものも含めて初めてなので、わくわくしています。 1日目のあたらんさんの記事、おもしろかったです。私も質問箱をつくりたくなりました。

今回はフロントエンドのアニメーションの話です。CSSやSVGのアニメーション、マイクロインタラクション、UIデザインに興味がある人におすすめの内容です。最後まで読んでいただけるとうれしいです。

ローディングでくるくる回るアレ

「あれ?ローディングが終わらない…」

スマホアプリやWebアプリで使われるインジケーター、みなさんも見たことがあると思います。データを取得しているときに表示して、ユーザーに「今がんばってるのでちょっと待ってね」ということを伝えるためのものですね。

ある日、あるサイトでこのインジケーターが表示されたまま進まなくなったとき、ふと「このくるくるってどんなを動きしているんだろう」と思ったのです。 じっと見つめてもよくわかりませんよね…。そこで、React向けのマテリアルデザインのUIフレームワークである Material-UI 注1 のソースコードを読んで、アニメーションを調べてみることにしました。

注1 本家 Material Design のソースを見ても良かったのですが、 Web版に丸いインジケーターがありませんでした。

円を描いたり消したりしながら回転している

このインジケーターは、div の中にSVGで描かれた円 ( circle 要素) でできています。

<!-- HTML -->
<div class="circular-progress">
  <svg>
    <circle cx="44" cy="44" r="20.2"stroke-width="3.6" />
  </svg>
</div>

この divcircle に別々のアニメーションが設定されています。

+
=

わかりやすさのために div をグレーにしています。真ん中が div にのみアニメーションをかけたものです。このアニメーションは、シンプルに1.4秒で等速で1回転するものです。

/* CSS */
div {
  animation: circular-rotate 1.4s linear infinite
}
@keyframes circular-rotate {
  100% { transform: rotate(360deg); }
}

左側が circle にのみアニメーションをかけたものです。まだ少しわかりにくいので、さらに分解して、遅くしてみましょう。

=

circle 要素のアニメーションは2段階あり、1つ目で時計回りに円を描いて、2つ目でその円を時計回りに消していくようになっています。

/* CSS */
circle {
  animation: circular-dash 1.4s ease-in-out infinite;
}
@keyframes circular-dash {
  0% {
    stroke-dasharray: 1px 200px;
    stroke-dashoffset: 0px;
  }
  50% {
    stroke-dasharray: 100px 200px;
    stroke-dashoffset: -15px;
  }
  100% {
    stroke-dasharray: 100px 200px;
    stroke-dashoffset: -125px;
  }
}

stroke-dasharraystroke-dashoffset は、本来は輪郭を破線にしてその間隔や位置を調節するプロパティですが、ここでは円を途中まで描くために使われています。

参考: stroke-dasharray - MDN , stroke-dashoffset - MDN

速度の変化は ease-in-out なので、最初は遅くてだんだん速くなって最後は遅くなる、という動きになります(図は Firefox の開発者ツールから引用)。

ease-in-out

以上が丸いプログレスインジケーターのアニメーションにです。シンプルで気持ち良い動きですが、これを頭の中で思いついてコードにおこすのは難しそうです。

プログレスバーのアニメーション

Material Design では、進捗を表す要素として、丸いインジケーターのほかに四角いバー型のインジケーターも提供されています。いわゆるプログレスバーです。

Material Design のドキュメント では、現在の進捗の割合に合わせてバーを進めていく determinate という状態の他に、進捗が数字ではわからないときのための indeterminate という状態も定義されています。上に置いたバーは indeterminate なものです。

※ 話が前後しますが、先述の丸いインジケーターも、 indeterminate のアニメーションです。

このバーのアニメーションも読み解いていきましょう。

長さを変えながら右へ動くバー

プログレスバーのDOMの構成は次のようになっています。

<!-- HTML -->
<div class="linear-progress">
  <div class="bar bar1"></div>
  <div class="bar bar2"></div>
</div>

親の div が薄い色の四角形、子の div がそれぞれ position: absolute; で動いています。

動きをよく見ると、濃い色の四角形が2つ動いているのがわかると思います。別々にして遅くするとこんな感じになります。

/* CSS */
.linear-progress > .bar1 {
  animation: linear-progress-bar1 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
}
.linear-progress > .bar2 {
  animation: linear-progress-bar2 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
  animation-delay: 1.15s;
}
@keyframes linear-progress-bar1 {
    0% { left: -35%; right: 100%; }
   60% { left: 100%; right: -90%; }
  100% { left: 100%; right: -90%; }
}
@keyframes linear-progress-bar2 {
    0% { left: -200%; right: 100%; }
   60% { left:  107%; right:  -8%; }
  100% { left:  107%; right:  -8%; }
}

1つ目のバーは6割までの時間で左から右へ動きます。左端より右端のほうが速く動きます。また cubic-bezier で最初と最後は速く、中盤はゆっくりになります。

2つ目のバーは animation-delay で半分の時間だけ遅らせて開始します。左端が右端に追いつくような動きになっており、 cubic-bezier で前半は急速、後半はゆっくりになっていますね。

cubic-bezier(...) の指定によるバーの速度変化はこんな感じです(右が1つ目、左が2つ目のバー)。

cubic-bezier

以上がプログレスバーのアニメーションの解説になります。

まとめ

見慣れたアイコンのアニメーションも、詳しく見てみると細部までこだわってデザインされていることがわかります。イージングを1つ変えただけでもかなり印象が変わります(みなさんも開発者ツールでぜひ試してみてください!)。

端末の描画能力が向上し、UIそのものの静的な見た目のデザインだけでなく、それに動きをつけるデザインも重要になってきています。そういったデザインの手間を省きつつ整ったUIを使えるフレームワークは便利だなと改めて感じました。 また機会があれば、ダイアログやメニューの開閉のアニメーションについても調べてみたいです。

CAMPHOR- Advent Calender 3日目の担当は marty1martie さんです。