LoginSignup
5
4

More than 1 year has passed since last update.

Javascript スワイプ検出

Posted at

javascriptバニラで十分なプログラムだけど、スワイプ入力したいと言われた時に書いたコード。
何かしらライブラリとかフレームワーク使う時には、たいがい含まれてるんじゃないかな…しらんけど。

スワイプ検出する君

  • 検出するエレメントを指定してもらう。
    • そのエレメントに対するtouch系のイベント ( touchstart, touchend, touchmove, touchcancel ) を見張る。
    • この動きってスワイプ? と思ったら、swipe.xxx というイベントを作って発生させる。
  • マルチタッチとか気にしない
// スワイプ検出
const SwipeTracker = function(elem, direction="") {

    let x = 0;
    let y = 0;
    let target = null;
    let startX = 0;
    let startY = 0;
    let moveX = 0;
    let moveY = 0;
    let thresholdX;
    let thresholdY;
    let eventList = {};

    // 指定されたエレメントを検出対象にする
    target = elem;
    
    // ----- 諸々関数
    
    // スワイプと見なす閾値の設定、数値は用途で調整
    const setThreshold = function(){
        // 画面幅1/4または300の小さい方、割としっかり動かさないとスワイプにならない
        thresholdX = Math.min(window.innerWidth/4, 300);
        // 画面高さ1/6または50の小さい方、縦は敏感
        thresholdY = Math.min(window.innerHeight/6, 50);
    };

    // イベント発行。同じ名前のイベントを繰り返し作るのが気にならないなら不要
    const kickEvent = function(eventName) {
        if (eventList[eventName]==undefined) {
            eventList[eventName] = new Event(eventName);
        }
        target.dispatchEvent(eventList[eventName]);
    };

    // 検出のリセット
    const resetSwipe = function() {
        x = 0;
        y = 0;   
        startX = 0;
        startY = 0;
        moveX = 0;
        moveY = 0;
    }

    // スワイプのキャンセル
    const cencelSwipe = function() {
        resetSwipe();
        kickEvent("swipe.cancel");
    }

    // 外部に移動量を教えるメソッド
    this.posx = function(){ return x; };
    this.posy = function(){ return y; };

    // ---- タッチイベントを監視

    // 開始
    target.addEventListener("touchstart", (ev) => {
        // 開始座標記録
        startX = ev.touches[0].pageX;
        startY = ev.touches[0].pageY;
        moveX = startX;
        moveY = startY;
        kickEvent('swipe.start');
    }, {passive:true});
    // キャンセル
    target.addEventListener("touchcancel", cencelSwipe, {passive:true});
    // 移動
    target.addEventListener('touchmove', (ev) => {
        moveX = ev.touches[0].pageX;
        moveY = ev.touches[0].pageY;
        // タッチ開始時からの差分
        x = moveX - startX;
        y = moveY - startY;
        kickEvent('swipe.move');
    }, {passive:true});
    // 終了
    target.addEventListener("touchend", (ev) => {
        // 横方向の移動が閾値を超えてる=>左右スワイプ
        if (Math.abs(x) >= thresholdX) {
            if (x < 0) {
                kickEvent('swipe.left');
            } else {
                kickEvent('swipe.right');
            }
        // 左右の検出をしたかったけど閾値超えず=>キャンセル
        } else if (direction == "lr") {
            kickEvent("swipe.cancel");
        }
        // 縦方向の移動が閾値を超えてる=>上下スワイプ
        if (Math.abs(y) >= thresholdY) {
            if (y < 0) {
                kickEvent('swipe.up');
            } else {
                kickEvent('swipe.down');
            }
        }
        // 上下の検出をしたかったけど閾値超えず=>キャンセル
        else if (direction == "ud") {
            kickEvent("swipe.cancel");
        }
        // 何にせよ値リセット
        resetSwipe();
    }, {passive:true});
    
    // 呼ばれた時に、閾値設定を実行
    setThreshold();
    // 画面リサイズ時にも再実行
    window.addEventListener("resize", setThreshold);
};

既存のよくできたライブラリを使うほどじゃない、ってことなので、ベタ打ち・使い捨て感のあるコードでいいじゃないか。
スワイプ中に移動量を知らなくていいなら、もっと簡単に書けるのに。

使い方

  • スワイプ検出したいエレメントと、オプションで検出したい方向("lr" / "ud")を渡す。
  • そのエレメントに対して「swipe.xxxx」とかのイベントが発生するので、拾って好きにする。
const menu = document.getElementById('mainmenu');  // このエレメントの
const menuswipe = SwipeTracker(menu, 'lr');        // 左右スワイプ知りたい
// ----- イベント拾う
menu.addEventListener('swipe.right', (ev){
    console.log('右スワイプした');
});
menu.addEventListener('swipe.left', (ev){
    console.log('左スワイプした');
});
menu.addEventListener('swipe.move', (ev){
    // 移動中に移動量使ったりとか
    manu.style.left = menuswipe.posx() + 'px';
});

参考

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4