1. はじめに
この節は規範的ではありません。
スクロール可能なコンテンツで一般的な UX パラダイムでは、コンテンツをページ単位で移動したり、 論理的な区分に分割したりすることがよくあります。 これは特にタッチ操作で当てはまります。 ユーザーにとって、タップナビゲーションで階層構造を掘り下げるよりも、 平面的に配置された広範なコンテンツをすばやくパンする方が迅速かつ容易だからです。 たとえば、アルバム内の個々の写真をタップするよりも、 写真のスライドショー表示をパンすることで、 ユーザーはフォトアルバム内の多くの写真をより簡単に見ることができます。
しかし、タッチによるパンやマウスホイールによるスクロールのような スクロール入力には不正確な性質があるため、 Web 開発者が十分に制御されたスクロール体験を保証することは困難であり、 特にコンテンツをページ単位で移動する効果を作成することは困難です。 たとえば、パンしたときに項目の一部だけが画面内に残るような 不自然なスクロール位置でユーザーが停止してしまうことは容易に起こります。
このため、このモジュールではスクロールスナップ位置を導入します。これは、スクロール操作の完了後にスクロールコンテナーのスクロールポートが終了できる スクロール位置を強制するものです。
また、スナップが無効な場合でも、 ページングおよびスクロール位置をより適切に制御できるようにするため、 このモジュールでは、すべてのスクロールコンテナーで使用するための scroll-padding プロパティを定義し、 ページングおよびスクロールして表示する操作のために、スクロールコンテナーの最適表示領域を 調整できるようにします。 同様に、scroll-margin プロパティは任意のボックスで使用でき、 スクロールして表示する操作のために その視覚領域を調整できます。
1.1. モジュール間の相互作用
このモジュールは、[CSS2] の 11.1 節で定義されているスクロールユーザーインターフェイス機能を拡張します。
このモジュール内のいずれのプロパティも、::first-line および ::first-letter 疑似要素には適用されません。
1.2. 値の定義
この仕様は、[CSS2] の CSS プロパティ 定義規約に従い、[CSS-VALUES-3] の値定義構文を使用します。 この仕様で定義されていない値型は、CSS Values & Units [CSS-VALUES-3] で定義されています。 他の CSS モジュールと組み合わせることで、これらの値型の定義が拡張される場合があります。
各定義に記載されているプロパティ固有の値に加えて、 この仕様で定義されるすべてのプロパティは、 プロパティ値としてCSS 全体キーワードも受け入れます。 可読性のため、それらは明示的には繰り返し記載されていません。
2. 動機付けとなる例
img{ /* 各写真の中央が、スナップ時に X 軸上でスクロールコンテナーの中央と 揃うよう指定します */ scroll-snap-align: none center; } .photoGallery{ width : 500 px ; overflow-x : auto; overflow-y : hidden; white-space : nowrap; /* スクロール操作の完了時に、スクロール位置が 常にスナップ位置にあることを 要求します。 */ scroll-snap-type: x mandatory; }
< div class = "photoGallery" > < img src = "img1.jpg" > < img src = "img2.jpg" > < img src = "img3.jpg" > < img src = "img4.jpg" > < img src = "img5.jpg" > </ div >
.page{ /* 各ページの上端を、スナップに使用する 端として定義します */ scroll-snap-align: start none; } .docScroller{ width : 500 px ; overflow-x : hidden; overflow-y : auto; /* 各要素のスナップ領域を上端から 100px オフセットした位置に揃えるよう指定します。 */ scroll-padding:100 px 0 0 ; /* スナップ位置の近くにある場合、操作の完了時に スクロールがスナップ位置で終了するよう促します */ scroll-snap-type: y proximity; }
< div class = "docScroller" > < div class = "page" > ページ 1</ div > < div class = "page" > ページ 2</ div > < div class = "page" > ページ 3</ div > < div class = "page" > ページ 4</ div > </ div >
3. スクロールスナップモデル
このモジュールでは、スクロールスナップ
位置を制御する仕組みを定義します。
これは、スクロールコンテナー内のコンテンツを
特定の配置にするスクロール位置です。
関連するスクロールコンテナーにscroll-snap-type
プロパティを使用することで、
作者は、スクロール操作後にスクロールポートが
スナップ位置に到達するよう、特定の傾向を要求できます
(scrollTo()
メソッドなどのプログラムによるスクロールを含みます)。
スナップ位置は、 要素のスクロールスナップ 領域(scroll-margin によって変更された境界バウンディングボックス)を、 スクロールコンテナーのスナップポート(scroll-padding によって縮小されたスクロールポート)内で、 特定の配置(scroll-snap-align)にするものとして指定されます。 これは概念的には、 配置 コンテナー内で配置対象の配置を指定することと同等です。 指定された配置を満たすスクロール位置が スナップ位置です。
スクロールコンテナーのスクロールポートの スクロール位置を、スナップ位置に揃うよう 調整する行為をスナップと呼び、 スクロールコンテナーのスクロールポートの スクロール位置がスナップ位置であり、 アクティブなスクロール操作がない場合、そのスクロールコンテナーはそのスナップ位置にスナップしているといいます。 CSS Scroll Snap Module は、スナップ 位置を強制するために使用される正確なアニメーションや物理挙動を 意図的に指定も義務付けもしていません。 これはユーザーエージェントに委ねられます。
スナップ位置は、 要素の包含ブロックチェーン上で最も近い祖先スクロール コンテナーにのみ影響します。
4. スクロールスナップ領域の捕捉: スクロールコンテナーのプロパティ
4.1. スクロールスナップ規則: scroll-snap-type プロパティ
| 名前: | scroll-snap-type |
|---|---|
| 値: | none | [ x | y | block | inline | both ] [ mandatory | proximity ]? |
| 初期値: | none |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 指定されたキーワード |
| 正規順序: | 文法に従う |
| アニメーション型: | 離散 |
scroll-snap-type プロパティは、 スクロールコンテナーがスクロールスナップコンテナーであるかどうか、 どの程度厳密にスナップするか、 およびどの軸を考慮するかを指定します。 厳密性の値が指定されていない場合、proximity と見なされます。
html {
scroll-snap-type: block; /* メイン文書スクローラーに適用 */
}
h1, h2, h3, h4, h5, h6 {
scroll-snap-align: start; /* ビューポートの先頭(上端)にスナップ */
}
UA は、ルート要素に設定されたscroll-snap-type の値を
文書ビューポートに適用しなければなりません。
overflow とは異なり、scroll-snap-type の値は HTML
body
から伝播しないことに注意してください。
4.1.1. スクロールスナップ軸: x、y、block、inline、および both 値
軸値は、どの軸がスナップ位置の影響を受けるか、 またスナップ位置が軸ごとに独立して 評価されるか、 2D 点としてまとめて評価されるかを指定します。 値は次のように定義されます:
- x
- スクロールコンテナーは、水平方向の軸でのみスナップ位置にスナップします。
- y
- スクロールコンテナーは、垂直方向の軸でのみスナップ位置にスナップします。
- block
- スクロールコンテナーは、ブロック軸でのみスナップ位置にスナップします。
- inline
- スクロールコンテナーは、インライン軸でのみスナップ位置にスナップします。
- both
- スクロールコンテナーは、両方の軸で独立してスナップ位置にスナップします (各軸で異なる要素にスナップする可能性があります)。
4.1.2. スクロールスナップの厳密性: none、proximity、および mandatory 値
厳密性の 値(none、proximity、mandatory)は、 スクロール コンテナーに対して、スナップ位置をどの程度厳密に強制するか (スクロール位置の調整を強制することで)を指定します。 値は次のように定義されます:
- none
- スクロールコンテナーに指定された場合、 そのスクロールコンテナーはスナップしてはなりません。
- mandatory
- スクロールコンテナーに指定された場合、 アクティブなスクロール操作がないとき、 そのスクロールコンテナーはスナップ位置にスナップしている必要があります。 有効なスナップ 位置が存在する場合、 スクロールコンテナーはスクロールの終了時にスナップしなければなりません (存在しない場合、スナップは発生しません)。
- proximity
- スクロールコンテナーに指定された場合、 そのスクロールコンテナーは、スクロールのパラメーターに基づく UA の裁量により、 スクロールの終了時にスナップ位置へスナップしてもかまいません。
作者は、さまざまなサイズの画面および(該当する場合)さまざまなサイズのコンテンツを 考慮して必須スナップ位置を使用するべきです。 特に、スクロールポートより大きいスナップされた要素へのアクセスは UA によって処理されますが、 作者が隣接していない兄弟要素に必須スナップを割り当てた場合、 その間のコンテンツが画面より長い場合には アクセス不能になることがあります。
ボックスがスクロール コンテナーであるか、または scroll-snap-type に none 以外の値を持つ場合、 そのボックスはスナップ 位置を捕捉するといいます。 ボックスの包含ブロックチェーン上で最も近いスナップ位置を捕捉する祖先が、 scroll-snap-type に none 以外の値を持つスクロールコンテナーである場合、 それがそのボックスのスクロールスナップコンテナーです。 それ以外の場合、そのボックスにはスクロールスナップコンテナーがなく、 そのスナップ 位置はスナップを発生させません。
4.1.3. レイアウト変更 後の再スナップ
文書のコンテンツまたはレイアウトが変更され (たとえば、コンテンツが追加、移動、削除、サイズ変更され)、 スナップポートのコンテンツが変化した場合、 UA は結果として生じるスクロール位置を再評価し、 必要に応じて再スナップしなければなりません。 コンテンツ変更前にスクロールコンテナーがスナップしていた場合で、 同じスナップ 位置が引き続き存在する場合 (たとえば、関連付けられた要素が削除されていない場合)、 スクロールコンテナーはコンテンツ変更後に 同じスナップ位置へ再スナップしなければなりません。 変更前に複数のボックスがスナップしており、 それらのスナップ位置が一致しなくなった場合、 そのうちの 1 つがフォーカスまたはターゲットされていれば、 スクロールコンテナーはそのボックスに再スナップしなければならず、 それ以外の場合、どのボックスに再スナップするかは UA によって定義されます。 (たとえば UA は、レイアウトの変化によって他の要素の スナップ位置が整列したり整列しなくなったりする際に、 どの要素がスナップされているかを追跡してもかまいません。)
新しい、または異なるボックスへの再スナップ操作に必要なスクロールは、 他のスクロールして表示する操作と同じ方法で 動作およびアニメーションしなければならず、 scroll-behavior のような制御も尊重しなければなりません。 ただし、以前と同じボックスへの再スナップ時のスクロール動作は UA によって定義されます。 たとえば UA は、 節の先頭にスナップしているとき、 文書内のそれより前の位置にコンテンツが動的に追加された場合に、 スクロールしていないように見せるため、 節の新しい位置までスクロールするアニメーションを行わないことを選択してもかまいません。
.log{ scroll-snap-type : proximity; align-content : end; } .log::after{ display : block; content : "" ; scroll-snap-align : end; }
これらの規則は、::after 疑似要素によって表される単一のスクロールスナップ領域を作成し、 スクロールスナップコンテナーの最下部に配置します。 ユーザーが下端の「近く」までスクロールすると、 コンテナーはそこにスナップします。 コンテナーにさらにコンテンツが動的に追加された場合も、 そこにスナップしたままとなります (変更後も同じスクロールスナップ領域が存在する場合、 スクロールコンテナーは 同じスクロールスナップ領域に再スナップする必要があるためです)。 ただし、ユーザーがログ内の別の位置までスクロールしている場合、 何も行いません。
4.2. スクロールスナップポート: scroll-padding プロパティ
| 名前: | scroll-padding |
|---|---|
| 値: | [ auto | <length-percentage> ]{1,4} |
| 初期値: | auto |
| 適用対象: | スクロールコンテナー |
| 継承: | しない |
| パーセンテージ: | スクロールコンテナーのスクロールポートの対応する寸法に対する相対値 |
| 算出値: | 各辺について、キーワード auto または 算出された <length-percentage> 値 |
| アニメーション型: | 算出値の型による |
| 正規順序: | 文法に従う |
このプロパティは、 (スクロールスナップコンテナーだけでなく、 すべてのスクロールコンテナーについて) スクロールポートの最適表示領域を定義するオフセットを指定します。 これは、ユーザーの視界に物を配置する際のターゲット領域として使用される領域です。 これにより作者は、他のコンテンツによって覆われている スクロールポートの領域 (固定位置のツールバーやサイドバーなど)を除外したり、 単にターゲット要素とスクロールポートの端との間に より多くの余白を設けたりできます。
scroll-padding プロパティは、 1 つの宣言ですべてのscroll-padding-* ロングハンドを設定するショートハンド プロパティであり、 padding プロパティがそのロングハンドに対して行うのとまったく同じ方法で、 各辺を表すロングハンドに値を割り当てます。 値の意味は次のとおりです:
- <length-percentage>
-
スクロールポートの対応する辺から内側へのオフセットを定義します。 ルートビューポートに適用される場合、 このオフセットは視覚ビューポートではなくレイアウトビューポートに対して計算および適用され、 固定配置ボックスに対する対応するインセットプロパティと同じ方法で処理されます。 最適表示領域は、視覚ビューポートと交差する 残りの領域です。
- auto
-
スクロールポートの対応する辺のオフセットが UA によって決定されることを示します。 これは通常、使用値として 0px を既定値とするべきですが、 UA は、ゼロ以外の値の方がより適切な場合を検出するためにヒューリスティックを使用してもかまいません。
これらのオフセットは、スクロール操作において 「表示可能」と見なされるスクロールポートの領域を縮小します。 これらはレイアウト、 スクロール原点または初期位置、 あるいは要素が実際に可視であると見なされるかどうかには影響しませんが、 要素またはキャレットがスクロールして表示されたと見なされるかどうか (たとえば、ターゲット指定またはフォーカス操作の場合)には影響するべきであり、 ページング操作 (PgUp および PgDn キーを使用したり、 スクロールバーから同等の操作を発生させたりする場合など)でのスクロール量を減らし、 スクロールポートの最適表示領域内で、 ユーザーが連続したコンテンツの流れを確認できるようにするべきです。
スクロールスナップ
コンテナーの場合、この領域は
スクロールスナップポートも定義します。—
html{ overflow-x : auto; overflow-y : hidden; scroll-snap-type : x mandatory; scroll-padding : 0 500 px 0 0 ; } .toolbar{ position : fixed; height : 100 % ; width : 500 px ; right : 0 ; } img{ scroll-snap-align : none center; }
UA は、ルート要素に設定されたscroll-padding の値を
文書ビューポートに適用しなければなりません。
(overflow とは異なり、scroll-padding の値は HTML
body
から伝播しないことに注意してください。)
5. スクロールスナップ領域の整列: 要素のプロパティ
5.1. スクロールスナップ領域: scroll-margin プロパティ
| 名前: | scroll-margin |
|---|---|
| 値: | <length>{1,4} |
| 初期値: | 0 |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 各辺について絶対長 |
| 正規順序: | 文法に従う |
| アニメーション型: | 算出値の型による |
このプロパティは、1 つの宣言ですべての scroll-margin-* ロングハンドを設定するショートハンドプロパティであり、 margin プロパティが そのロングハンドに対して行うのとまったく同じ方法で、 各辺を表すロングハンドに値を割り当てます。
値は、このボックスをスナップポートへスナップする際に使用されるスクロールスナップ領域を定義する外側への拡張量を表します。 スクロールスナップ領域は、 変形後の境界ボックスを取得し、 その矩形バウンディングボックス (スクロールコンテナーの座標空間内で軸に揃えたもの)を求め、 そこに指定された外側への拡張量を加えることで決定されます。
注: これにより、スクロールスナップ領域は常に矩形となり、 スクロール コンテナーの座標空間の軸に揃います。
ターゲット要素を定義するフラグメントへページがナビゲートされた場合
(:target に一致する要素、
または scrollIntoView()
のターゲット)、
UA は、表示するスクロール可能なオーバーフロー領域のどの部分を表示するかを決定する際に、
単なる要素の境界ボックスではなく、その要素のスクロールスナップ領域を使用するべきです。スナップが無効である場合、
またはこの要素にスナップが適用されていない場合でも同様です。
5.2. スクロールスナップの整列: scroll-snap-align プロパティ
| 名前: | scroll-snap-align |
|---|---|
| 値: | [ none | start | end | center ]{1,2} |
| 初期値: | none |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 2 つのキーワード |
| 正規順序: | 文法に従う |
| アニメーション型: | 離散 |
scroll-snap-align プロパティは、 ボックスのスナップ 位置を、そのスナップ領域(配置対象として)を そのスナップ コンテナーのスナップポート(配置コンテナーとして)内に配置することとして指定します。 2 つの値は、それぞれ ブロック軸およびインライン軸での スナップ配置を指定し、 スナップコンテナーの書字モードによって決定されます。 値が 1 つだけ指定された場合、2 番目の値は同じ値を既定値とします。
値は次のように定義されます:
- none
- このボックスは、指定された軸にスナップ位置を定義しません。
- start
- スクロール コンテナーのスナップポート内で、このボックスのスクロールスナップ領域を先頭に揃えた配置が、 指定された軸におけるスナップ位置です。
- end
- スクロール コンテナーのスナップポート内で、このボックスのスクロールスナップ領域を末尾に揃えた配置が、 指定された軸におけるスナップ位置です。
- center
- スクロール コンテナーのスナップポート内で、このボックスのスクロールスナップ領域を中央に揃えた配置が、 指定された軸におけるスナップ位置です。
先頭および末尾の配置は、 スクロールスナップ領域がスナップポートより大きい場合を除き、 スナップコンテナーの書字モードを基準に解決されます。 その場合はボックス自体の書字モードを基準に解決されます。 (これにより、コンテナー内の項目は通常、一貫したスナップ配置を持つことができる一方、 start は常に項目を そのコンテンツを先頭から読めるように配置できます。)
5.2.1. 有効なスナップ位置を可視ボックスに限定する
スクロールスナップの目的は、最適な表示のためにスクロールポート内で コンテンツを整列することなので、 あるスクロール位置へスナップすると、その位置に寄与するスナップ領域がスナップポートの完全に外側に残る場合、 たとえそれ以外の点ではスナップ 領域に要求される配置を満たしていても、有効なスナップ位置とは見なせません。
╔══ビューポート══╗┈┈┈┈┈┈┈┈┌──────────────┐
║ ┌─────┐ ┌──┐ ║ │ 上端スナップ │
║ ├──┐ │ └──┘ ║ │ 要素 │
║ └──┴──┘ ║ │ │
╚════════════════╝ │ │
└──────────────┘
なぜ要素が可視の場合にのみスナップを制限するのか?
WebKit の実装者が 指摘しているように、 スナップ辺をキャンバス全体に無限に拡張すると、 グリッド状のレイアウトに対するスナップしかできず、 画面外の要素が画面内の要素と揃っていない場合、 ユーザーにとって不自然な動作になります。 (ただし、この要件が実装者にとって負担が大きい場合、 既定でグリッド状の動作とし、 より賢い動作を後から有効にする切り替えを導入することもできます。)注: scroll-snap-type: both は スナップ 位置を各軸で独立して評価しますが、ある軸でのスナップ位置の選択は、 もう一方の軸のスナップ位置の影響を受ける場合があります。 たとえば、一方の軸でスナップすると、 他方の軸が揃えようとしていたスナップ領域が画面外に押し出され、 そのスナップ位置が無効となり、 選択できなくなる場合があります。
5.2.2. スクロールポートからオーバーフローするボックスのスナップ
スナップ領域が 特定の軸でスナップポートより 大きい場合、 スナップ領域がスナップポートを覆い、 かつその軸における幾何学的に前後のスナップ位置間の距離が、 その軸におけるスナップポートのサイズより大きい 任意のスクロール位置は、その軸における有効なスナップ位置です。 UA は、特定のスクロール操作(たとえば明示的なページング)に対して、 指定された配置をより正確なターゲットとして使用してもかまいません。
スナップ領域は スナップポートより大きいため、 領域がビューポート全体を完全に満たしている間、 コンテナーは任意にスクロールでき、 配置された位置へ戻るようスナップしようとはしません。 ただし、領域がある軸でビューポートを完全に満たさなくなる位置まで コンテナーがスクロールされた場合、 別のスナップ位置へのスナップを発生させるのに十分な距離まで スクロールされるまで、 領域は外向きのスクロールに抵抗します。
section
要素に必須の上端スナップを設定すると、
(大きな最上位の節から)大きなスナップ領域が生成され、
その中に(下位節からの)より小さなスナップ領域が
含まれる可能性があります。
下位節が十分に小さい場合、
通常どおりスナップします。
長い場合には、
閲覧者はその中を自由にスクロールでき、
またはスナップ先となる下位節のない最上位の節の大きな区間内を自由にスクロールできます。
┌─ 最上位の節 ─────┐ ━┓ │ │ 1┃ │ │ ┃ │ │ ━┩ │ │ ┆ │ │ ┆ │┌──── 下位節 ──────┐│ ╯ ━┓ │└───────────────────┘│ 2┃ │┌──── 下位節 ──────┐│ ━┓ ┃ ││ ││ 3┃ ━┛ │└───────────────────┘│ ┃ │┌──── 下位節 ──────┐│ ━┛ ━┓ │└───────────────────┘│ 4┃ │┌──── 下位節 ──────┐│ ━┓ ┃ ││ ││ 5┃ ━┛ ││ ││ ┃ ││ ││ ━┩ ││ ││ ┆ ││ ││ ┆ ││ ││ ┆ │└───────────────────┘│ ┆ └─────────────────────┘ ╯
注: 作者が代わりに各節の 見出しに必須スナップ位置を設定した場合 (節そのものではなく)、 最初と 5 番目の節のコンテンツの一部は ユーザーからアクセスできなくなります。 見出しのスナップ領域は節全体を覆うまで広がらないためです。 このため、大きく間隔が空く可能性のある要素に 必須スナップ位置を使用するのは適切ではありません。
5.2.3. 到達不能なスナップ位置
スナップ 位置が指定どおりには到達不能であり、 そこへ揃えるにはスクロール コンテナーのビューポートを スクロール可能なオーバーフロー領域の端を越えてスクロールする必要がある場合、 このスナップ領域に対する 使用されるスナップ位置は、 関連する各軸で目的のスナップ位置に向かって 可能な限りスクロールした結果の位置となります。
5.3. スクロールスナップの制限: scroll-snap-stop プロパティ
| 名前: | scroll-snap-stop |
|---|---|
| 値: | normal | always |
| 初期値: | normal |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 指定されたキーワード |
| 正規順序: | 文法に従う |
| アニメーション型: | 離散 |
意図された方向を伴ってスクロールする場合、 スクロールコンテナーは、 スクロール操作の自然な終端に到達し、 最終的なスクロール位置を選択する前に、 複数の可能なスナップ位置を 「通過」することがあります (スクロール操作が同じ方向で、より短い距離を使用した場合には、 それらは有効なスナップ先となります)。 scroll-snap-stop プロパティは、そのような可能なスナップ位置にスクロール操作を「捕捉」させ、 スクロール操作が自然に終了する前に スクロールコンテナーを停止させることができます。
値は次のように定義されます:
- normal
- スクロールコンテナーは、スクロール操作の実行中に、 この要素によって定義されたスナップ位置を通過してもかまいません。
- always
- スクロールコンテナーは、スクロール操作の実行中に、 この要素によって定義されたスナップ位置を通過してはならず、 代わりにこの要素の最初のスナップ 位置にスナップしなければなりません。
このプロパティは、意図された終了位置のみを持つスクロール操作には影響しません。 そのような操作は概念上、いかなるスナップ位置も「通過」しないためです。
6. スナップのメカニズム
スナップ先のスナップ位置を選択する正確なモデルアルゴリズムは、 意図的にほとんど未定義のままとされています。 これにより、ユーザーエージェントはユーザーの意図や操作に関する高度なモデルを考慮し、 時間の経過とともに応答方法を調整して、 ユーザーに最も適切に対応できます。
この節では、スクロールスナップのメカニズムについて議論するのに役立つ概念をいくつか定義し、 効果的なスクロールスナップ戦略がどのようなものになり得るかについて指針を示します。 ユーザーエージェントには、この指針を適応させ、 独自のスナップ動作を定義する際に 自らの最善の判断を適用することが推奨されます。 また、作者がスクロールスナップを念頭に置いてインターフェイスを設計する際に 依存できる最低限妥当な動作を確保するため、 少数の動作要件も示します。
6.1. スクロール方法の種類
ページがスクロールされるとき、 その操作は 意図された終了位置 および/または意図された方向を伴って実行されます。 これら 2 つの組み合わせそれぞれが 異なるスクロールのカテゴリを定義し、 それぞれ多少異なる方法で扱うことができます:
- 意図された終了 位置
-
意図された 終了位置のみを持つスクロールの一般的な例には、次のものがあります:
-
勢いを付けずに離す パンジェスチャー
-
スクロールバーの「つまみ」を明示的に操作すること
-
scrollTo()などの API を介してプログラムからスクロールすること -
文書内のフォーカス可能な要素間を Tab キーで移動すること
-
ページ内のアンカーへ移動すること
-
Home/End キーなどのホーム移動操作
-
- 意図された方向および終了位置
-
意図された方向および終了位置の両方を持つスクロールの 一般的な例には、次のものがあります:
-
慣性を伴うものとして解釈される 「フリング」ジェスチャー
-
scrollBy()などの API を介してプログラムからスクロールすること -
PgUp/PgDn キーなどのページング操作(またはスクロールバー上の同等の 操作)
スナップポイントなどの機能が介入する前の スクロールの意図された終了点を、その自然な終了点と呼びます。
-
- 意図された 方向
-
意図された 方向のみを持つスクロールの一般的な例には、次のものがあります:
-
キーボードの矢印キーを押すこと(またはスクロールバー上の同等の操作)
-
慣性ではなく固定量のスクロールとして解釈されるスワイプジェスチャー
-
さらに、ページレイアウトでは通常、物を垂直方向および/または水平方向に揃えるため、 UA はスクロールの方向が十分に垂直または水平である場合に、 スクロールを軸ロックすることがあります。 軸ロックされたスクロールは、 その軸に沿ってのみスクロールするよう制約されます。 これにより、精度の低い入力機構が主軸ではない方向へずれることを防ぎます。
注: この仕様は、ユーザーエージェントがサポートするスクロール方法にのみ適用されます。 ユーザーエージェントに特定の入力方法またはスクロール方法をサポートすることを要求するものではありません。
6.2. スナップ位置の選択
スクロールコンテナーは、そのスクロール可能なオーバーフロー領域全体に散在する多数のスナップ領域を持つことができます。 スナップ位置を選択する単純なアルゴリズムは、 ユーザーにとって直感的でない動作を生み出す可能性があるため、 選択アルゴリズムを設計する際には注意が必要です。 選択処理に役立ついくつかの指針を以下に示します:
-
スナップ 位置は、この節に記載されている追加の制約に従いつつ、 終了点 (または自然な 終了点)と 最終的にスナップされたスクロール位置との距離を最小化するよう選択するべきです。
-
スクロールが軸ロックされている場合、 もう一方の軸にあるスナップ 位置は、スクロール中は無視するべきです。 (ただし、もう一方の軸にあるスナップ位置は、 最終的なスクロール位置には依然として影響を与える可能性があります。)
-
画面から遠く離れた要素が スクロール位置に理解しにくい影響を与えるのを防ぐため、 スナップ位置の要素が、 スナップポートが スクロール可能なオーバーフロー領域内を移動するときに定義する 「通路」から大きく外れている場合、 または意図された方向のみを持つスクロールの方向にある仮想的な「通路」から大きく外れている場合、 または意図された 終了位置のみを持つスクロール後のスナップポートから大きく外れている場合、 そのスナップ位置は無視するべきです。
-
ユーザーエージェントは、スクロール方法に関係なく、 ユーザーがスナップ位置から 「脱出」できることを保証しなければなりません。 たとえば、スナップ型がmandatoryで、次のスナップ位置が 2 画面幅以上離れている場合、 終了位置が 1 画面幅しか離れていないと、 単純な「常に最も近い位置にスナップする」選択アルゴリズムでは ユーザーを「閉じ込める」可能性があります。 代わりに、終了点が開始スナップ位置から十分に小さい距離にある場合にのみそこへ戻り、 それ以外の場合は開始スナップ位置を無視する より賢いアルゴリズムの方が適切な動作になります。
(これは、意図された方向のみを持つスクロールは、 常に開始時のスナップ 位置を無視しなければならないことを意味します。)
-
ターゲット要素を定義するフラグメントへページがナビゲートされた場合 (たとえば :target に一致する要素、 または
scrollIntoView()のターゲット)、 かつその要素がいくつかのスナップ位置を定義している場合、 最も近いスクロールコンテナーがスクロールスナップコンテナーであるなら、 ユーザーエージェントはその要素のスナップ位置の 1 つに スナップしなければなりません。 ユーザーエージェントは、スクロール コンテナーが scroll-snap-type: none である場合でも、 これを行ってもかまいません。
付録 A: ロングハンド
物理ロングハンドおよび論理ロングハンド(ならびにそれらのショートハンド)は、 [CSS-LOGICAL-1] で定義されているとおりに相互作用します。
scroll-padding の物理ロングハンド
| 名前: | scroll-padding-top, scroll-padding-right, scroll-padding-bottom, scroll-padding-left |
|---|---|
| 値: | auto | <length-percentage> |
| 初期値: | auto |
| 適用対象: | スクロールコンテナー |
| 継承: | しない |
| パーセンテージ: | スクロールコンテナーのスクロールポートに対する相対値 |
| 算出値: | キーワード auto または算出された <length-percentage> 値 |
| 正規順序: | 文法に従う |
| アニメーション型: | 算出値の型による |
scroll-padding のこれらのロングハンドは、 スナップポートの 上端、右端、下端、左端を それぞれ指定します。 負の値は無効です。
scroll-padding のフロー相対ロングハンド
| 名前: | scroll-padding-inline-start, scroll-padding-block-start, scroll-padding-inline-end, scroll-padding-block-end |
|---|---|
| 値: | auto | <length-percentage> |
| 初期値: | auto |
| 適用対象: | スクロールコンテナー |
| 継承: | しない |
| パーセンテージ: | スクロールコンテナーのスクロールポートに対する相対値 |
| 算出値: | キーワード auto または算出された <length-percentage> 値 |
| 正規順序: | 文法に従う |
| アニメーション型: | 算出値の型による |
scroll-padding のこれらのロングハンドは、 スナップポートの block-start、inline-start、block-end、inline-end の各辺を それぞれ指定します。 負の値は無効です。
| 名前: | scroll-padding-block, scroll-padding-inline |
|---|---|
| 値: | [ auto | <length-percentage> ]{1,2} |
| 初期値: | auto |
| 適用対象: | スクロールコンテナー |
| 継承: | しない |
| パーセンテージ: | スクロールコンテナーのスクロールポートに対する相対値 |
| 算出値: | 個々のプロパティを参照 |
| アニメーション型: | 算出値による |
| 正規順序: | 文法に従う |
scroll-padding-block-start + scroll-padding-block-end および scroll-padding-inline-start + scroll-padding-inline-end のこれらのショートハンドは、scroll-padding のロングハンドであり、 スナップポートのブロック軸およびインライン軸の辺をそれぞれ指定します。
2 つの値が指定された場合、最初の値は先頭値を、 2 番目の値は末尾値を指定します。
scroll-margin の物理ロングハンド
| 名前: | scroll-margin-top, scroll-margin-right, scroll-margin-bottom, scroll-margin-left |
|---|---|
| 値: | <length> |
| 初期値: | 0 |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 絶対長 |
| 正規順序: | 文法に従う |
| アニメーション型: | 算出値の型による |
scroll-margin のこれらのロングハンドは、 スクロールスナップ 領域の上端、右端、下端、左端をそれぞれ指定します。
scroll-margin のフロー相対ロングハンド
| 名前: | scroll-margin-block-start, scroll-margin-inline-start, scroll-margin-block-end, scroll-margin-inline-end |
|---|---|
| 値: | <length> |
| 初期値: | 0 |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 絶対長 |
| 正規順序: | 文法に従う |
| アニメーション型: | 算出値の型による |
scroll-margin のこれらのロングハンドは、 スクロールスナップ 領域の block-start、inline-start、block-end、inline-end の各辺をそれぞれ指定します。
| 名前: | scroll-margin-block, scroll-margin-inline |
|---|---|
| 値: | <length>{1,2} |
| 初期値: | 0 |
| 適用対象: | すべての要素 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | 個々のプロパティを参照 |
| アニメーション型: | 算出値の型による |
| 正規順序: | 文法に従う |
scroll-margin-block-start + scroll-margin-block-end および scroll-margin-inline-start + scroll-margin-inline-end のこれらのショートハンドは、scroll-margin のロングハンドであり、 スクロールスナップ 領域のブロック軸およびインライン軸の辺をそれぞれ指定します。
2 つの値が指定された場合、最初の値は先頭値を、 2 番目の値は末尾値を指定します。
7. プライバシーおよびセキュリティに関する考慮事項
この仕様は、DOM によって直接すでに公開されている情報以外の いかなる情報も公開しません。 単にスクロールを少し機能的にするだけです。 新たなプライバシーまたはセキュリティ上の考慮事項はありません。
8. 謝辞
David Baron、 Simon Fraser、 Håkon Wium Lie、 Theresa O’Connor、 François Remy、 Majid Valpour、 そして特に Robert O’Callahan に、 この文書に取り入れられた 提案および推奨事項について深く感謝します。
9. 変更点
9.1. 2019年3月19日 CR 以降の変更点
2019年3月19日 勧告候補以降の変更点には、次のものがあります:
- scroll-snap-align を解決するためにどの書字モードを使用するかを指定しました。 (課題 3815)
-
複数の要素が一致している場合の再スナップ要件を定義しました。
(課題 4651)
コンテンツ変更前にスクロールコンテナーがスナップしていた場合で、 同じスナップ位置が引き続き存在する場合 (たとえば、関連付けられた要素が削除されていない場合)、 スクロールコンテナーはコンテンツ変更後に 同じスナップ位置へ再スナップしなければなりません。 変更前に複数のボックスがスナップしており、 それらのスナップ位置が一致しなくなった場合、 そのうちの 1 つがフォーカスまたはターゲットされていれば、 スクロールコンテナーはそのボックスに再スナップしなければならず、 それ以外の場合、どのボックスに再スナップするかは UA によって定義されます。 (たとえば UA は、レイアウトの変化によって他の要素の スナップ位置が整列したり整列しなくなったりする際に、 どの要素がスナップされているかを追跡してもかまいません。)
-
新しい要素への再スナップを、
他のスクロールして表示する操作と同じ方法で
アニメーションすることを要求しました。
(課題 4609)
新しい、または異なるボックスへの再スナップ操作に必要なスクロールは、 他のスクロールして表示する操作と同じ方法で 動作およびアニメーションしなければならず、 scroll-behavior のような制御も尊重しなければなりません。 ただし、以前と同じボックスへの再スナップ時のスクロール動作は UA によって定義されます。 たとえば UA は、 節の先頭にスナップしているとき、 文書内のそれより前の位置にコンテンツが動的に追加された場合に、 スクロールしていないように見せるため、 節の新しい位置までスクロールするアニメーションを行わないことを選択してもかまいません。
-
scroll-snap-type および scroll-padding の値が、
期待されるとおりルート要素から文書ビューポートへ
伝播されることを明示的に定義しました。
(課題 3740)
UA は、ルート要素に設定されたscroll-snap-type の値を 文書ビューポートに適用しなければなりません。 overflow とは異なり、scroll-snap-type の値は HTML
bodyから伝播しないことに注意してください。UA は、ルート要素に設定されたscroll-padding の値を文書ビューポートに適用しなければなりません。 (overflow とは異なり、scroll-padding の値は HTML
bodyから伝播しないことに注意してください。) -
スナップ配置は
視覚ビューポートに対する相対値ですが、scroll-padding は
レイアウトビューポートに対して解決され、
ルートビューポート上でscroll-padding と inset が一貫することを明確化しました。
(課題 4393)
スクロールポートの対応する辺から内側へのオフセットを定義します。 ルートビューポートに適用される場合、 このオフセットは視覚ビューポートではなくレイアウトビューポートに対して 計算および適用され、 固定配置ボックスに対する対応するインセットプロパティと同じ方法で処理されます。 最適表示領域は、視覚ビューポートと交差する 残りの領域です。
- scroll-padding-inline および scroll-padding-block の「適用対象」の行を修正しました。 (課題 5845)
コメントの処理結果を 参照できます。
9.2. 2019年1月31日 CR 以降の変更点
2019年1月31日 勧告候補以降の変更点には、次のものがあります:
-
スクロールスナップが無効な場合でも、
scroll-padding および scroll-margin が適用されることを強調しました。
(課題 3721)
また、スナップが無効な場合でも、 ページングおよびスクロール位置をより適切に制御できるようにするため、 このモジュールでは、すべてのスクロールコンテナーで使用するためのscroll-padding プロパティを定義し、 ページングおよびスクロールして表示する操作のために、 スクロールコンテナーの最適表示領域を調整できるようにします。 同様に、scroll-margin プロパティは任意のボックスで使用でき、 スクロールして表示する操作のために その視覚領域を調整できます。
このプロパティは、 (スクロールスナップ コンテナーだけでなく、すべてのスクロールコンテナーについて) スクロールポートの最適表示領域を定義するオフセットを指定します…
ターゲット要素を定義するフラグメントへページがナビゲートされた場合 (:target に一致する要素、 または
scrollIntoView()のターゲット)、 UA は、表示するスクロール可能なオーバーフロー領域のどの部分を表示するかを決定する際に、 単なる要素の境界ボックスではなく、その要素のスクロールスナップ領域を使用するべきです 。スナップが無効である場合、 またはこの要素に適用されていない場合でも 。
9.3. 2018年8月14日 CR 以降の変更点
2018年8月14日 勧告候補以降の変更点には、次のものがあります:
- scroll-padding のロングハンドについて、プロパティ定義表に新しい auto キーワードが記載されるよう修正しました。 (課題 3189)
- プロパティ定義表の「算出値」および「アニメーション型」の行を修正しました。
- scroll-margin のプロパティ定義表に紛れ込んでいた <percentage> 値を削除しました。 (3289)
9.4. 2017年12月14日 CR 以降の変更点
2017年12月14日 勧告候補以降の変更点には、次のものがあります:
- 論理ショートハンドの規約に従い、 scroll-snap-align ショートハンドが、 ブロック軸の値を最初に、インライン軸の値を 2 番目に割り当てるよう修正しました。 (課題 2232
- UA のヒューリスティックを考慮するため、初期値として auto キーワードを 'scroll-padding に追加しました。 (課題 2728
-
scroll-snap-type の定義で、
scrollTo()などのプログラムによるスクロールもスナップの対象になることを明確化しました。 (課題 2593)関連するスクロールコンテナーにscroll-snap-type プロパティを使用することで、 作者は、スクロール操作後にスクロールポートが スナップ位置に到達するよう、特定の傾向を要求できます (
scrollTo()メソッドなどのプログラムによるスクロールを含みます) 。 - § 5.2.1
有効なスナップ位置を可視ボックスに限定するの文言を、
旧バージョンと比べて
より明確になるよう調整しました。—
(課題 2526)
コメントの処理結果を 参照できます。
9.5. 2017年8月24日 CR 以降の変更点
2017年8月24日 勧告候補以降の変更点には、次のものがあります:
-
:target/
scrollIntoView()/その他は、 スナップが有効かどうかに関係なく、 scroll-margin を考慮するべきです。 (課題 1ターゲット要素を定義するフラグメントへページがナビゲートされた場合 (:target に一致する要素、 または
scrollIntoView()のターゲット)、 UA は、表示するスクロール可能なオーバーフロー領域のどの部分を 表示するかを決定する際に、 単なる要素の境界ボックスではなく、その要素のスクロールスナップ領域を使用するべきです。 -
:target/
scrollIntoView()/その他は、 スナップが有効な場合、 スナップ位置を使用するべきではなく、使用しなければならないようにしました。 (課題 1ターゲット要素を定義するフラグメントへページがナビゲートされた場合 (:target に一致する要素、 または
scrollIntoView()のターゲット)、 かつその要素がいくつかのスナップ位置を定義している場合、 ユーザーエージェントはするべきですしなければなりません その要素のスナップ位置の 1 つに スナップ 。ただし、その要素に最も近いスクロールコンテナーがスクロールスナップ コンテナーである場合に限ります 。 ユーザーエージェントは、 スクロールコンテナーが scroll-snap-type: none である場合でも、 これを行ってもかまいません。 - スナップ動作に関係なく要素へスクロールする際の余白を提供するという より一般的な役割を反映するため、 scroll-snap-margin の名前を scroll-margin に変更しました。 (課題 4)
コメントの処理結果を 参照できます。
9.6. 2016年10月20日 CR 以降の変更点
2016年10月20日 勧告候補以降の変更点には、次のものがあります:
-
scroll-padding を負でない値に制限しました。
(課題 1084)
値は 負であってはならず、 padding と同様に解釈されます …
- § 6.1 スクロール方法の種類の例に、ページングおよびホーム移動操作を追加しました。 (課題 1605)
-
一方の軸でのスナップが、もう一方の軸で特定のスナップ領域にスナップできるかどうかに
影響を与える可能性があることを明確化しました。
(課題 950)
scroll-snap-type: both は、スナップ位置を各軸で独立して評価しますが、 ある軸でのスナップ 位置の選択は、 もう一方の軸のスナップ位置の影響を受ける場合があります。 たとえば、一方の軸でスナップすると、 他方の軸が揃えようとしていたスナップ領域が画面外に押し出され、 そのスナップ位置が無効となり、 選択できなくなる場合があります。
- scroll-padding および scroll-margin ショートハンドが、 そのロングハンドに値を割り当てる方法を明確化しました。 (課題 1050)
-
スクロールスナップが特定の入力方法を義務付けないことを明確化しました。
(課題 1305)
この仕様は、ユーザーエージェントがサポートするスクロール方法にのみ適用されます。 ユーザーエージェントに特定の入力方法またはスクロール方法をサポートすることを要求するものではありません。
- さまざまなスクロール操作に対するscroll-snap-stop の意図された効果を明確化しました。 (課題 1552)
- scroll-snap-stop は、 スクロールスナップ コンテナー内のすべてのスナップ位置に適用されるのではなく、 要素によって定義されたスナップ 位置に適用されることを明確化しました。
- 例に含まれていたいくつかの構文エラーを修正し、scroll-snap-type の節に新しい例を追加しました。 (課題 827)
コメントの処理結果を 参照できます。