CSS

打倒slick! CSS Scroll Snap入門

はじめに

2, 3ヶ月ほど前、勧告候補である「CSS Scroll Snap Module Level 1」のプロパティ群が、Google Chromeで使用できるようになりました:tada:

いったいどんなことが出来るのか、気になります。そこで、さっそく使ってみました。この記事は、その際のメモです。

用意したものは、最新版のGoogle Chrome(バージョン: 70.0.3538.77)です。

CSS Scroll Snap とは

CSS Scroll Snap は、スクロール可能な領域内のスクロール位置を調整できる機能を提供してくれるようです。

私たちは、Webページから目的の内容を探し出すとき、スクロールをしながら、セクション内の文章を読みます。
そして、目的の内容が見つかると、スクロールを止めます。

その時に「あっ、スクロールしすぎちゃった。最初から読むためにスクロール位置を調整しなきゃ...。」なんてことがよく起こると思います。これは急いでいるときには特に煩わしい作業です。

そこで、そんな作業をしなくても済むように、CSS Scroll Snap を使うことが出来ます。これを使えば、スクロール操作が完了した後に、スクロール位置を指定した場所まで調整してくれるのです。

押さえておくべきプロパティ

  • scroll-snap-typeプロパティ
  • scroll-snap-alignプロパティ
  • scroll-paddingプロパティ
  • scroll-marginプロパティ

CSS Scroll Snapを使うなら、とりあえずこの4つを押さえておけば良いかな、と思います。

scroll-snap-typeプロパティ

scroll-snap-typeプロパティは、

  • スクロール位置の調整を行うかどうか
  • どの軸でスクロール位置を調整するか
  • どの程度厳密に調整するか

を指定します。
このプロパティは、スクロール可能な領域(スクロールコンテナ)に対して設定します。

スクロール位置の調整を行うかどうか

スクロール位置の調整を行わない場合、このプロパティにはnoneを設定します。これが初期値です。

どの軸でスクロール位置を調整するか

  • x: X 軸でスクロール位置の調整を有効にする
  • y: Y 軸でスクロール位置の調整を有効にする
  • block: ブロック軸でスクロール位置の調整を有効にする
  • inline: インライン軸でスクロール位置の調整を有効にする
  • both: X, Y 軸両方でスクロール位置の調整が行われる

どの程度厳密に調整するか

  • mandatory: スクロール位置を調整できる場合、スクロール位置が調整される
  • proximity: スクロール位置を調整でき、調整後のスクロール位置が現在位置から近い場合は、スクロール位置が調整される。そうでない場合、スクロール位置の調整は行われない。

使用例

これらの設定は結構重要なのですが、言葉だけでは伝わりにくい部分があるので、簡単な例を用意しました。
調整の厳密さに関する指定であるmandatoryproximityを並べて、比較できるようにしています。
最新版のGoogle Chromeであれば動作すると思うので、ぜひ実際に試してみてください(動作確認リンク)。

両方ぐりぐり弄っているとわかると思いますが、mandatoryでは絶対にスクロール位置の調整が行われるのに対して、proximityではスクロール位置の調整が行われない位置があります

<div class="container">
  <div class="scroll-container">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=mandatory1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=mandatory2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=mandatory3" alt="">
  </div>

  <div class="scroll-container proximity">
    <img src="http://placehold.jp/703d40/ffffff/450x450.png?text=proximity1" alt="">
    <img src="http://placehold.jp/703d40/ffffff/450x450.png?text=proximity2" alt="">
    <img src="http://placehold.jp/703d40/ffffff/450x450.png?text=proximity3" alt="">
  </div>
</div>
.container {
  display: flex;
}

.scroll-container {
  display: flex;
  flex-basis: 50%;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  max-height: 100vh;
}

.proximity {
  scroll-snap-type: y proximity;
}

img {
  scroll-snap-align: start;
  margin-bottom: 5px;
}

scroll-snap-alignプロパティ

scroll-snap-alignプロパティでは、

  • スクロール位置の調整を行うかどうか
  • スクロールコンテナ内の可視領域(スナップポート)にある要素をどのように配置するか

を指定します。
このプロパティは、スクロール可能な領域の子要素に対して設定します。

スクロール位置の調整を行うかどうか

  • none: noneを指定したコンテンツでは、スクロール位置の調整が行われない。つまり、スクロールが調整される場合、そのコンテンツの隣にあるコンテンツにスクロールされる。動作確認リンクを用意した。これを実行すると、2つ目のimg要素にスクロールが調整されないことがわかる
  • start: スクロールコンテナ内で可視領域にある要素の始端が、スクロールコンテナの始端に位置するように、スクロール位置が調整される
  • end: スクロールコンテナ内の可視領域にある要素の終端が、スクロールコンテナの終端に位置するように、スクロール位置が調整される
  • center: スクロールコンテナ内の可視領域にある要素の中央が、スクロールコンテナの中央に位置するように、スクロール位置が調整される

使用例

こちらも、言葉だけではいまいちわからないと思うので、例を用意しました(動作確認リンク)。
scroll-snap-alignプロパティに、start, end, centerを指定した時の比較をしています。

この例を実行してみるとわかると思いますが、startを指定すると、スクロールコンテナの上端にスクロール位置が調整されるようになります。center, endを指定した場合も、それぞれスクロールコンテナの中央、下端にスクロール位置が調整されます。

<div class="container">
  <div class="scroll-container start">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=start2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start3" alt="">
  </div>

  <div class="scroll-container end">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=end1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=end2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=end3" alt="">
  </div>

  <div class="scroll-container center">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=center2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=center1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=center3" alt="">
  </div>
</div>
.container {
  display: flex;
}

.scroll-container {
  display: flex;
  flex-basis: 33.3%;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  max-height: 100vh;
}

img {
  margin-bottom: 5px;
}

.start img {
  scroll-snap-align: start;
}

.end img {
  scroll-snap-align: end;
}

.center img {
  scroll-snap-align: center;
}

scroll-paddingプロパティ

scroll-paddingプロパティは、

  • スクロールコンテナ内にpaddingを設定するかどうか

を指定できます。

scroll-paddingプロパティについては、例を使いながら説明をします。
たとえば、このような固定ヘッダーがあるページで、CSS Scroll Snapを使用した場合を考えます。

<div class="container">
  <header></header>
  <div class="scroll-container start">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=start2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start3" alt="">
  </div>
</div>
body{
  margin:0;
}

.container {
  display: flex;
}

header {
  position: fixed;
  width: 100vw;
  height: 100px;
  background: rgba(255,0,255,.3);
  top: 0;
  left: 0;
}

.scroll-container {
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  max-height: 100vh;
  scroll-padding-top: 150px;
}

img {
  margin-bottom: 5px;
}

.start img {
  scroll-snap-align: start;
}

従来の方法であれば、padding-topを固定ヘッダーの高さ分設定することで、固定ヘッダーの部分に余白が作られ、コンテンツ全体が表示されるようになります。

しかし、CSS Scroll Snapを使用すると、padding-topを設定していても、スクロール位置を調整する際にpaddingを無視したスクロール位置の調整が行われます

こちらはpadding-topのみを設定したときの例です。一度スクロールすると、スクロール位置の調整が行われ、padding-top分を無視したスクロール位置の調整が行われることがわかると思います。

<div class="container">
  <header></header>
  <div class="scroll-container start">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=start2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start3" alt="">
  </div>
</div>
body {
  margin: 0;
}

.container {
  display: flex;
  max-height: 100vh;
}

header {
  position: fixed;
  width: 100vw;
  height: 100px;
  background: rgba(255, 0, 255, 0.3);
  top: 0;
  left: 0;
}

.scroll-container {
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  max-height: 100vh;
  padding-top: 100px;
}

img {
  margin-bottom: 5px;
}

.start img {
  scroll-snap-align: start;
}

この動作を防ぐために、scroll-paddingプロパティを使うことができます。
scroll-paddingプロパティを使うことで、スクロール位置の調整にもpaddingが考慮されるようになります。

scroll-paddingプロパティを使った時の動作はこのようになります

<div class="container">
  <header></header>
  <div class="scroll-container start">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start1" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/250x250.png?text=start2" alt="">
    <img src="http://placehold.jp/3d4070/ffffff/450x450.png?text=start3" alt="">
  </div>
</div>
body {
  margin: 0;
}

.container {
  display: flex;
  max-height: 100vh;
}

header {
  position: fixed;
  width: 100vw;
  height: 100px;
  background: rgba(255, 0, 255, 0.3);
  top: 0;
  left: 0;
}

.scroll-container {
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  max-height: 100vh;
  padding-top: 100px;
  scroll-padding-top: 100px;
}

img {
  margin-bottom: 5px;
}

.start img {
  scroll-snap-align: start;
}

scroll-marginプロパティ

scroll-marginプロパティは、

  • スクロールコンテナに対するmarginを設定するか

を指定できます。
(※このプロパティの動作については、十分に理解できていないので、この部分は改めて追記する予定です。)

ブラウザの対応状況

Can I use によると、Google Chrome, Safari はCSS Scroll Snapをサポートしているようですが、その他のブラウザでのサポートはまだ十分ではありません。また、現在の段階ではGoogle Chrome, Safari間の実装にも差異がある可能性が残っているため、注意が必要です。

以上。