CSSのスクロール駆動アニメーションを1回だけ実行し、終了フレームに留まらせる実装方法 -runOnce
Post on:2023年10月17日
スクロール駆動アニメーションは、ビジターがスクロールするとそれに連動して要素がアニメーションします。スクロールに連動するということは、上下に繰り返しスクロールするとアニメーションも繰り返し実行されてしまいます。
スクロール駆動アニメーションを1回だけ実行し、アニメーションの終了フレームに留まらせる実装方法を紹介します。
Run a Scroll-Driven Animation only once
by Bramus!
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
スクロール駆動アニメーション(Scroll-Driven Animations)は、スクロールによって制御されます。上下にスクロールすると、アニメーションはそれに反応して前後にスクラブします。
しかし、スクロール駆動アニメーションが再生された後、その終了フレームに留まりたい場合にどうすればよいでしょうか?
この記事で紹介する小さなjavaScriptが役に立ちます。
スクロール駆動アニメーション(Scroll-Driven Animations)について詳しくは、下記をご覧ください。
CSSでの実装が大きく変わる! Scroll-driven Animations スクロールをトリガーにしたアニメーションを実装する方法
スクロール駆動アニメーションを1回だけ実行するコード
コードだけが目的なら、@bramus/sda-utilities
パッケージからrunOnce
をインポートできます。
1 2 3 4 5 6 7 |
import { runOnce } from '@bramus/sda-utilities'; // Run the “fade-in” scroll-driven animation on the `#hero` element only once. window.addEventListener('load', (e) => { const $hero = document.querySelector('#hero'); runOnce($hero, 'fade-in'); }); |
このJavaScriptの動作を確認したい場合は、下記のデモをご覧ください。
runOnce
関数自体のソースコードはGitHubで入手できます。
コードの使い方
runOnce
関数を呼び出し、1回だけ実行したいアニメーションの名前とともに要素への参照を渡します。
1 2 3 |
document.querySelectorAll(".photo").forEach(($photo) => { runOnce($photo, "animate-in"); }); |
渡されるアニメーション名(上記ではanimate-in
)は、オプションで省略可能です。渡さない場合は、その要素に付属するすべてのスクロール駆動アニメーションは1回だけ実行されます。
仕様では、対象とする要素がDOMで利用可能になった瞬間からこのコードを実行できます。しかし残念ながらChrome 115-117にはバグがあり、タイムラインの計算中に不正なanimationend
イベントをトリガーする可能性があります。このバグはChrome 118.0.5993.11で修正されていますが、それ以前のバージョンでは回避策が必要です。
その回避策は簡単です。
window
のload
イベントがトリガーされた後に、イベントリスナーをアタッチするだけです。
1 2 3 4 5 |
window.addEventListener("load", (e) => { document.querySelectorAll(".photo").forEach(($photo) => { runOnce($photo, "animate-in"); }); }); |
なぜ機能するのか
このコードのポイントは、渡された要素のanimationend
イベントをリッスンすることです。その要素から関連するアニメーションが除外され、停止されます。
1 2 3 4 5 6 7 8 9 10 |
$el.addEventListener('animationend', (e) => { const animation = animations.find((a) => a.animationName == e.animationName); if (shouldAnimationBeStopped(animation, animationName)) { animation.commitStyles(); animation.cancel(); } }); |
アニメーションを効果的に停止するには、animation.cancel();
が呼び出される前にanimation.commitStyles();
を使用してアニメーションの現在のスタイルの計算値を要素に書き込みます。
削除時の不具合を防ぐには、animation-fill-mode
をforwards
またはboth
に設定することをお勧めします。これを設定していない場合、不具合が発生する可能性があるという警告がコンソールに表示されます。
アニメーションが要素のアニメーションリストからフィルタリングされる方法は、すべての一般的なシナリオで機能しますが、同じ名前がつけられた複数のアニメーションがある場合は機能しません。回避策として、要素上の各アニメーションに固有の名前をつけてください。
CSS WG Resolutionによると、animation-*
イベントは渡された実際のAnimation
オブジェクトも取得するように更新され、この問題が修正されました。しかし、2023年10月現在ではどのブラウザにも実装されていません。これを追跡するにはbugs.chromium.orgをチェックしてください。
デモ
実際の動作は、デモページでご覧ください。
アニメーションがいつ終了したかを明確にするために、その状態に達した要素の周囲にライムのボーダーが引かれます。
See the Pen
Run Scroll-Driven Animations only once by coliss (@coliss)
on CodePen.
sponsors