CSS アニメーションのヒントとコツ

CSS アニメーションはドキュメントとアプリを構成する要素を使って驚くべき事を行うことを可能にします。 しかしながら、行いたくても理解しやすくないことがあったり、すぐに思いつかないであろう巧妙な方法があります。 この記事は停止したアニメーションをどのように再び実行するのかを含む、あなたの仕事を楽にするかもしれない私達が見つけた秘訣やトリックを集めたものになります。

再度アニメーションを実行する

CSS アニメーションの仕様書では再度アニメーションを実行する方法を提示していません。 要素に魔法の関数 resetAnimation() というものはなく、要素の animation-play-state を再び "running" に設定することさえもできません。その代わりに、停止したアニメーションをリプレイするための巧妙なトリックを使う必要があります。

ここでの私達が考える停止したアニメーションのリプレイを行う方法はあなたに提案することに対して十分に信頼、安定しています。

HTML の内容

はじめに、HTML にアニメーションしてほしい <div> と、アニメーションを再生(またはリプレイ)するためのボタンを定義しましょう。

<div class="box">
</div>

<div class="runButton">Click me to run the animation</div>

CSS の内容

次に CSS を使ってアニメーションそのものを定義しましょう。 簡潔さのためにいくつかの重要ではない CSS (”Run” ボタンそのもののスタイル) はここではお見せしません。

@keyframes colorchange {
  0% { background: yellow }
  100% { background: blue }
}

.box {
  width: 100px;
  height: 100px;
  border: 1px solid black;
}

.changing {
  animation: colorchange 2s;
}

ここに2つのクラスがあります。 ”box” クラスはアニメーション情報を含まない box の外見の基本的な記述です。 アニメーションの詳細は ”changing” クラスに含まれており、その中で ”colorchange” と名付けられた @keyframes は box をアニメーションするために2秒間を使うべきだと書いてあります。

このため、box は所定の位置でアニメーション効果を開始しないので、アニメーションすることはないでしょう。

JavaScript の内容

次にアニメーションを行うための JavaScript を見ていきます。 このテクニックの内容は play() 関数にあり、ユーザーが "Run" ボタンをクリックした時に呼ばれます。

function play() {
  document.querySelector(".box").className = "box";
  window.requestAnimationFrame(function(time) {
    window.requestAnimationFrame(function(time) {
      document.querySelector(".box").className = "box changing";
    });
  });
}

これは奇妙に見受けられるでしょうか? 再度アニメーションを実行する唯一の方法はアニメーション効果を取り除くことであるため、あなたが行いたいことをドキュメントに知らせてあげて、ドキュメントにスタイルの再計算させてあげましょう。 その時要素に戻るためのアニメーション効果を追加します。 それを実現するためには、私達は創造的でなければなりません。

play() 関数が呼び出された時に起きることは以下のものです。

  1. box の CSS クラスのリストを単純な "box" にリセットします。 これは、その box に現在適用されている他のどのクラスを取り除く効果を持ちます。 これにはアニメーションを扱う "changing" クラスが含まれます。 言い換えると、box からアニメーション効果を取り除きます。 しかしながら、クラスのリストを変更することはそのスタイルの再計算が完了するまで効果を持たず、更新はその変更を反映するために発生し続けます。
  2. スタイルが確実に再計算されるように、window.requestAnimationFrame() を使用し、コールバックを指定します。 コールバックは次のドキュメントの再描画の前に適切に実行されます。 問題はそのコールバックの呼び出しが再描画の前であるゆえに、スタイルの再計算はまだ実際に起きてはいないのです。
  3. コールバックは巧妙でもう一度 requestAnimationFrame() を呼びます! 今回は、スタイルの再計算が行われた後の次の再描画の前に、コールバックが実行されます。 このコールバックは box クラスの後ろに "changing" クラスを追加し、そのためクラスを追加したことによる再描画はもう一度要素のアニメーションを始めることでしょう。

もちろん、実際に動作するために "Run" ボタンにイベントハンドラーを追加することは必要です。

document.querySelector(".runButton").addEventListener("click", play, false);

結果

アニメーションの停止

単純に、要素に適用した animation-name を取り除くことはそれの次の状態にジャンプまたは割り込ませます。 代わりに、アニメーションを完了して停止する場合は、別のアプローチを試す必要があります。 主要なトリックは次のものです。

  1. 可能なら自身を含めるようアニメーションさせること。 これは animation-direction: alternate を信頼するべきではないことを意味します。 代わりに一回の繰り返しの中で完全なアニメーションを経過するキーフレームアニメーションを明示的に書くべきです。
  2. JavaScript を使用し、animationiteration イベントが発生した時に使われるアニメーションをクリアーすること。

次のデモは前述の JavaScript テクニックをどのように利用するかを示します。

.slidein {
  animation-duration: 5s;
  animation-name: slidein;
  animation-iteration-count: infinite;
}

.stopped {
  animation-name: none;
}

@keyframes slidein {
  0% {
    margin-left: 0%;
  }
  50% {
    margin-left: 50%;
  }
  100% {
    margin-left: 0%;
  }
}
<h1 id="watchme">Click me to stop</h1>
let watchme = document.getElementById('watchme')

watchme.className = 'slidein'
const listener = (e) => {
  watchme.className = 'slidein stopped'
}
watchme.addEventListener('click', () =>
  watchme.addEventListener('animationiteration', listener, false)
)

デモ https://jsfiddle.net/morenoh149/5ty5a4oy/

関連項目