通过iPhone和Android上的JavaScript检测手指滑动


268

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

我想知道是否存在一种适用于iPhone和Android手机上的网站的解决方案。


1
对于滑动识别,我建议使用Hammer.js。它很小,并且支持许多手势:- 滑动 -旋转-捏-按下(长按)-点击-平移
Will Brickner

有一个事件:“ touchmove”
粘土

@Clay一个人仍然无法在Safari中使用,因此没有iPhone。
Jakuje

Answers:


341

简单的原始JS代码示例:

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;

function getTouches(evt) {
  return evt.touches ||             // browser API
         evt.originalEvent.touches; // jQuery
}                                                     

function handleTouchStart(evt) {
    const firstTouch = getTouches(evt)[0];                                      
    xDown = firstTouch.clientX;                                      
    yDown = firstTouch.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 ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            /* left swipe */ 
        } else {
            /* right swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* up swipe */ 
        } else { 
            /* down swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

在Android上测试。


1
外观很酷,简单,任何想法是这个活动的支持 touchstarttouchmove
d.raev

1
它工作得很好,但是在检测直线运动时存在问题。我将在该主题上发布另一个答案,将其固定为JQuery(桌面)解决方案。它还添加了这些滑动事件的鼠标版本并添加了灵敏度选项。
Codebeat

1
该死的。主题已关闭,所以无法添加我的答案!
Codebeat 2015年

3
这很好用,但是左/右和上/下是向后的。
Peter Eisentraut '16

4
originalEvent是一个JQuery属性。如果您在没有JQuery的情况下运行纯JavaScript,则应将其排除在外。如果不使用JQuery运行当前代码。
Jan Derk'May

31

根据@givanse的答案,这是您可以使用的方式classes

class Swipe {
    constructor(element) {
        this.xDown = null;
        this.yDown = null;
        this.element = typeof(element) === 'string' ? document.querySelector(element) : element;

        this.element.addEventListener('touchstart', function(evt) {
            this.xDown = evt.touches[0].clientX;
            this.yDown = evt.touches[0].clientY;
        }.bind(this), false);

    }

    onLeft(callback) {
        this.onLeft = callback;

        return this;
    }

    onRight(callback) {
        this.onRight = callback;

        return this;
    }

    onUp(callback) {
        this.onUp = callback;

        return this;
    }

    onDown(callback) {
        this.onDown = callback;

        return this;
    }

    handleTouchMove(evt) {
        if ( ! this.xDown || ! this.yDown ) {
            return;
        }

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

        this.xDiff = this.xDown - xUp;
        this.yDiff = this.yDown - yUp;

        if ( Math.abs( this.xDiff ) > Math.abs( this.yDiff ) ) { // Most significant.
            if ( this.xDiff > 0 ) {
                this.onLeft();
            } else {
                this.onRight();
            }
        } else {
            if ( this.yDiff > 0 ) {
                this.onUp();
            } else {
                this.onDown();
            }
        }

        // Reset values.
        this.xDown = null;
        this.yDown = null;
    }

    run() {
        this.element.addEventListener('touchmove', function(evt) {
            this.handleTouchMove(evt).bind(this);
        }.bind(this), false);
    }
}

您可以像这样使用它:

// Use class to get element by string.
var swiper = new Swipe('#my-element');
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// Get the element yourself.
var swiper = new Swipe(document.getElementById('#my-element'));
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();

// One-liner.
(new Swipe('#my-element')).onLeft(function() { alert('You swiped left.') }).run();

7
该代码可能无法正常工作,因为您尝试调用.bindundefined 时会遇到异常,因为您handleTouchMove实际上未返回任何内容。调用函数时也没有用,this.因为它已经绑定到当前上下文,因此无法调用bind
nick.skriabin 16-10-14

3
我刚删除.bind(this);,它正常运行。谢谢@nicholas_r
Ali Ghanavatian

部分自己获取元素,我只是在document.getElementById('my-element')中删除了“#”,所以效果很好。谢谢@Marwelln :)
蓝色电车

4
如果您想等到滑动ENDS(意思是在他们举起手指或touches[0]changedTouches[0]handleTouchMovehandleTouchEnd
鼠标悬停

打电话run()两次,您会收到令人讨厌的内存泄漏
Mat

20

我将此处的一些答案合并为一个脚本,该脚本使用CustomEvent在DOM中触发滑动事件。将0.7k swiped -events.min.js脚本添加到您的页面,并监听滑动事件:

向左滑动

document.addEventListener('swiped-left', function(e) {
    console.log(e.target); // the element that was swiped
});

向右滑动

document.addEventListener('swiped-right', function(e) {
    console.log(e.target); // the element that was swiped
});

刷了

document.addEventListener('swiped-up', function(e) {
    console.log(e.target); // the element that was swiped
});

扫下

document.addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

您还可以直接附加到元素:

document.getElementById('myBox').addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

可选配置

您可以指定以下属性来调整页面中滑动交互功能的方式(这些是可选的)

<div data-swipe-threshold="10"
     data-swipe-timeout="1000"
     data-swipe-ignore="false">
        Swiper, get swiping!
</div>

源代码在Github上可用


我之所以来到这里,是因为纯
笔刷不适

@StefanBob如果您在github存储库上打勾并提供足够的信息以允许我重现该问题,我将进行调查
John Doherty

1
谢谢,它运行完美!我用您的库替换了Hammer.js,因为前者不适用于浏览器缩放,这是一个严重的可用性问题。使用此库,缩放功能可以正常工作(在Android上测试)
collimarco

15

我之前使用过的方法是,必须检测mousedown事件,记录其x,y位置(无论哪个相关),然后检测mouseup事件,然后减去两个值。


28
我认为可以使用的是touchstart,touchmove,touchcancel和touchend,而不是mousedown或mouseup。
Volomike


12

我发现@givanse绝妙的答案是在多个移动浏览器中最可靠和兼容的,用于注册划动动作。

但是,他的代码需要进行更改,以使其能够在使用的现代移动浏览器中运行jQuery

event.touches如果jQuery使用,将不会存在,并且会导致undefined并应将其替换event.originalEvent.touches。没有jQueryevent.touches应该工作正常。

所以解决方案变成了

document.addEventListener('touchstart', handleTouchStart, false);        
document.addEventListener('touchmove', handleTouchMove, false);

var xDown = null;                                                        
var yDown = null;                                                        

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

function handleTouchMove(evt) {
    if ( ! xDown || ! yDown ) {
        return;
    }

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

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;

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

经过测试:

  • Android:Chrome,UC浏览器
  • iOS:Safari,Chrome,UC浏览器

originalEvent是一个JQuery属性。它甚至不存在于纯Javascript中。
Jan Derk'May

1
根据此SO答案,如果浏览器支持,则会通过公开触摸事件event.originalEvent。事情现在event.touches已经不复存在并导致了undefined
nashcheez

event.touches仅在使用JQuery时不存在。在没有JQuery的情况下尝试代码,您将得到一个错误,提示evt.originalEvent未定义。JQuery完全用自己的事件替换事件,并将本机浏览器事件放入originalevent中。简短版本:您的代码仅适用于JQuery。如果删除了原始事件,则无需JQuery即可工作。
Jan Derk

1
是的,我进行了一些研究,意识到您对jquery支持的可用性是正确的event.originalEvent。我将更新我的答案。谢谢!:)
nashcheez


6

一些mod的uppest答案(无法评论...)以应对轻扫

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;
    }
};

6

垃圾桶,超时刷卡,swipeBlockElems添加。

document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
document.addEventListener('touchend', handleTouchEnd, false);     

const SWIPE_BLOCK_ELEMS = [
  'swipBlock',
  'handle',
  'drag-ruble'
]

let xDown = null;
let yDown = null; 
let xDiff = null;
let yDiff = null;
let timeDown = null;
const  TIME_TRASHOLD = 200;
const  DIFF_TRASHOLD = 130;

function handleTouchEnd() {

let timeDiff = Date.now() - timeDown; 
if (Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
  if (Math.abs(xDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
    if (xDiff > 0) {
      // console.log(xDiff, TIME_TRASHOLD, DIFF_TRASHOLD)
      SWIPE_LEFT(LEFT) /* left swipe */
    } else {
      // console.log(xDiff)
      SWIPE_RIGHT(RIGHT) /* right swipe */
    }
  } else {
    console.log('swipeX trashhold')
  }
} else {
  if (Math.abs(yDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
    if (yDiff > 0) {
      /* up swipe */
    } else {
      /* down swipe */
    }
  } else {
    console.log('swipeY trashhold')
  }
 }
 /* reset values */
 xDown = null;
 yDown = null;
 timeDown = null; 
}
function containsClassName (evntarget , classArr) {
 for (var i = classArr.length - 1; i >= 0; i--) {
   if( evntarget.classList.contains(classArr[i]) ) {
      return true;
    }
  }
}
function handleTouchStart(evt) {
  let touchStartTarget = evt.target;
  if( containsClassName(touchStartTarget, SWIPE_BLOCK_ELEMS) ) {
    return;
  }
  timeDown = Date.now()
  xDown = evt.touches[0].clientX;
  yDown = evt.touches[0].clientY;
  xDiff = 0;
  yDiff = 0;

}

function handleTouchMove(evt) {
  if (!xDown || !yDown) {
    return;
  }

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


  xDiff = xDown - xUp;
  yDiff = yDown - yUp;
}

4

如果有人尝试在Android上使用jQuery Mobile并遇到JQM滑动检测问题

(我在Xperia Z1,Galaxy S3,Nexus 4和一些Wiko手机上也有使用过),这很有用:

 //Fix swipe gesture on android
    if(android){ //Your own device detection here
        $.event.special.swipe.verticalDistanceThreshold = 500
        $.event.special.swipe.horizontalDistanceThreshold = 10
    }

除非经过长时间,精确和快速的滑动,否则不会检测到Android上的滑动。

通过这两行,它可以正常工作


我还需要补充:$.event.special.swipe.scrollSupressionThreshold = 8;但是您让我朝着正确的方向前进!谢谢!
菲利普G

4

我在用户拖动手指时连续触摸触发处理程序遇到麻烦。我不知道这是由于我做错了什么还是我做错了什么,但我将其重新布线以通过touchmove累积移动,而touchend实际上触发了回调。

我还需要拥有大量的这些实例,因此我添加了启用/禁用方法。

还有一个短触发不会触发的阈值。每次轻触启动零的计数器。

您可以随时更改target_node。创建时启用是可选的。

/** Usage: */
touchevent = new Modules.TouchEventClass(callback, target_node);
touchevent.enable();
touchevent.disable();

/** 
*
*   Touch event module
*
*   @param method   set_target_mode
*   @param method   __touchstart
*   @param method   __touchmove
*   @param method   __touchend
*   @param method   enable
*   @param method   disable
*   @param function callback
*   @param node     target_node
*/
Modules.TouchEventClass = class {

    constructor(callback, target_node, enable=false) {

        /** callback function */
        this.callback = callback;

        this.xdown = null;
        this.ydown = null;
        this.enabled = false;
        this.target_node = null;

        /** move point counts [left, right, up, down] */
        this.counts = [];

        this.set_target_node(target_node);

        /** Enable on creation */
        if (enable === true) {
            this.enable();
        }

    }

    /** 
    *   Set or reset target node
    *
    *   @param string/node target_node
    *   @param string      enable (optional)
    */
    set_target_node(target_node, enable=false) {

        /** check if we're resetting target_node */
        if (this.target_node !== null) {

            /** remove old listener */
           this.disable();
        }

        /** Support string id of node */
        if (target_node.nodeName === undefined) {
            target_node = document.getElementById(target_node);
        }

        this.target_node = target_node;

        if (enable === true) {
            this.enable();
        }
    }

    /** enable listener */
    enable() {
        this.enabled = true;
        this.target_node.addEventListener("touchstart", this.__touchstart.bind(this));
        this.target_node.addEventListener("touchmove", this.__touchmove.bind(this));
        this.target_node.addEventListener("touchend", this.__touchend.bind(this));
    }

    /** disable listener */
    disable() {
        this.enabled = false;
        this.target_node.removeEventListener("touchstart", this.__touchstart);
        this.target_node.removeEventListener("touchmove", this.__touchmove);
        this.target_node.removeEventListener("touchend", this.__touchend);
    }

    /** Touchstart */
    __touchstart(event) {
        event.stopPropagation();
        this.xdown = event.touches[0].clientX;
        this.ydown = event.touches[0].clientY;

        /** reset count of moves in each direction, [left, right, up, down] */
        this.counts = [0, 0, 0, 0];
    }

    /** Touchend */
    __touchend(event) {
        let max_moves = Math.max(...this.counts);
        if (max_moves > 500) { // set this threshold appropriately
            /** swipe happened */
            let index = this.counts.indexOf(max_moves);
            if (index == 0) {
                this.callback("left");
            } else if (index == 1) {
                this.callback("right");
            } else if (index == 2) {
                this.callback("up");
            } else {
                this.callback("down");
            }
        }
    }

    /** Touchmove */
    __touchmove(event) {

        event.stopPropagation();
        if (! this.xdown || ! this.ydown) {
            return;
        }

        let xup = event.touches[0].clientX;
        let yup = event.touches[0].clientY;

        let xdiff = this.xdown - xup;
        let ydiff = this.ydown - yup;

        /** Check x or y has greater distance */
        if (Math.abs(xdiff) > Math.abs(ydiff)) {
            if (xdiff > 0) {
                this.counts[0] += Math.abs(xdiff);
            } else {
                this.counts[1] += Math.abs(xdiff);
            }
        } else {
            if (ydiff > 0) {
                this.counts[2] += Math.abs(ydiff);
            } else {
                this.counts[3] += Math.abs(ydiff);
            }
        }
    }
}

这是ES5还是ES6?
1.21吉瓦,

@gigawatts我不记得了。使用过的项目已经达到EOL,此后我就不再需要代码了。我怀疑当时我正在为ES6写作,但是那是两年多以前了。
Trendal Toews

3

我也合并了一些答案,主要是第一个和第二个与类,这是我的版本:

export default class Swipe {
    constructor(options) {
        this.xDown = null;
        this.yDown = null;

        this.options = options;

        this.handleTouchStart = this.handleTouchStart.bind(this);
        this.handleTouchMove = this.handleTouchMove.bind(this);

        document.addEventListener('touchstart', this.handleTouchStart, false);
        document.addEventListener('touchmove', this.handleTouchMove, false);

    }

    onLeft() {
        this.options.onLeft();
    }

    onRight() {
        this.options.onRight();
    }

    onUp() {
        this.options.onUp();
    }

    onDown() {
        this.options.onDown();
    }

    static getTouches(evt) {
        return evt.touches      // browser API

    }

    handleTouchStart(evt) {
        const firstTouch = Swipe.getTouches(evt)[0];
        this.xDown = firstTouch.clientX;
        this.yDown = firstTouch.clientY;
    }

    handleTouchMove(evt) {
        if ( ! this.xDown || ! this.yDown ) {
            return;
        }

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

        let xDiff = this.xDown - xUp;
        let yDiff = this.yDown - yUp;


        if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
            if ( xDiff > 0 && this.options.onLeft) {
                /* left swipe */
                this.onLeft();
            } else if (this.options.onRight) {
                /* right swipe */
                this.onRight();
            }
        } else {
            if ( yDiff > 0 && this.options.onUp) {
                /* up swipe */
                this.onUp();
            } else if (this.options.onDown){
                /* down swipe */
                this.onDown();
            }
        }

        /* reset values */
        this.xDown = null;
        this.yDown = null;
    }
}

之后可以将其用作以下内容:

let swiper = new Swipe({
                    onLeft() {
                        console.log('You swiped left.');
                    }
});

当您只想说“ onLeft”方法时,它有助于避免控制台错误。



2

如果只需要滑动,则最好只使用所需的零件,以减小尺寸。这应该可以在任何触摸设备上使用。

经过gzip压缩,缩小,babel等后,这约为450个字节。

我根据其他答案写了下面的类,它使用移动百分比而不是像素,并使用事件分配器模式来挂接/取消挂接。

像这样使用它:

const dispatcher = new SwipeEventDispatcher(myElement);
dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })

export 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.options = Object.assign({ triggerPercent: 0.3 }, options);

		element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
		element.addEventListener('touchend', evt => this.handleTouchEnd(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;
	}

	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');
			}
		}
	}
}

export default SwipeEventDispatcher;


2

我只想检测左右滑动,但仅在touch事件结束时才触发动作,因此我略微修改了@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;
}

1

水平滑动的简单香草JS示例:

let touchstartX = 0
let touchendX = 0

const slider = document.getElementById('slider')

function handleGesure() {
  if (touchendX < touchstartX) alert('swiped left!')
  if (touchendX > touchstartX) alert('swiped right!')
}

slider.addEventListener('touchstart', e => {
  touchstartX = e.changedTouches[0].screenX
})

slider.addEventListener('touchend', e => {
  touchendX = e.changedTouches[0].screenX
  handleGesure()
})

您可以对垂直滑动使用完全相同的逻辑。


1

此处添加此答案。这增加了对鼠标事件的支持,以便在台式机上进行测试:

<!--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!') })
});

0

有关如何与offset一起使用的示例。

// 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]
}


当前正在使用此版本。如果重复轻扫,如何防止多次触发?我将其与动画功能一起用于横摇表单,并且当我多次滑动时,事情变得有些棘手,并且div开始在可见区域重叠。
NMALM

0

您可能会更轻松地用鼠标事件来实现它,以实现原型。

这里有很多答案,包括最上面的答案,应谨慎使用,因为它们不考虑边缘情况,尤其是在边界框周围。

看到:

您将需要进行实验以捕获边缘情况和行为,例如指针在结束之前移到元素外部。

滑动是一种非常基本的手势,它是较高级别的界面指针交互处理,大致位于处理原始事件和手写识别之间。

尽管实际上所有方法通常都遵循检测距离和速度或速度的阈值的跨元素运动的基本原理,但没有唯一的精确方法可以检测滑动或甩动。您可能只是说,如果在给定时间内在给定方向上跨越屏幕尺寸的65%进行移动,那么这就是滑动。线条的确切位置和计算方式完全取决于您。

有些人可能还从某个方向的动量以及释放该元素时将其推离屏幕多远的角度来查看它。使用粘性滑动可以更清楚地看到,可以在其中拖动元素,然后在释放时将弹起或弹回屏幕,就好像松紧带断裂一样。

尝试找到一个手势库,可以移植或重用通常用于保持一致性的手势库,可能是理想的选择。这里的许多示例都过于简单化,将滑动记录为任何方向上的丝毫触感。

Android将是显而易见的选择,尽管存在相反的问题,但它过于复杂。

许多人似乎误解了这个问题,认为它是一个方向上的任何运动。滑动是在单个方向上压倒性的广泛且相对简短的运动(尽管可能是弧形的并且具有某些加速属性)。猛冲类似,尽管它打算根据自身的动力随意地将物品推开一定距离。

两者足够相似,以至于某些库可能仅提供刷新或滑动,可以互换使用。在纯平屏幕上,很难将两个手势真正分开,并且一般来说,人们都在做这两个手势(滑动物理屏幕,但猛冲屏幕上显示的UI元素)。

您最好的选择是自己不要做。已经存在大量用于检测简单手势的JavaScript库


0

我重新设计了@givanse的解决方案以充当React挂钩。输入是一些可选的事件侦听器,输出是功能性的引用(需要功能性,因此当/如果引用发生更改,挂钩可以重新运行)。

还添加了垂直/水平滑动阈值参数,以使小动作不会意外触发事件侦听器,但可以将其设置为0以更接近原始答案。

提示:为了获得最佳性能,应记住事件监听器的输入功能。

function useSwipeDetector({
    // Event listeners.
    onLeftSwipe,
    onRightSwipe,
    onUpSwipe,
    onDownSwipe,

    // Threshold to detect swipe.
    verticalSwipeThreshold = 50,
    horizontalSwipeThreshold = 30,
}) {
    const [domRef, setDomRef] = useState(null);
    const xDown = useRef(null);
    const yDown = useRef(null);

    useEffect(() => {
        if (!domRef) {
            return;
        }

        function handleTouchStart(evt) {
            const [firstTouch] = evt.touches;
            xDown.current = firstTouch.clientX;
            yDown.current = firstTouch.clientY;
        };

        function handleTouchMove(evt) {
            if (!xDown.current || !yDown.current) {
                return;
            }

            const [firstTouch] = evt.touches;
            const xUp = firstTouch.clientX;
            const yUp = firstTouch.clientY;
            const xDiff = xDown.current - xUp;
            const yDiff = yDown.current - yUp;

            if (Math.abs(xDiff) > Math.abs(yDiff)) {/*most significant*/
                if (xDiff > horizontalSwipeThreshold) {
                    if (onRightSwipe) onRightSwipe();
                } else if (xDiff < -horizontalSwipeThreshold) {
                    if (onLeftSwipe) onLeftSwipe();
                }
            } else {
                if (yDiff > verticalSwipeThreshold) {
                    if (onUpSwipe) onUpSwipe();
                } else if (yDiff < -verticalSwipeThreshold) {
                    if (onDownSwipe) onDownSwipe();
                }
            }
        };

        function handleTouchEnd() {
            xDown.current = null;
            yDown.current = null;
        }

        domRef.addEventListener("touchstart", handleTouchStart, false);
        domRef.addEventListener("touchmove", handleTouchMove, false);
        domRef.addEventListener("touchend", handleTouchEnd, false);

        return () => {
            domRef.removeEventListener("touchstart", handleTouchStart);
            domRef.removeEventListener("touchmove", handleTouchMove);
            domRef.removeEventListener("touchend", handleTouchEnd);
        };
    }, [domRef, onLeftSwipe, onRightSwipe, onUpSwipe, onDownSwipe, verticalSwipeThreshold, horizontalSwipeThreshold]);

    return (ref) => setDomRef(ref);
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.