フーノページ



CSSのスナップスクロール

CSS、決まった位置にスナップさせるスクロール。








syntax。

parent {
    scroll-snap-type:x/y mandatory/proximity;
    }

child {
    scroll-snap-align:start/center/end;
    }




スクロールスナップ。


こんにちは、「ふ」です。

⬆︎は子要素をスクロールできるようになっている、スクロールコンテナです。ユーザーがスクロール操作を行うと、それに合わせて子要素が流れていきます。

いっぽう⬆︎は、スクロールさせると各子要素が親コンテナの中央にピッタリと停止しています。
これは「スクロールスナップ」と呼ばれるものです。

このスクロールスナップを効かせれば「コンテナ内に子要素をピッタリ合わせて閲覧したい」というユーザにも優しい仕組みとなります。

今回はこのスクロールスナップの実装について学んでいきましょう。





scroll-snap-type、
scroll-snap-align。


スクロールスナップを実装するには、scroll-snap-typeとscroll-snap-alignという、2つのプロパティが必要になってきます。

parent {
    scroll-snap-type:x/y mandatory/proximity;
    (初期値:none)
}

scroll-snap-typeは、親要素に指定するプロパティです。
値は2つ指定することができ、x/yでスナップさせるスクロールの方向、mandatory/proximityでスナップの強制度合いを指定します。

・mandatoryは「強制」という意味で、文字通り強制的にスナップがはたらきます。
・proximityは「近接」の意。子要素がスナップ位置に近いところにある場合に、スナップが発動します。

child {
    scroll-snap-align:start/center/end;
    (初期値:none)
}


scroll-snap-alignは子要素に指定するプロパティです。親子要素のどの部分をスナップさせるかを指定します。

scroll-snap-type、scroll-snap-alignはともに初期値が「none」となっており、両方をそれ以外の値に指定してやる必要があります。


スクロールコンテナが前提。

またスクロールスナップを実装するには、「スクロールコンテナ」が構成されていることが前提となります。

・スクロール方向に対し、親要素のwidthまたはheightが明示されている。
・子要素のはみ出し部分がスクロールによって表示される仕組みになっている。

このスクロールスコンテナが構築されていないと、これら2つのプロパティを指定するだけではスクロールスナップは実現しません。




実装してみる。


まづはスクロールコンテナを作成し、子要素を配置しましょう。

<div class = "area">
    <img src = "elm1.svg"> 
    <img src = "elm2.svg"> 
    <img src = "elm3.svg">
    <img src = "elm4.svg">
    <img src = "elm5.svg">
</div>

親コンテナ「.area」の内部に5つの子要素を並べました。

.area {
    width:50%;
    aspect-ratio:1/1;「「1 ..@1@」」
    overflow:auto;「「1 ..@2@」」
    }

@1@ スクロールコンテナの条件1、スクロール方向の大きさの明示。子要素は縦方向に並べているので、heightを明確に指定する必要があります。
ただここではwidthを指定→それを元にaspect-ratio(縦横比)のプロパティで、heightを確定させています。こうした明示方法でもOKです。

@2@ もう1つの条件である、はみ出し部分の処理。
ここではoverflow:autoで指定しています。とりわけ縦方向なので「overflow-y:scroll」としても構いません。



現在の状態⬆︎です。y方向のスクロールコンテナが構築されました。
いまはまだ、普通のスクロールで子要素が推移しています。ここからスナップを仕込んでいきましょう。

.area {
    width:50%;
    aspect-ratio:1/1;
    overflow:auto;
    scroll-snap-type:y mandatory「「1 ..@3@」」
    }

@3@ 親要素に対しscroll-snap-typeでスナップの方向と強制度合いを指定します。
縦方向なので「y」、強制度を「mandatory(強制的)」にしました。2つの値は半角スペースで区切って記述してください。


area img {
    scroll-snap-align:center;「「1 ..@4@」」
}

@4@ そして子要素に対し、scroll-snap-alignを使ってスナップの基準を指定します。
ここでは「center」としました。



結果⬆︎。
スクロールさせると、必ずいずれかの子要素が中央にピタッと停止するようになりました。




スナップの強制度を変化させる。


先ほどはスナップの強制度を「mandatory(強制)」としていました。
もう1つの「proximity(近接)」だとどのような挙動になるのでしょうか。

<div class = "area2">
    <img src = "elm1.svg"> 
    <img src = "elm2.svg"> 
    <img src = "elm3.svg">
    <img src = "elm4.svg">
    <img src = "elm5.svg">
</div>
.area2 {
        「「1display:flex;」」
        width:50%;
        aspect-ratio:1/1;
        overflow:auto;
    } 


せっかくなので、今度は横スクロールで試してみます。

.area2 {
    width:50%;
    aspect-ratio:1/1;
    overflow:auto;
    scroll-snap-type:x proximity「「1 ..@5@」」
    }
    
    
area2 img {
    scroll-snap-align:center;
}

@5@ スクロールスナップの指定をしていきます。前のサンプルとは違い、今度は水平スクロールなので方向を「x」に指定します。
そして強制度合いを「proximity(近接)」としました。



結果。
必ずしも各子要素は、親コンテナの中央に揃うとは限りません。ユーザは中間位置で停止させることもできます。
ただそれらしい位置にきたときには、中央位置に修正されるようになりました。

「mandatory」に比べ、こちらのほうが直感的な印象を受けます。



スナップ位置の変更。


こんどはscroll-snap-alignをいじって、スナップ位置を変更してみます。
scroll-snap-alignのには次の3つのものがあります。

・start : 親子要素の先頭を合わせる
・center : 親子要素の中央を合わせる
・end : 親子要素の末端を合わせる

〜実際に試していきます。

<div class = "area3">
    <img src = "elm1.svg"> 
    <img src = "elm2.svg"> 
    <img src = "elm3.svg">
    <img src = "elm4.svg">
    <img src = "elm5.svg">
</div>
.area3 {
        display:flex;
        width:50%;
        aspect-ratio:「「11.5/1」」;
        overflow:auto;
    } 

今回はスナップ位置が観察しやすいよう、親boxを横長にしました。

.area3 {
    width:50%;
    aspect-ratio:1/1;
    overflow:auto;
    scroll-snap-type:x mandatoru
    }
    
area3 img {
    scroll-snap-align:「「1start」」;
}

scroll-snap-alignを「start」にしてみます。

スクロール方向に対し、親コンテナと子要素の先頭部分がスナップします。

area3 img {
    scroll-snap-align:「「1center」」;
}

つぎは「center」。
親子要素の中央がスナップしています。

area3 img {
    scroll-snap-align:「「1end」」;
}

「end」にすると、末端部分がスナップするようになりました。

最初or最後はスナップしない。

3つのscroll-snap-alignを試してきました。実際にサンプルをスクロールさせると、最初または最後の子要素については必ずしも指定値どおりにスナップしていないことがわかります。

原因は、親要素の内部にまったく余白が設けられていない点です。
たとえば水平スクロールにおいてscroll-snap-align:endとしたとき、先頭の子要素は親コンテナの左いっぱいに位置しているため(上)、親要素の右端にスナップさせることができません。

先頭の子要素を右端にスナップさせたいのであれば、親コンテナの中に余白を設けてやる必要がでてきます(下)。

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

.area3 img:first-of-type {
        「「1margin-left:50%」」;
    }

scroll-snap-alignを「end」としているとき、1つ目の子要素にmargin-leftを仕込みます。

先頭要素も末端がスナップするようになりました。
ただし、先頭要素の左には空白ができてしまいます。そのときのwebデザインの方向性によって、採用を検討したほうがいいかもですね。



レイアウトの工夫も必要。


今回はCSSのスクロールスナップの実装について解説してきました。しかしながら実際のwebデザイン上で「ユーザにこんなふうに見せたい」という目的を実現するためには、レイアウトにも工夫が必要です。先ほどの「先頭要素もスナップさせたい」問題に挙げられるように。

今回のサンプルでは親要素に対し子要素の画像を領域いっぱいに詰め込んでいましたが、もし余白をつけて画像を表示したいとき。

<div class = "area_n">
    <div class = "space">
        <img src = "....">
    </div> 
</div>

親要素の直下にブロック要素を設け、その中に画像を入れ子にする→直下要素の「.space」に対して余白を指定する〜などの工夫が必要です。

スクロールスナップ+αのアレンジを施して、ユーザに優しいインターフェースを目指しましょう。

最後までお読みくださり、ありがとうございました。
ではまた〜 🎵




「ふ」です。

ふ

ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。