如何检测用户用JavaScript在网页上向某个方向滑动手指?

我想知道是否有一种解决方案可以同时适用于iPhone和Android手机上的网站。


当前回答

我只想检测左右滑动,但只在触摸事件结束时触发动作,所以我稍微修改了@givanse的答案来实现这一点。

为什么要这么做?例如,在滑动时,用户注意到他最终不想滑动,他可以将手指移动到原来的位置(一个非常流行的“约会”手机应用程序可以做到这一点;)),然后“向右滑动”事件被取消。

因此,为了避免因为水平上有3px的差异而导致“向右滑动”事件,我添加了一个阈值,在该阈值下事件将被丢弃:为了有一个“向右滑动”事件,用户必须至少滑动浏览器宽度的1/3(当然,您可以修改这个)。

所有这些小细节都增强了用户体验。

请注意,目前,如果两个手指中的一个在缩放过程中做了一个大的水平移动,那么“触摸缩放”可能会被检测为滑动。

这是(Vanilla JS)代码:

var xDown = null, yDown = null, xUp = null, yUp = null;
document.addEventListener('touchstart', touchstart, false);        
document.addEventListener('touchmove', touchmove, false);
document.addEventListener('touchend', touchend, false);
function touchstart(evt) { const firstTouch = (evt.touches || evt.originalEvent.touches)[0]; xDown = firstTouch.clientX; yDown = firstTouch.clientY; }
function touchmove(evt) { if (!xDown || !yDown ) return; xUp = evt.touches[0].clientX; yUp = evt.touches[0].clientY; }
function touchend(evt) { 
    var xDiff = xUp - xDown, yDiff = yUp - yDown;
    if ((Math.abs(xDiff) > Math.abs(yDiff)) && (Math.abs(xDiff) > 0.33 * document.body.clientWidth)) { 
        if (xDiff < 0) 
            document.getElementById('leftnav').click();
        else
            document.getElementById('rightnav').click();
    } 
    xDown = null, yDown = null;
}

其他回答

我重新包装了TouchWipe作为一个简短的jquery插件:detectSwipe

函数检查水平方向和垂直方向,以确定哪个滑动更长,以防止执行两个指令,因为不可能进行完美的单向滑动。滑动总是在X和Y上有偏差。

let touchstartX = 0;
let touchendX = 0;
let touchstartY = 0;
let touchendY = 0;   

function checkDirection() {
    let difX = touchstartX - touchendX;
    let difY = touchstartY - touchendY;
if (Math.abs(difX) > Math.abs(difY)) {
    if (touchendX < touchstartX) {/*left*/}
    if (touchendX > touchstartX) {/*right*/}
} else {
    if (touchendY < touchstartY) {/*up*/}
    if (touchendY > touchstartY) {/*down*/}
}
};
document.addEventListener('touchstart', e => {
    e.preventDefault();
    touchstartX = e.changedTouches[0].screenX;
    touchstartY = e.changedTouches[0].screenY;
});

document.addEventListener('touchend', e => {
    e.preventDefault();
    touchendX = e.changedTouches[0].screenX;
    touchendY = e.changedTouches[0].screenY;
    checkDirection();
});

一个如何使用偏移量的例子。

// at least 100 px are a swipe // you can use the value relative to screen size: window.innerWidth * .1 const offset = 100; let xDown, yDown window.addEventListener('touchstart', e => { const firstTouch = getTouch(e); xDown = firstTouch.clientX; yDown = firstTouch.clientY; }); window.addEventListener('touchend', e => { if (!xDown || !yDown) { return; } const { clientX: xUp, clientY: yUp } = getTouch(e); const xDiff = xDown - xUp; const yDiff = yDown - yUp; const xDiffAbs = Math.abs(xDown - xUp); const yDiffAbs = Math.abs(yDown - yUp); // at least <offset> are a swipe if (Math.max(xDiffAbs, yDiffAbs) < offset ) { return; } if (xDiffAbs > yDiffAbs) { if ( xDiff > 0 ) { console.log('left'); } else { console.log('right'); } } else { if ( yDiff > 0 ) { console.log('up'); } else { console.log('down'); } } }); function getTouch (e) { return e.changedTouches[0] }

一些mod的顶部回答(不能评论…)来处理短滑动

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);
var xDown = null;                                                        
var yDown = null;                                                        
function handleTouchStart(evt) {                                         
    xDown = evt.touches[0].clientX;                                      
    yDown = evt.touches[0].clientY;                                      
};                                                
function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

    var xUp = evt.touches[0].clientX;                                    
    var yUp = evt.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;
    if(Math.abs( xDiff )+Math.abs( yDiff )>150){ //to deal with to short swipes

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {/* left swipe */ 
            alert('left!');
        } else {/* right swipe */
            alert('right!');
        }                       
    } else {
        if ( yDiff > 0 ) {/* up swipe */
            alert('Up!'); 
        } else { /* down swipe */
            alert('Down!');
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;
    }
};

再加上这个答案。这个增加了对鼠标事件的支持,用于桌面测试:

<!--scripts-->
class SwipeEventDispatcher {
    constructor(element, options = {}) {
        this.evtMap = {
            SWIPE_LEFT: [],
            SWIPE_UP: [],
            SWIPE_DOWN: [],
            SWIPE_RIGHT: []
        };

        this.xDown = null;
        this.yDown = null;
        this.element = element;
        this.isMouseDown = false;
        this.listenForMouseEvents = true;
        this.options = Object.assign({ triggerPercent: 0.3 }, options);

        element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
        element.addEventListener('touchend', evt => this.handleTouchEnd(evt), false);
        element.addEventListener('mousedown', evt => this.handleMouseDown(evt), false);
        element.addEventListener('mouseup', evt => this.handleMouseUp(evt), false);
    }

    on(evt, cb) {
        this.evtMap[evt].push(cb);
    }

    off(evt, lcb) {
        this.evtMap[evt] = this.evtMap[evt].filter(cb => cb !== lcb);
    }

    trigger(evt, data) {
        this.evtMap[evt].map(handler => handler(data));
    }

    handleTouchStart(evt) {
        this.xDown = evt.touches[0].clientX;
        this.yDown = evt.touches[0].clientY;
    }

    handleMouseDown(evt) {
        if (this.listenForMouseEvents==false) return;
        this.xDown = evt.clientX;
        this.yDown = evt.clientY;
        this.isMouseDown = true;
    }

    handleMouseUp(evt) {
        if (this.isMouseDown == false) return;
        const deltaX = evt.clientX - this.xDown;
        const deltaY = evt.clientY - this.yDown;
        const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
        const activePct = distMoved / this.element.offsetWidth;

        if (activePct > this.options.triggerPercent) {
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
            } else {
                deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
            }
        }
    }

    handleTouchEnd(evt) {
        const deltaX = evt.changedTouches[0].clientX - this.xDown;
        const deltaY = evt.changedTouches[0].clientY - this.yDown;
        const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
        const activePct = distMoved / this.element.offsetWidth;

        if (activePct > this.options.triggerPercent) {
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
            } else {
                deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
            }
        }
    }
}

// add a listener on load
window.addEventListener("load", function(event) {
    const dispatcher = new SwipeEventDispatcher(document.body);
    dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })
    dispatcher.on('SWIPE_LEFT', () => { console.log('I swiped left!') })
});