どうも、Reoです。
スマホ等のタッチデバイスでスクロール時に、指定した位置でスクロールをストップさせたい!というのを実装してみたので紹介します。
PureでVanillaな素のjsで実装しました。
今回デモ用に作ったのは以下のものです。
指定したボックスの下端が画面の中心に来たらスクロールが止まります。
スクロールを止める方法
スクロールを止める方法は結構色々試しました。
試した方法は3種類。
- addEventListenerでスクロール中にpreventDefaultをする
- スクロール中に画面をタッチしたことにして止める
- 指定座標についたらscrollToで同じ座標にスクロールさせる
1はtouchmoveを指定してやってみましたが、慣性スクロールは止まらず、慣性スクロールが止まった後のtouchmoveを無効にすることしかできませんでした。
2の擬似タッチはタッチしたことにならず諦め。原理的にはイケそうな気はしてます。
そして今回の方法(3番)は発想の転換、というかそんな感じで偶然発見できた方法です。なにより、一番コード量が少なく簡単にできます!
スクロールを止める部分のコードはこちら。
/**
* スクロールを止める
*/
function stopScroll() {
// 現在のスクロール量
var scrollOffsetY = window.pageYOffset;
window.scrollTo(0,scrollOffsetY);
}
実質たったの2行で実現できちゃいました。
あとは位置指定部分と、スクロール中に呼ばれる部分を実装すればおkです。
要素が指定した範囲内にあるかの判別
要素(item)が指定範囲内にあるかどうかを返す関数を書きます。
function isItemInScreen( item ) {
var screenHeight = window.innerHeight; // 画面の高さ
var itemBottomY = item.getBoundingClientRect().bottom; // item bottom座標
var boundary = screenHeight / 2; // 上からスクロールしてくるとここで止まる 画面topからどれだけか
if ( 0 <= itemBottomY && itemBottomY <= boundary ) {
return true;
}
return false;
}
今回は画面トップ(0)から画面の真ん中までの範囲内に要素があれば、trueが返るものを用意しました。
getBoundingClientRect()で要素のページ内座標が取得できます。
ここはお好みで止めたい位置を指定ください〜。
スクロール中の処理を書く
最後にスクロール中の処理を書きます。window.onscrollを使います。
/**
* Scroll
*/
window.onscroll = scrollWindow;
/**
* スクロールされている間呼ばれる
*/
function scrollWindow() {
// 要素を取得
var items = document.querySelectorAll('.stop');
items.forEach( function(item){
var backgroundColor = item.style.backgroundColor;
if ( isItemInScreen(item) && !(backgroundColor == 'rgb(37, 37, 37)') ){
item.style.backgroundColor = 'rgb(37, 37, 37)';
stopScroll();
}
});
}
scrollWindow内にて要素を取得し、アイテムが範囲内にあるかどうかをチェックし、あればスクロールを止めます。
今回のデモでは、止まるのは最初の一度だけです。止まる時に色を変えて、その色で条件分岐しています。本来ならもうちょっと別のことで条件分岐したほうがいいと思います。
はい、これだけ!
全体コード
gistにコードあげときました。一応htmlとcssも付けておきました。
実はjs単体の記事は初めてなので、CodePen的なのは何も用意してないです。あとで追記するかもしれないし、しないかもしれないです。
慣性スクロールを止めるのは使いどころは結構難しいような、でも意外とあるような、そんな気がしてます。
例えばヘッダーとかフッターでスクロールoffsetがマイナスになるのを防ぐとか。
onScrollが必ず境界値で呼ばれるわけではないのでスクロール速度が速いとずれることがあります。
最近気付いたけれど、Xcodeのシミュレータならローカルファイルでもチェックできるので便利ですね。
ちなみに慣性スクロールはタッチデバイスでのみなので、同様なものをPC側で実装するには、addEventListenerで一定時間preventDefaultで実現することができます。動かしてみるとちょっと微妙だけれど。
これはjavascript – How to disable scrolling temporarily? – Stack Overflowの回答で実装できます。
書き終わってプレビューして見たらGIFが酷いね。。。雰囲気が伝われば・・・。
ほいでは〜〜〜。
こんにちは。
大変参考になる記事、ありがとうございます。
ただ、今回のコードでは止まるのは最初の一度だけですが、
itemBottomYを超えたら「下にスクロールできない」「上にスクロールはできる」
を実現するにはどうしたら良いでしょうか?
ご教授いただけたら幸いです。
よろしくお願いいたします。