CSS Scroll Snap を使用したスクロールの適切な制御

スクロールのスナップ位置を宣言することで、適切に制御されたスクロール エクスペリエンスを実現できます。

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

ウェブ デベロッパーは、CSS スクロール スナップ機能を使用して、スクロール スナップ位置を宣言することで、適切に制御されたスクロール エクスペリエンスを実現できます。たとえば、ページネーションされた記事と画像カルーセルの 2 つがよく使用されています。CSS Scroll Snap は、このような一般的な UX パターンを構築するための、使いやすく一貫した API を提供します。

背景

スクロール スナップのケース

スクロールは、ウェブ上のコンテンツを操作するための一般的で自然な方法です。これは、画面に同時に表示される情報よりも多くの情報にアクセスできるプラットフォーム固有の手段であり、画面のスペースが限られているモバイル プラットフォームで特に重要になります。したがって、ウェブ作成者が、深い階層ではなく、スクロール可能なフラットリストでコンテンツを整理することを好む傾向が強まっているのは当然と言えます。

スクロールの主な欠点は、精度に欠けることです。スクロールが段落や文に揃えられることはめったにありません。これは、有意義な境界があるページ分けされたコンテンツや項目別のコンテンツでは、スクロールがページまたは画像の中央で終了し、部分的に表示された場合、より顕著になります。このようなユースケースでは、適切に制御されたスクロール エクスペリエンスが役立ちます。

ウェブ デベロッパーは、この欠点に対処するために、スクロールの制御に JavaScript ベースのソリューションに依存してきました。しかし、JavaScript ベースのソリューションでは、スクロールのカスタマイズのプリミティブがないこと、または合成スクロールにアクセスできないため、完全な忠実度ソリューションを提供できません。CSS のスクロール スナップは、高速で高忠実度と使いやすさを兼ね備えた、複数のブラウザで一貫して機能する使いやすいソリューションです。

ウェブ作成者は CSS のスクロール スナップを使用して、各スクロール コンテナにスクロール操作を終了する境界をマークできます。ブラウザは、スクロール操作の詳細、スクロール コンテナのレイアウトと表示設定、スナップ位置の詳細に応じて、最適な終了位置を選択し、スムーズにアニメーション化します。前の例に戻ると、ユーザーがカルーセルのスクロールを終了すると、表示画像が所定の位置に固定されます。JavaScript によるスクロール調整は必要ありません。

CSS のスクロール スナップと画像カルーセルの使用例。
画像カルーセルとともに CSS のスクロール スナップを使用する例。 このスクロールのスナップにより、スクロールの終了時に、画像の水平方向の中央がスクロール コンテナの水平方向の中央に揃えられます。

CSS スクロール スナップ

スクロールのスナップとは、スクロール操作の完了後に、スクロール コンテナのスクロール オフセットを望ましいスナップ位置に調整することです。

スクロール コンテナは、scroll-snap-type プロパティを使用してスクロールのスナップを有効にできます。これにより、このスクロール コンテナを子孫によって生成されたスナップ位置にスナップする必要があることをブラウザに伝えます。scroll-snap-type により、スクロールが発生する軸(xy、または both)とスナップの厳格性(mandatoryproximity)が決まります。詳細については後述します。

スナップ位置は、要素上の必要な配置を宣言することで生成できます。この位置は、最も近い祖先スクロール コンテナと要素が指定された軸の指定に従って配置されるスクロール オフセットです。各軸で、startendcenter の配置が可能です。

start の配置は、スクロール コンテナのスナップポートの開始端が要素のスナップ領域の始端とフラッシュする必要があることを意味します。同様に、endcenter の配置では、スクロール コンテナのスナップポートの終端または中央を、要素のスナップ領域の終端または中央とフラッシュする必要があります。

横方向のスクロール軸上のさまざまな配置の例。

次の例は、これらのコンセプトの使用方法を示しています。

スクロールのスナップの一般的なユースケースとして、画像カルーセルがあります。たとえば、スクロールすると各画像にスナップする横方向の画像カルーセルを作成するには、横軸に必須の scroll-snap-type を持つスクロール コンテナを指定できます。各画像を scroll-snap-align: center に設定すると、カルーセル内で画像が中央に配置されます。

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

スナップ位置は要素に関連付けられているため、要素とスクロール コンテナのサイズを考慮して、スナップのタイミングと方法を適切に調整できます。たとえば、1 つの画像がカルーセルよりも大きい場合について考えてみましょう。単純なスナップ アルゴリズムでは、ユーザーが画像全体を表示するためにパンニングできない場合があります。ただし、仕様では、このようなケースを検出して、端でスナップするだけでユーザーが画像内を自由にスクロールできるようにする必要があります。

デモを見る | ソース

例: ジャーニー プロダクト ページ

スクロール スナップでメリットが得られるもう 1 つの一般的なケースは、一般的な商品ページなど、垂直方向にスクロールする複数の論理セクションを持つページです。このようなケースには、scroll-snap-type: y proximity; のほうが適しています。ユーザーが特定のセクションの中央までスクロールしても妨げにはなりませんが、十分に近くまでスクロールするとスナップして新しいセクションに注意を向けます。

方法は次のとおりです。

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

スクロールのパディングと余白

商品ページの上ヘッダーが固定されています。また、スクロール コンテナがスナップされたときに上部セクションの一部を表示したままにして、上記のコンテンツに関するデザイン上の手がかりをユーザーに提供することも求められました。

scroll-padding プロパティは、スクロール コンテナ(スナップポート)の有効な視認可能領域の調整に使用できる新しい CSS プロパティです。スナップポートは、スクロール スナップの配置を計算するときに使用されます。このプロパティは、スクロール コンテナのパディング ボックスに対するインセットを定義します。この例では、15vh 追加のインセットが上部に追加されました。これにより、スクロール スナップの垂直方向の始端としてスクロール コンテナの上端より低い位置(15vh の下)を考慮するようブラウザに指示します。スナップすると、スナップ ターゲット要素の始端がこの新しい位置でフラッシュされ、上にスペースが残ります。

scroll-margin プロパティは、スナップ スクロール コンテナで scroll-padding がどのように機能するかと同様に、スナップ ターゲットの有効ボックスの調整に使用されるアウトセットの量を定義します。

お気づきのように、この 2 つのプロパティには「snap」という単語がありません。これは、単なるスクロールのスナップではなく、関連するすべてのスクロール操作で実際にボックスを変更するため、意図的なものです。たとえば、Chrome では、PageDown や PageUp などのページング スクロール操作のページサイズの計算や、Element.scrollIntoView() 操作のスクロール量の計算でこれらの要素が考慮されます。

デモを見る | ソース

他のスクロール API との連携

DOM スクロール API

スクロールのスナップは、スクリプトによって開始されたものを含むすべてのスクロール操作の後に行われます。Element.scrollTo などの API を使用している場合、ブラウザはオペレーションの目的のスクロール位置を計算し、適切なスナップ ロジックを適用して、最終的なスナップ位置を検出します。したがって、スナップの計算を手動で行うユーザー スクリプトは必要ありません。

スムーズ スクロール

スムーズ スクロールはプログラムによるスクロール操作の動作を制御し、スクロール スナップは移動先を決定します。これらはスクロールの直交する要素を制御するため、併用して互いに補完し合うことができます。

オーバースクロールの動作

オーバースクロール動作 API は、スクロールを複数の要素にまたがってチェーンする方法を制御します。スクロール スナップの影響を受けることはありません。

注意点とベスト プラクティス

ターゲット要素の間隔が広い場合は、必須スナップを使用しないでください。これにより、スナップ位置間のコンテンツにアクセスできなくなる可能性があります。

多くの場合、機能検出なしで、スクロール スナップを拡張機能として追加できます。必要に応じて、@supports または CSS.supports を使用して、CSS のスクロール スナップのサポートを検出します。サポートが終了した仕様にも存在する scroll-snap-type は使用しないでください。

CSS での特徴検出

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

JavaScript での特徴検出

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

プログラムによってスクロールする API(Element.scrollTo など)が常に、リクエストされたスクロール オフセットで終了するとは想定しないでください。プログラムによるスクロールが完了した後に、スクロールのスナップによりスクロール オフセットが調整される場合があります。他の理由でスクロールが中断されている可能性があるため、これはスクロール スナップの前でも適切な前提ではありませんでしたが、特にスクロール スナップの場合に該当します。

今後の作業

Chrome チームが最近実施したアンケートで重視されたのは、スクロール エクスペリエンスです。調査の結果、プラグイン ライブラリと CSS の差を縮めるために追加の作業が必要な分野がいくつか特定されました。これから提出する課題では、scroll-snap に焦点を絞って次のようなテーマがあります。

  1. API の可用性とブラウザ間の互換性。
  2. scroll-start などの新しい CSS API で作業を行います。
  3. 新しい JS イベントsnapChanged() など)を処理する。