CSSでスクロールのスナップが可能に!scroll-snapプロパティの基礎知識と便利な使い方

CSSのscroll-snapプロパティを使用すると、スマホのホーム画面のようにちょっとずらすだけで、すぐに次のコンテンツを表示させることができます。今まではJavaScriptの領域でしたが、これからはCSSのみで実装できます。

scroll-snapプロパティの基礎知識と便利な使い方を紹介します。

サイトのキャプチャ

Practical CSS Scroll Snapping


下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

スクロールにスナップさせる「scroll-snapプロパティ」

CSSのscroll-snapプロパティを使用すると、ユーザーがスクロールを終了した後にビューポートを特定の要素または場所にスナップさせることができます。

スナップさせるとは、期待した位置にぴたっと表示させることで、スクリーンいっぱいのコンテンツが並んだページのインタラクションを構築するのに最適です。下記はスマホのホーム画面をイメージした水平スクロールのコンテンツです。もちろん垂直の縦長ページにも利用できます。

See the Pen Horizontal Full-Screen by Max Kohler (@maxakohler) on CodePen.

スマホでもscroll-snapは、動作します。

上記デモをスマホで表示

「scroll-snapプロパティ」のサポートブラウザ

scroll-snapのサポートブラウザは、Chrome、Firefox、Edge、IE、Safariのデスクトップ用ブラウザのすべてでサポートされています。Chromeは先日9/4にリリースされたChrome 69+です。モバイル用のブラウザでは、iOS用のSafaiのみサポートされています。scroll-snapは2016年に導入されて以来、大幅に改善されています。
参考: Introducing CSS Scroll Snap Points

サイトのキャプチャ

scroll-snapのサポートブラウザ

「scroll-snapプロパティ」の基礎知識

scroll-snapは、コンテナの親要素にscroll-snap-typeプロパティを、その内側の子要素にscroll-snap-alignプロパティを定義するのが基本的な使い方です。

たったこれだけで、親要素がスクロールされると、定義した子要素にスナップされます。
コードを見てましょう。

親要素と子要素にscroll-snapを定義します。

ちなみに、仕様の最初のバージョンでは、下記のようにrepeatを使用して手動でスナップポイントを設定していました。

最初のバージョンの方法は、使うシーンがかなり限定されてしまいます。上記の指定だと、300pxの均等に配置されたスナップポイントのみを使用できるため、異なるサイズの要素にスナップさせることはできません。また、スクリーンサイズによって異なる要素にも使用できません。

2018年9月現在、Firefox、Edge、IEはこの最初のバージョンの仕様をサポートしていますが、ChromeとSafariは要素ベースの新しい方法をサポートしています。

両方の指定方法を一緒に使用すると(同サイズの要素にスナップさせる場合)、両タイプのブラウザをサポートできます。

scroll-snapを使用する際の最適な記述方法は、要素ベースの構文を排他的に使用し、要素ベースをサポートしていないブラウザをサポートするためにポリフィルを使用することです。この記事で紹介しているデモはこの方法で実装しています。

残念なことに、このポリフィルはビルドプロセスを使用していない場合は少し使いにくいです。最も簡単な方法は、bundle.runのスクリプトにリンクし、DOMがロードされたらcssScrollSnapPolyfill()を使用して初期化することです。このポリフィルでは要素ベースをサポートします、初期のrepeatベースはサポートしていません。

親要素の「scroll-snapプロパティ」

scroll-snapプロパティは親要素と子要素の両方に適用され、それぞれに固有の値が設定されます。CSS FlexboxのFlexコンテナとFlexアイテムと同じような感じです。scroll-snapの場合、親要素はSnapコンテナになります。

まずは、親要素に使用するプロパティと値を見てましょう。

scroll-snap-type: 「mandatory」と「proximity」

「mandatory」は、ユーザーがスクロールを停止するたびにブラウザがスナップポイントにスナップされます。「proximity」はそれほど厳密ではなく、適切であればスナップポイントにスナップされます。
実際に動作を試してみると、スナップポイントの数100ピクセル以内でスクロールを停止すると、スナップされることが分かります。

より一貫したユーザーエクスペリエンスが必要であることは分かりますが、仕様が指摘しているように、危険でもあります。
例えば、スクロールするコンテナ内の子要素がビューポートよりも大きい場合を想像してみてください。

子要素(オレンジ)がビューポートよりも大きい場合

子要素(オレンジ)がビューポートよりも大きい場合

親要素が「scroll-snap-type: mandatory」の場合、要素の上端または下端のいずれかに常にスナップされてしまい、背が高い要素の中間部分はスクロールできなくなります。

scroll-padding

デフォルトでは、コンテンツはコンテナの端にスナップされます。これは、「scroll-padding」で変更できます。「scroll-padding」は通常のpaddingと同じ構文です。

固定ヘッダのレイアウトなど、コンテンツの途中に入る要素がある場合に便利です。

子要素の「scroll-snapプロパティ」

続いて、子要素に使用するscroll-snapプロパティを見てましょう。

scroll-snap-align

scroll-snap-alignは、要素のどの部分がコンテナにスナップするかを指定できます。値は「start, center, end」の3つがあります。

scroll-snap-alignの値

scroll-snap-alignの値

「start, center, end」の3つの値は、スクロール方向に関連しています。
垂直方向にスクロールする場合は、startは要素の上端を指します。水平方向にスクロールする場合は、左端です。 centerとendも同じ仕様です。使用する時は、スクロール方向ごとに異なる値をスペースで区切って設定できます。

scroll-snap-stop: 「normal」と「always」

scroll-snap-stopはデフォルトでは、ユーザーがスクロールを停止した時に開始されます。つまり、停止する前にいくつかのスナップポイントをスキップできます。これを変更するには、子要素に「scroll-snap-stop: always」を設定します。この記述により、ユーザーがスクロールを続ける前にスクロールコンテナが強制的にその要素で停止します。
2018年9月現在、ブラウザはscroll-snap-stopをネイティブにサポートしていません。

「scroll-snapプロパティ」の便利な使い方

scroll-snapを使用したデモを見てましょう。

垂直方向のリスト

垂直方向の各リスト要素にスナップさせるために必要なCSSは、たった2つです。まずは親要素のコンテナに、縦のy軸に沿ってスナップするように定義します。

あとは、スナップポイントを定義するだけです。各リスト要素の上部にスナップさせるように定義します。

たったこれだけのCSSで、完了です。

See the Pen Vertical List by Max Kohler (@maxakohler) on CodePen.

水平方向のスライダー

水平方向のスライダーを作成するためには、コンテナに横のx軸に沿ってスナップされるように定義します。そして、scroll-paddingを使用して、子要素がコンテナの中央にスナップされるようにします。

あとは、どのポイントにスナップするかをコンテナに伝えるだけです。ギャラリーを中心にするために、各要素の中心点をスナップポイントとして定義します。

水平方向のスライダーも簡単に作成できます。

See the Pen Horizontal, different sized images by Max Kohler (@maxakohler) on CodePen.

垂直方向のフルスクリーンコンテンツ

縦長ページでよく見かけるレイアウトも非常に簡単です。
スナップポイントは、直接<body>要素に定義することができます。

あとは、各セクションをビューポートのサイズいっぱいにし、上端をスナップポイントとして定義するだけです。

非常にシンプルなCSSで、縦長ページも実装できます。

See the Pen Vertical Full-Screen by Max Kohler (@maxakohler) on CodePen.

水平方向のフルスクリーンコンテンツ

水平方向にすると、どうなるでしょう?
これも簡単です。コンセプトは垂直方向と同じで、<body>要素の定義をx軸に変えるだけです。

横方向のレイアウトも簡単です。

See the Pen Horizontal Full-Screen by Max Kohler (@maxakohler) on CodePen.

2Dのグリッド

scroll-snapは、同時に2つの方向で機能します。
ここでも、<body>要素に直接scroll-snap-typeを定義します。2方向なので、値は「both」です。

あとは、各タイルの左上隅をスナップポイントとして定義します。

画像などのギャラリーでよく使用されるレイアウトです。

See the Pen 2d Snapping by Max Kohler (@maxakohler) on CodePen.

スクロールスナップのユーザーエクスペリエンス

スクロールを扱うのは、危険なビジネスです。スクロールはユーザーがWebとやり取りするための基本的な操作であるため、何らかの方法で変更してしまうと、スクロールジャック(Scrolljacking)という言葉があるように不快感を与えることがあります。

ここで紹介した「scroll-snap」の素晴らしい点は、スクロールの位置を制御するものではないということです。単に、ユーザーがスクロールした際にスナップする位置のリストを与えるだけです。つまり、提供するインターフェースはどのプラットフォームであっても、ネイティブのインターフェース(同じアニメーションなどを使用する)のように感じることになります。

「scroll-snap」は特に、スマホでうまく機能するのも大きな利点です。既にスマホでは、このスクロールのスナップがUIの一部で採用されています。iOSとAndroidのホーム画面は、スナップポイント付きの水平スライダーです。

確かにスクロールのスナップを実現させるために、いくつかのJavaScriptがあります。しかし、「scroll-snap」のおかげで簡単にCSSで実装できるようになりました。

もちろん「scroll-snap」をすべてに使用するべきではありません。記事ページなどでは、必要ないでしょう。イメージギャラリーやスライドーショー、そして縦長ページなどは適したコンテンツであると思いますが、それら以外にも潜在的な可能性があるという点で素晴らしい改善ができると思います。

最後に

細心の注意が払われていれば、スクロールのスナップは有用なツールになります。CSSのスナップポイントを使用すると、ブラウザのネイティブのスクロール操作を使用して、インターフェイスがシームレスでスムーズに感じられます。そして、JavaScript APIがあれば、さらに強力になるでしょう。軽くタッチするだけで、簡単に進めることができます。

sponsors

top of page

©2018 coliss