10分で簡単!CSSだけでできるローディングアニメーションを作ってみた

株式会社アビストAIソリューション事業部 AI-Plant Bamboo 開発チームの杉山です。

AI-Plant Bambooは、機械学習プラットフォームという特性上、データの読み込みやモデルの作成の段階でユーザーを待たせてしまう場面があります。

開発エンジニアとして、こうした待ち時間を減らすことに注力しますが、設計やリソースの都合上、どうしても待ち時間を短縮できない場合があります。

このような待ち時間が発生する場面では「操作を間違ったかもしれない」「壊してしまったかもしれない」といったユーザーの不安を取り除くことが大切です。

ユーザーの不安を軽減する方法の1つとして「ローディングアニメーションを表示する」というものがあります。ローディングアニメーションを表示することによって「アプリケーションが動作している」ということを視覚的に表現しつつ「いまは待つ時間だよ」ということをユーザーに伝えることができます。

今回は、HTMLとCSSだけで簡単に実装できるローディングアニメーションを作ってみました。 CSSでアニメーションを作る基本的な方法から、ローディングアニメーションの実装方法まで、分かりやすく紹介しますので「CSSでアニメーションを作ってみたい」という方は、ぜひ気軽に試してみてください。

0. 対象とする読者

  • CSSでアニメーションを作ってみたい方
  • JavaScriptを使わずにアニメーションを作りたい方
  • ローディングアニメーションの実装例を見たい方

1. CSSで作るローディングアニメーション

Webアプリケーション上でアニメーションを表示する方法としては「JavaScriptを使用する方法」や「GIF画像を作成して埋め込む方法」など、いくつかありますが、CSSを使用して作る方法はとても簡単です。

今回は、CSSとHTMLで、以下のようなローディングアニメーションを作成します。

ローディングアニメーション

2. CSSアニメーションの作り方

CSSでは@keyframesという@-規則を利用して作成します。 「@-規則」はCSSの動作を規定するものです。

@keyframesを使用すると、アニメーションのキーフレームを定義できます。

キーフレームは、アニメーションの主要な変化を定義するフレームで、アニメーションの変化点における要素のスタイルを定義します。 アニメーションが実行される際に、変化点から変化点に、定義したスタイルをなめらかにつなぐように描画されます。

@keyframes アニメーションの名前 {
    0% {
        /* アニメーションの開始時のスタイルを記述する */
    }
    50% {
        /* アニメーションが50%進行した状態のスタイルを記述する */
    }
    100% {
        /* アニメーションの終了時のスタイルを記述する */
    }
}

上記の @keyframes には「開始時点」「50%進行した時点」「終了時点」のキーフレームを定義した例を示しています。

実例を見たほうが分かりやすいと思いますので、例として「正方形の箱が右方向にスライドするアニメーション」を作ってみましょう。

<div id="object"></div>
#object {
    animation: 2s slide;
    background-color: green;
    height: 40px;
    width: 40px;
}

@keyframes slide {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(100px);
    }
}

ここでは0%(開始状態)のスタイルと100%(終了状態)のスタイルを定義しています。

transformは、指定した要素を変形したり移動したりできるプロパティです。 translateX(移動距離)を記述することによって、要素がX軸方向に移動します。

このアニメーションは以下のように表示されます。

f:id:ABIST_AI:20200803001514g:plain

緑色の四角形が画面右方向に連続的に移動します。 さらに、以下のように、スタイルを追加することによって、色を変えたり、回転を加えることもできます。

スライドアニメーション2

#object {
    animation: 2s slide;
    background-color: green;
    height: 40px;
    width: 40px;
}

@keyframes slide {
    0% {
        background-color: blue;
        transform: translateX(0);
    }
    100% {
        background-color: green;
        transform: translateX(100px) rotateZ(90deg);
    }
}

このように、キーフレームごとにスタイルを記述することによって、複雑なアニメーションを実現できます。

3. CSSアニメーションを要素に適用する

上記のようにして定義したアニメーションは、animationプロパティを通じて、指定した要素に適用できます。 例えば、以下のようなHTMLがあり、id="object"の要素に対してアニメーションを適用したいとします。

<div id="container">
    <div id="object"></div>
</div>

このとき、#objectに対して以下のようなスタイルを定義することによって、アニメーションを適用できます。

#object {
    animation: [アニメーションの名前] [アニメーションの時間];
}

たとえば slide という名前のアニメーションを1秒間実行するには、以下のようにスタイルを記述します。

#object {
    animation: slide 1s;
}

4. ローディングアニメーションを作ろう

それでは、この記事のテーマである、ローディングアニメーションを作りましょう。

今回は、以下のようなアニメーションを作成します。

ローディングアニメーション

stackblitz.com

4本のバーが下から階段状に現れ、4本のバーが同じ高さに揃った後で同じ高さに集合し、もとに戻る、というシンプルなローディングアニメーションです。

4.1. HTMLを設置する

まず、HTMLを記述します。アニメーション自体が入るコンテナと、その内側に4本のバーを配置します。 それぞれcontainerクラスとbarクラスを付与します。

<div class="container">
  <div class="bar"></div>
  <div class="bar"></div>
  <div class="bar"></div>
  <div class="bar"></div>
</div>

4.2. 基本のスタイルを記述する

次に、アニメーションのない初期状態で適用するスタイルを記述します。

.container {
  height: 200px;
  position: relative;
  width: 200px;
}

.bar {
  background-color: #37A372;
  bottom: 0;
  height: 100px;
  position: absolute;
  transform-origin: center bottom;
  width: 20px;
}

バーの大きさは、幅を20px、高さを100pxとしました。

transform-origin は、後にそれぞれのバーにtransformプロパティで指定する変形方法について、変形の原点を指定するプロパティです。

{ transform-origin: center bottom; }

この例の場合、水平方向についてはcenterが、垂直方向についてはbottomが変形の原点となります。 これによって、アニメーションの実行時にバーが下側から出てくるようになります。

続いて、4本のそれぞれのバーにスタイルを記述します。 4本のバーが左から順に5px間隔で配置されるようにしました。

:nth-child() は擬似クラスで、兄弟要素について位置を指定して、スタイルを記述できます。

.bar:nth-child(1) {
  left: calc(25px * 1);
}

.bar:nth-child(2) {
  left: calc(25px * 2);
}

.bar:nth-child(3) {
  left: calc(25px * 3);
}

.bar:nth-child(4) {
  left: calc(25px * 4);
}

calc() 関数を使用すると「25px * 2」のようにプロパティの値を計算によって指定できます。

ここまでのHTMLとCSSで、以下のように表示されます。

ローディングアニメーションの初期スタイル

4.3. アニメーションを記述する

さて、いよいよアニメーションを記述します。

4本のバーについて、それぞれタイミングを変えて動かすため、それぞれのバーについて@keyframesを定義して適用します。

@keyframes expand_bar_1 {
    /* 左から1本目のバーに適用するアニメーションを記述 */
}

@keyframes expand_bar_2 {
    /* 左から2本目のバーに適用するアニメーションを記述 */
}

@keyframes expand_bar_3 {
    /* 左から3本目のバーに適用するアニメーションを記述 */
}

@keyframes expand_bar_4 {
    /* 左から4本目のバーに適用するアニメーションを記述 */
}

また、今回作成するアニメーションは6つのステップに分割できます。

1. 1本目のバーが生える
2. 2本目のバーが生える
3. 3本目のバーが生える
4. 4本目のバーが生える
5. 全てのバーの高さがそろう
6. 全てのバーが引っ込む

@keyframesには、0~100%で値を指定してキーフレームを定義する必要があるため、アニメーションを6つに分割し、以下の変化点でキーフレームを定義します。

1. 1番目のバーが生える (~16%)
2. 2番目のバーが生える (~32%)
3. 3番目のバーが生える (~48%)
4. 4番目のバーが生える (~64%)
5. 全てのバーの高さがそろう (~80%)
6. 全てのバーが引っ込む (~100%)

各バーの@keyframesは、以下のような構造になります。

@keyframes expand_bar_#num {
  0% {
    /* 開始状態のスタイルを記述 */
  }
  16% {
    /* ステップ1の状態のスタイルを記述 */
  }
  32% {
    /* ステップ2の状態のスタイルを記述 */
  }
  48% {
    /* ステップ3の状態のスタイルを記述 */
  }
  64% {
    /* ステップ4の状態のスタイルを記述 */
  }
  80% {
    /* ステップ5の状態のスタイルを記述 */
  }
  100% {
    /* ステップ6の状態のスタイルを記述 */
  }
}

アニメーションの各ステップにおいて、変化するスタイルは各バーの「高さ」だけです。 高さはtransformプロパティのscaleY()関数を用いて、縮尺比率で定義します。

{ transform: scaleY(Y軸方向の縮尺比率); }

以上を踏まえて、各バーの@keyframesを記述すると以下のようになります。

@keyframes expand_bar_1 {
  0% {
    transform: scaleY(0);
  }
  16% {
    transform:  scaleY(0.25);
  }
  32% {
    transform:  scaleY(0.25);
  }
  48% {
    transform: scaleY(0.25);
  }
  64% {
    transform: scaleY(0.25);
  }
  80% {
    transform: scaleY(0.5);
  }
  100% {
    transform: scaleY(0);
  }
}

@keyframes expand_bar_2 {
  0% {
    transform: scaleY(0);
  }
  16% {
    transform: scaleY(0);
  }
  32% {
    transform: scaleY(0.5);
  }
  48% {
    transform: scaleY(0.5);
  }
  64% {
    transform: scaleY(0.5);
  }
  80% {
    transform: scaleY(0.5);
  }
  100% {
    transform: scaleY(0);
  }
}

@keyframes expand_bar_3 {
  0% {
    transform: scaleY(0);
  }
  16% {
    transform: scaleY(0);
  }
  32% {
    transform: scaleY(0);
  }
  48% {
    transform: scaleY(0.75);
  }
  64% {
    transform: scaleY(0.75);
  }
  80% {
    transform: scaleY(0.5);
  }
  100% {
    transform: scaleY(0);
  }
}

@keyframes expand_bar_4 {
  0% {
    transform: scaleY(0);
  }
  16% {
    transform: scaleY(0);
  }
  32% {
    transform: scaleY(0);
  }
  48% {
    transform: scaleY(0);
  }
  64% {
    transform: scaleY(1);
  }
  80% {
    transform: scaleY(0.5);
  }
  100% {
    transform: scaleY(0);
  }
}

4.4. アニメーションを適用する

最後に、これらの@keyframesを各バーに対して適用しましょう。 animationプロパティを介して適用します。

{ animation: 6s expand_bar_#num ease infinite; }

1ステップあたり約1秒のアニメーションにしたいので、アニメーションの継続時間を 6(ステップ) x 1(秒) = 6(秒) とします。

また、ease(animation-timing-function)を指定することによって、各キーフレームの動作がなめらかになります。

さらに、infinite(animation-iteration-count)を指定することによって、アニメーションが繰り返し実行されます。 今回はローディングアニメーションであり、処理が終わるまで繰り返し動作させたいのでinfiniteを指定しました。

.bar:nth-child(1) {
  animation: 6s expand_bar_1 ease infinite;
  left: calc(25px * 1);
}

.bar:nth-child(2) {
  animation: 6s expand_bar_2 ease infinite;
  left: calc(25px * 2);
}

.bar:nth-child(3) {
  animation: 6s expand_bar_3 ease infinite;
  left: calc(25px * 3);
}

.bar:nth-child(4) {
  animation: 6s expand_bar_4 ease infinite;
  left: calc(25px * 4);
}

こうして、以下のようなローディングアニメーションができました。

ローディングアニメーション

5. まとめ

今回はCSSとHTMLだけで作れるローディングアニメーションを実装しました。 CSSだけでもそれなりに動くアニメーションを作れることが分かっていただけたかと思います。

ピュアなCSSを用いたため、繰り返し記述が必要な部分がありました。 Sass等を使えばより少ない記述で書けるかもしれません。

株式会社アビストAIソリューション事業部では、このような技術者向けの記事を今後も公開する予定です。 Twitterもやっていますので、ぜひフォローしてください。

「こんなアニメーションを作ってみました」とか「こうするともっときれいに実装できるよ」といったコメントもお待ちしております。

AIソリューション事業部 公式Twitterアカウント @abist_ai