【JavaScript】指定した位置で慣性スクロールを止めよう【スマホ】

どうも、Reoです。

スマホ等のタッチデバイスでスクロール時に、指定した位置でスクロールをストップさせたい!というのを実装してみたので紹介します。

PureでVanillaな素のjsで実装しました。

 

今回デモ用に作ったのは以下のものです。

指定したボックスの下端が画面の中心に来たらスクロールが止まります。

スクロールを止める方法

スクロールを止める方法は結構色々試しました。

試した方法は3種類。

  1. addEventListenerでスクロール中にpreventDefaultをする
  2. スクロール中に画面をタッチしたことにして止める
  3. 指定座標についたら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が酷いね。。。雰囲気が伝われば・・・。

ほいでは〜〜〜。

コメントは認証制です。詳しくは下記の注意をお読みください。お気軽にコメントお願いします!

Write a Comment

コメント時の注意

「Twitter」「Facebook」「Google+」「WordPress」のいずれかのアカウントをお持ちの方は各アカウントと連携することでコメントできます。 コメントしたことはSNSに流れませんので、アカウントをお持ちの方はこちらの方法でコメントを投稿して下さると嬉しいです。 アカウントをお持ちでない方はメールアドレスで投稿することができます。 初回コメント時は承認後に表示されます。

Related Memo...

iOSでiframeのwidthの挙動がおかしくなる時の対処メモ【pure js】

pureでvanillaな素のjsで。

どういう挙動かというと

iframeロード → 表示(多分画面幅くらい)→ ちょっと縮小 → さらに縮小して目的のwidthになる

という感じ。画像は載せられないので割愛します。

指定したwidthからはみ出た状態で始まり、最後には指定したwidthになるといった挙動ですね。

結局

var iframe = document.createElement('iframe');
iframe.width = '100%';
iframe.height = '100%';

とかいう風に書いているところに

iframe.scrolling = 'no';

この一文を入れてやると解決しました。

viewportとかwidthのこといっぱい調べたのになんのことはない・・・。

JavaScript

【pure js】アイキャッチの下などにあるヘッダーをスクロール後固定する

pureでvanillaな素のものjsで。

[ アイキャッチ画像 ]

[ ヘッダー ]

[ コンテンツ ]

みたいな作りのwebページでスクロール後ヘッダーを固定する。

固定解除部分は 元のcssにあわせて書けばおkです。

 

JavaScript

【vagrant+CentOS+CakePHP】JavaScriptファイルで何書いてもillegal characterって言われたときメモ..

jsファイルに何かいてもillegal characterって言われて泣くかと思った。(ブラウザはFirefox)

vagrantで下の記事通りに設定したらちゃんとjs動くようになった。本当に助かった・・・。

JavaScript
more