CSS講座 / キーフレームアニメーション:再生・終了状態・一時停止

CSSのキーフレームアニメーションは、時間軸上の値を定義し、ホバーなどの値変更がなくても再生できます。所要時間、遅延、反復回数、終了前後の値、一時停止はそれぞれ別の設定です。

transitionとanimationを使い分ける

transitionはホバーやクラス変更などによる前後の値を補間します。animationはキーフレームに開始・途中・終了の値を書き、適用と遅延の後に再生できます。反復や方向も別に指定できます。

ボタンの色変更だけならtransitionが簡潔です。通知の登場や複数段階の視覚説明ならanimationを検討できます。同じ要素の同じプロパティを両方で制御すると、どの値が効くか追いにくいため、実習では役割を分けます。

キーフレームを要素の再生設定へ結び付ける

fromは0%、toは100%で、途中は50%などで定義します。名前を宣言するだけでは再生せず、要素のanimation-nameから参照します。

@keyframes travel {
  0% { transform: translateX(0); }
  50% { transform: translateX(3rem); }
  100% { transform: translateX(6rem); }
}
.marker { animation: travel 4s linear 1s 1 normal forwards; }

これは完全例の同名ルールを短く表したものです。4sは一回の時間、1sは開始待ちです。durationを省略すると初期値0秒なので過程を観察しにくくなります。最初は個別のプロパティで時間と回数を区別します。

timing-functionは進み方、iteration-countは回数、directionは方向、fill-modeは再生範囲外の値、play-stateは進行の停止を扱います。infiniteは無限反復で、pausedは現在の進行を一時停止します。実習はlinearを使いますが、等間隔のキーフレームでも進み方の関数で位置の変化は異なります。MDNの@keyframesを参照してください。

方向・回数・fill-modeを組み合わせる

normalは最初から最後、reverseは逆、alternateは一回目が順方向で次が逆方向です。整数回を完了する条件で、alternateを二回なら最後は開始側へ到達します。

forwardsが保つのは常に100%の値ではなく、方向と回数によって最後に到達したキーフレームです。例ではnormal一回は6rem、reverse一回とalternate二回は0の位置を保ちます。端数の回数は途中で終わることもあるので区別します。MDNのanimation-fill-modeを参照してください。

noneは遅延中も終了後も基本CSSです。forwardsは終了後、backwardsは遅延中、bothは両方にキーフレームの値を使います。例では基本位置を1rem、開始0、終了6remに分け、違いが見えるようにしています。必要な案内本文は、アニメーションなしでも完成状態で読める基本CSSにします。

完全例で一設定ずつ変える

次をanimation-lab.htmlとして保存します。上の案内が短く現れ、下の装飾は一秒後に四秒かけて動きます。変更を保存したら再読み込みし、開始から比べます。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Keyframes and end states exercise</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
.notice { padding: 1rem; border: 1px solid #90bdd8; border-radius: .5rem; background: #eaf4fa;
  opacity: 1; transform: none; animation: appear 400ms ease-out; }
@keyframes appear {
  from { opacity: 0; transform: translateY(.5rem); }
  to { opacity: 1; transform: translateY(0); }
}
.track { margin-block: 1rem; padding: 1rem; border: 2px dashed #8a9ba7; }
.marker { width: 2rem; height: 2rem; border-radius: .25rem; background: #166b92;
  transform: translateX(1rem);
  animation-name: travel; animation-duration: 4s; animation-timing-function: linear;
  animation-delay: 1s; animation-iteration-count: 1; animation-direction: normal;
  animation-fill-mode: forwards; }
@keyframes travel {
  0% { transform: translateX(0); background-color: #166b92; }
  50% { transform: translateX(3rem); background-color: #6a3989; }
  100% { transform: translateX(6rem); background-color: #28633b; }
}
#pause:checked ~ .track .marker { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .notice, .marker { animation: none; }
}
  </style>
</head>
<body>
<main>
<h1>Check entrance and movement</h1>
<p class="notice">This notice remains readable without animation.</p>
<h2>Comparing end states</h2>
<p>Movement starts one second after loading. Refresh the page to compare again.</p>
<input id="pause" type="checkbox"><label for="pause">Pause movement</label>
<div class="track"><div class="marker" aria-hidden="true"></div></div>
<p>The colors and movement are decorative. Essential information remains available independently of the animation, as in this sentence.</p>
</main>
</body>
</html>
  1. 標準では遅延中1rem、開始時0、終了後6remです。待ち時間と開始値が違うことを確認します。
  2. fill-modeだけをnoneへ変え、終了後に基本の1remと青へ戻ることを見てforwardsへ戻します。
  3. bothへ変えると遅延中も開始値0になります。確認してforwardsへ戻します。
  4. 回数1のままreverseへ変え、終了0を確認してnormalへ戻します。
  5. alternateの一回と二回を比べます。最後は回数1とnormalへ復元します。
  6. 再読み込み直後にPauseをチェックし、待ってから解除します。最初からやり直すのでなく、止めた進行から再開するか確認します。

動きがなくても内容を読めるようにする

動きを減らす条件では案内と装飾のanimationを外します。基本のopacityが1なので案内は読め、装飾は基本位置1remに残ります。読める値を最後のキーフレームやforwardsだけへ置くと、アニメーションがないときに内容が消える場合があります。

OSまたは開発者ツールで条件を有効にして再読み込みし、静止しても内容が見えるかを確認して設定を戻します。MDNのprefers-reduced-motion、画面と操作の点検を参照してください。

無限反復を付ける前に目的と停止方法を決めます。動かないなら名前、時間、動きを減らす条件を確認し、終了位置が違うなら方向、回数、fill-mode、基本CSSを分けて調べます。