--- title: スクロール連動エフェクト slug: Mozilla/Performance/Scroll-linked_effects tags: - CSS - JavaScript - Performance - Scroll - Scroll-Linked Effects - Web Animations - compositor translation_of: Mozilla/Performance/Scroll-linked_effects ---
スクロール連動エフェクトはスクロール位置に基づいて Web ページを変化させるエフェクト実装です。(例えば、スクロールによる視差エフェクトを生み出すために位置プロパティを更新させるなど) この記事はスクロール連動エフェクトについてパフォーマンス・関連するツール・移行技術の可能性について記載しています。
時々スクロールエフェクトは {{event("scroll")}} イベントを監視し、いくつかの手段(通常は CSS の {{cssxref("position")}} や {{cssxref("transform")}} プロパティ)でページ内の要素を更新して実装されます。このようなエフェクトは CSS Scroll API: Use Cases でサンプルを見ることができます。
これらのエフェクトは、スクロールがブラウザのメインスレッドで同期的に完了するブラウザの場合はうまく動作します。しかし、現在多くのブラウザはユーザに一貫した 60 FPS の体験を提供するために何らかの非同期スクロールを提供しています。非同期スクロールモデルでは、表示されているスクロールポジションはコンポジタースレッドで更新されて {{event("scroll")}} イベントが DOM 内で更新されメインスレッドで発火される以前にユーザーに表示されます。これは、実装されたエフェクトがユーザーが更新されたスクロール位置を見るよりも少し遅れることになります。これはエフェクトが遅延したり、カクカクしたり、ユーザーをイライラさせます。つまりできることなら避けたい事象です。
以下は非同期スクロールでうまく動作しないエフェクト例と、適切に動作するサンプルです。
これはスクロールダウンしても "toolbar" div がスクリーンの上部に固定される sticky 位置エフェクトの実装です。
<body style="height: 5000px" onscroll="document.getElementById('toolbar').style.top = Math.max(100, window.scrollY) + 'px'"> <div id="toolbar" style="position: absolute; top: 100px; width: 100px; height: 20px; background-color: green"></div> </body>
この sticky 位置の実装は "toolbar" div の再配置を監視するスクロールイベントリスナーに基づいています。スクロールイベントリスナーがブラウザーのメインスレッドで動作する JavaScript で動く限り、ユーザーが見えているスクロールとは非同期処理になります。したがって非同期スクロールにおいて、イベントハンドラーはユーザーに見えているスクロールの動きとは相対的に遅延して、div は意図したとおり固定された表示になりません。代わりに、ユーザーのスクロールによって div は移動して、スクロールインベントハンドラーが実行されたときにスクリーンジョブに "すばやく戻ります"。この一連の動きと素早い動作はかくかくした視覚エフェクトになります。スクロールイベントリスナーを利用しない 1 つの実装は、これを目的とした CSS プロパティーを利用する方法です。
<body style="height: 5000px"> <div id="toolbar" style="position: sticky; top: 0px; margin-top: 100px; width: 100px; height: 20px; background-color: green"></div> </body>
このバージョンは、ユーザーのスクロールによる "toolbar" div の位置更新をブラウザ自身が行うため、非同期スクロールでもうまく動作します。
Thisこの機能は Web 標準から削除されています。いくつかのブラウザではまだサポートされていますが機能削除の最中です。可能ならば利用を避け既存コードを更新してください。https://developer.mozilla.org/ja-JP/docs/Web/CSS/scroll-snap-coordinate#Browser_compatibility
以下はスクロールスナップの実装で、目的の位置近くでスクロールをユーザーが止めたときに指定した目的のスクロールポジション目的位置に移動するものです。
<body style="height: 5000px"> <script> function snap(destination) { if (Math.abs(destination - window.scrollY) < 3) { scrollTo(window.scrollX, destination); } else if (Math.abs(destination - window.scrollY) < 200) { scrollTo(window.scrollX, window.scrollY + ((destination - window.scrollY) / 2)); setTimeout(snap, 20, destination); } } var timeoutId = null; addEventListener("scroll", function() { if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(snap, 200, parseInt(document.getElementById('snaptarget').style.top)); }, true); </script> <div id="snaptarget" class="snaptarget" style="position: relative; top: 200px; width: 100%; height: 200px; background-color: green"></div> </body>
このサンプルでは、スクロールしたときのポジションが "snaptarget" div の上部 200 ピクセルいないだった場合を検知するスクロールイベントリスナーが存在します。もしその条件だった場合、div の上部へスクロール位置を移動するアニメーションを動かします。アニメーションはブラウザのメインスレッド JavaScript で動作するため、他のタブやウィンドウで動作している JavaScript によって割り込まれるかもしれません。従って、期待通りにスムーズに動かなかったりカクカク動作するようになります。代わりに CSS sap-point プロパティを利用することで、アニメーションを非同期で動作させることをブラウザに許可し、ユーザーにスムーズな視覚エフェクトを提供します。
<body style="height: 5000px"> <style> body, /* blink currently has bug that requires declaration on `body` */ html { scroll-snap-type: y proximity; } .snaptarget { scroll-snap-align: start; position: relative; top: 200px; height: 200px; background-color: green; } </style> <div class="snaptarget"></div> </body>
このバージョンはブラウザのメインスレッドで重たい JavaScript が動作していたとしてもスムーズにブラウザで動作することが可能です。
多くの場合、スクロール連動エフェクトは CSS を利用したりコンポジットスレッドで動作させることで再実装可能です。しかし、いくつかのケースにおいて現在ブラウザが提供している API では許可されない物が有ります。しかし全ての場合において Firefox はスクロール連動エフェクトをページ上で検知すると開発コンソールに警告を表示します。(バージョン 46 以降) JavaScript でスクロールイベントを使わない JavaScript を使ったスクロールエフェクトの場合は警告は表示されません。他の CSS を利用した実装でカクカクした動作を防ぐサンプルを知りたい場合は、Firefox における非同期スクロール というブログを見てください。
更に我々はコンポジターでのさらなるエフェクトをサポートする予定です。これを実現するために、あなたが実装しようとしているスクロール連動エフェクトの種類に関する情報提供を必要としており、それは実現するための手助けになります。現在そのようなエフェクトを許可するためのいくつかの提案があり、これらにはメリット/デメリットがあります。この提案は現在以下の仕様で議論されています。
以下の事を考えや意見がある場合、
我々に情報提供してください。public-houdini メーリングリスト上での議論に参加可能です。