在 iPhone 和 Android 上检测手指轻扫 JavaScript

Detect a finger swipe through JavaScript on the iPhone and Android

提问人:827 提问时间:2/15/2010 更新时间:6/18/2023 访问量:449104

问:

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

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

javascript iphone android 刷卡

评论

6赞 Will Brickner 11/4/2016
对于滑动识别,我推荐 Hammer.js。它非常小,并且支持许多手势: - 滑动 - 旋转 - 捏合 - 按下(长按) - 点击 - 平移
0赞 Clay 5/23/2020
有一个事件:“touchmove”
1赞 Jakuje 6/13/2020
@Clay那个仍然无法在Safari中工作,因此没有iPhone。
0赞 Piotr Śródka 12/18/2022
@Jakuje“Can I Use”网站上说 iOS 上的 Safari 支持触摸事件,所以 iPhone 是的。
0赞 ccpizza 2/23/2023
最新版本的 iOS 对 HTML5 视频元素的左右滑动手势进行了原生 ootb 处理;手势将寻找带有一些动态动画UI弹出窗口的视频,以指示视频中的当前位置;因此,除非它旨在覆盖此行为,否则可能应该显式检测到这种情况。

答:

16赞 helloandre 2/15/2010 #1

我以前使用的是,您必须检测 MouseDown 事件,记录其 x,y 位置(以相关为准),然后检测 mouseup 事件,并减去这两个值。

评论

31赞 Volomike 1/17/2011
我相信可以使用的是 touchstart、touchmove、touchcancel 和 touchend,而不是 mousedown 或 mouseup。
12赞 Grinn 11/26/2012 #2

jQuery Mobile 还包括滑动支持: http://api.jquerymobile.com/swipe/

$("#divId").on("swipe", function(event) {
    alert("It's a swipe!");
});

评论

4赞 Niklas Ekman 11/28/2013
如果不希望 jQuery mobile 操作 UI,请参阅:stackoverflow.com/questions/8648596/...
9赞 Marc-André Lafortune 3/26/2014 #3

我已将 TouchWipe 重新打包为一个简短的 jquery 插件:detectSwipe

537赞 givanse 4/23/2014 #4

简单的 vanilla 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 ) {
            /* right swipe */ 
        } else {
            /* left swipe */
        }                       
    } else {
        if ( yDiff > 0 ) {
            /* down swipe */ 
        } else { 
            /* up swipe */
        }                                                                 
    }
    /* reset values */
    xDown = null;
    yDown = null;                                             
};

在 Android 中测试。

评论

2赞 d.raev 6/5/2014
看起来很酷,很简单,知道这个活动的支持是什么吗?touchstarttouchmove
1赞 Codebeat 4/22/2015
它运行良好,但在检测直线运动时存在问题。我将在本主题中发布另一个答案,将其修复为 JQuery(桌面)解决方案。它还添加了这些滑动事件的鼠标版本,并添加了敏感度选项。
1赞 Codebeat 4/22/2015
该死的。主题已关闭,因此无法添加我的答案!
6赞 Peter Eisentraut 2/16/2016
这很好用,但左/右和上/下是倒退的。
7赞 Jan Derk 5/22/2017
originalEvent 是一个 JQuery 属性。如果您在没有 JQuery 的情况下运行纯 javascript,则应将其省略。如果在不使用 JQuery 的情况下运行,则当前代码将引发异常。
4赞 Rayjax 7/15/2014 #5

如果有人尝试在 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 上滑动不会被检测到,除非它是非常长、精确和快速的滑动。

使用这两条线,它可以正常工作

评论

0赞 Philip G 12/26/2014
我还需要补充:但你把我放在正确的方向上!谢谢!$.event.special.swipe.scrollSupressionThreshold = 8;
13赞 rmnsh 9/14/2016 #6

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

评论

0赞 Jack 6/4/2023
添加了水平捏合缩放if ( ! xDown || ! yDown || e.touches.length === 2 ) {
47赞 Marwelln 9/17/2016 #7

根据 @givanse 的回答,您可以通过以下方式使用类

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();

评论

8赞 nick.skriabin 10/14/2016
此代码可能无法正常工作,因为您在尝试调用 undefined 时会遇到异常,因为您实际上没有返回任何内容。此外,在调用函数时调用 bind 是没有用的,因为它已经绑定到当前上下文.bindhandleTouchMovethis.
3赞 Ali Ghanavatian 7/29/2017
我刚刚删除了它,它工作得很优雅。谢谢@nicholas_r.bind(this);
0赞 Blue Tram 3/25/2019
部分自己获取元素,我只是删除document.getElementById('my-element')中的'#',效果很好。谢谢@Marwelln :)
6赞 TetraDev 4/19/2019
如果要等到轻扫 ENDS(即在他们抬起手指或鼠标向上之后),请更改为 和 事件处理程序类型touches[0]changedTouches[0]handleTouchMovehandleTouchEnd
1赞 Mat 4/19/2019
调用两次,您会得到令人讨厌的内存泄漏run()
6赞 Sergey Guns 11/25/2016 #8

阈值、超时滑动、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_THRESHOLD = 200;
const  DIFF_THRESHOLD = 130;

function handleTouchEnd() {

let timeDiff = Date.now() - timeDown; 
if (Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
  if (Math.abs(xDiff) > DIFF_THRESHOLD && timeDiff < TIME_THRESHOLD) {
    if (xDiff > 0) {
      // console.log(xDiff, TIME_THRESHOLD, DIFF_THRESHOLD)
      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_THRESHOLD && timeDiff < TIME_THRESHOLD) {
    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;
}
20赞 nashcheez 4/5/2017 #9

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

但是,他的代码需要进行更改才能使其在使用 .jQuery

event.touches如果使用 ,则不存在,并导致 和 应替换为 。没有,应该可以正常工作。jQueryundefinedevent.originalEvent.touchesjQueryevent.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 浏览器

评论

0赞 Jan Derk 5/22/2017
originalEvent 是一个 JQuery 属性。它甚至不存在于纯 Javascript 中。
1赞 nashcheez 5/22/2017
根据此 SO 答案,如果浏览器支持,则触摸事件将通过 公开。事情现在已经不复存在,并导致.event.originalEventevent.touchesundefined
0赞 Jan Derk 5/22/2017
event.touches 仅在使用 JQuery 时才不复存在。在没有 JQuery 的情况下尝试您的代码,您将收到一个错误,指出 evt.originalEvent 未定义。JQuery 将事件完全替换为自己的事件,并将本机浏览器事件放在 originalevent 中。简短版本:您的代码仅适用于 JQuery。如果删除 originalevent,则无需 JQuery 即可工作。
1赞 nashcheez 5/22/2017
是的,我研究了一下,意识到你对jquery启用的可用性是正确的。我会更新我的答案。谢谢!:)event.originalEvent
0赞 Kviz Majster 9/4/2020
更改 xDown = evt.originalEvent.touches[0].clientX;yDown = evt.originalEvent.touches[0].clientY;到 xDown = evt.offsetX;yDown = evt.offsetY;现在它在普通 JS 上就像魅力一样工作。我喜欢这个解决方案。
4赞 Trendal Toews 8/11/2017 #10

当用户拖动手指时,触摸端处理程序连续触发时,我遇到了麻烦。我不知道这是否是由于我做错了什么,但我重新连接了它以使用 touchmove 累积动作,touchend 实际上触发了回调。

我还需要有大量这样的实例,所以我添加了启用/禁用方法。

以及短划不触发的阈值。每次 Touchstart 0 都是计数器。

您可以即时更改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);
            }
        }
    }
}

评论

0赞 1.21 gigawatts 12/31/2019
这是针对 ES5 还是 ES6?
0赞 Trendal Toews 1/1/2020
@gigawatts我不记得了。使用它的项目已经达到 EOL,从那以后我就不需要代码了。我怀疑当时我正在为 ES6 写作,但那是 2 年前的事了。
2赞 Star 8/18/2017 #11

使用两个:

jQuery mobile:在大多数情况下都可以工作,特别是当您开发使用其他jQuery插件的应用程序时,最好使用jQuery移动控件。在这里访问它: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp

锤子时间!最好、轻量级和快速的基于 JavaScript 的库之一。在这里访问它: https://hammerjs.github.io/

63赞 John Doherty 1/15/2018 #12

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

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

向左滑动

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>

若要在应用程序范围内设置默认值,请在最顶层元素上设置配置属性:

<body data-swipe-threshold="100" data-swipe-timeout="250">
    <div>Swipe me</div>
    <div>or me</div>
</body>

源代码可在 Github 上找到

评论

0赞 StefanBob 5/4/2018
我来这里是因为纯滑动在移动设备上不适合我
0赞 John Doherty 1/10/2019
@StefanBob如果您在 github 存储库上打勾,并提供足够的信息让我重现该问题,我会调查它
2赞 collimarco 11/27/2019
谢谢,它工作得很好!我用你的库替换了 Hammer.js,因为前者不适用于浏览器缩放,这是一个严重的可用性问题。使用此库,缩放可以正常工作(在 Android 上测试)
3赞 AlexandreS 11/19/2020
Hammer.js 似乎不再维护了
1赞 Zahidul Islam Ruhel 7/7/2022
@JohnDoherty扩展它,以便它也适用于桌面(非触摸)设备。
3赞 Vargr 8/29/2018 #13

如果您只需要滑动,最好只使用您需要的部分。 这应该适用于任何触摸设备。

这是 ~450 字节,经过 gzip 压缩、缩小、babel 等。

我根据其他答案编写了以下类,它使用移动百分比而不是像素,以及事件调度器模式来挂钩/取消钩接事物。

像这样使用它:

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;

0赞 Илья Зеленько 12/30/2018 #14

如何与 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]
}

评论

1赞 NMALM 4/11/2020
当前使用此版本。如果重复滑动,我将如何防止它多次触发?我将它与动画功能一起使用,用于侧滚动体,当我多次滑动时,事情变得有点糟糕,我的 div 开始在可见区域重叠。
3赞 Romanna Semenyshyn 5/5/2019 #15

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

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”方法时,它有助于避免控制台错误。

263赞 Damjan Pavlica 6/19/2019 #16

水平滑动的简单 vanilla JS 示例:

let touchstartX = 0
let touchendX = 0
    
function checkDirection() {
  if (touchendX < touchstartX) alert('swiped left!')
  if (touchendX > touchstartX) alert('swiped right!')
}

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

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

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

评论

7赞 codepleb 5/1/2021
哈哈,这太简单了,甚至允许指定“旅行距离”。
7赞 Mattia Rasulo 6/25/2021
到目前为止最好的答案..很遗憾它没有更多的赞成票..
3赞 Mystogan 11/1/2021
@MattiaRasulo可能需要向上和向下滑动
2赞 Henry James 2/1/2022
这是一些非常好的代码,如果你想上下做,你需要做的就是改变它状态 X 的位置并用 Y 替换。
5赞 Philipp Moers 3/18/2022
很好的答案,因为它很简单!值得注意的是,如果触摸点具有最小的 x 距离,则使用此代码将垂直滑动检测为水平滑动。
1赞 jgmjgm 8/29/2019 #17

首先使用鼠标事件实现原型可能会更容易。

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

看:

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

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

没有单一的精确方法来检测滑动或甩动,尽管几乎所有方法通常都遵循一个基本原理,即检测具有距离和速度或速度阈值的元素的运动。您可以简单地说,如果在给定时间内沿给定方向移动 65% 的屏幕尺寸,那么它就是滑动。你在哪里画线以及如何计算它取决于你。

有些人也可能从某个方向的动量以及元素释放时它被推离屏幕多远的角度来看待它。使用粘性滑动可以拖动元素,然后在释放时会反弹或飞出屏幕,就像松紧带断裂一样,这一点会更清晰。

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

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

许多人似乎将这个问题误解为朝着某个方向的任何运动。滑动是在一个方向上压倒性地进行的广泛且相对短暂的运动(尽管可能是弧形的并具有一定的加速特性)。甩球与此类似,但目的是在自身动量下随意地将物品推开一段距离。

这两者非常相似,以至于某些库可能只提供 fling 或 swipe,它们可以互换使用。在平面屏幕上,很难真正区分这两种手势,一般来说,人们会同时执行这两种操作(滑动物理屏幕,但甩动屏幕上显示的 UI 元素)。

你最好的选择是不要自己动手。已经有大量的 JavaScript 库用于检测简单的手势

7赞 Basj 10/15/2019 #18

我只想检测左右滑动,但只在触摸事件结束时触发操作,所以我稍微修改了@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;
}
5赞 1.21 gigawatts 12/31/2019 #19

在这里补充这个答案。这增加了对鼠标事件的支持,以便在桌面上进行测试:

<!--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赞 Dgloria 7/26/2021
这是完美的。
2赞 Ruben Martinez Jr. 5/27/2020 #20

我重新设计了 @givanse 的解决方案,使其充当 React 钩子。输入是一些可选的事件侦听器,输出是功能性 ref(需要功能性,以便钩子可以在 ref 更改时重新运行)。

还添加了垂直/水平滑动阈值参数,以便小动作不会意外触发事件侦听器,但可以将这些设置为 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);
};

评论

1赞 Vivi 2/19/2021
您有机会添加示例用法吗?
0赞 dimButTries 7/26/2020 #21

我必须为轮播编写一个简单的脚本来检测向左或向右滑动。

我使用了指针事件而不是触摸事件。

我希望这对个人有用,我欢迎任何改进我的代码的见解;与非常优秀的 JS 开发人员一起加入这个线程,我感到相当羞怯。

function getSwipeX({elementId}) {

  this.e               = document.getElementsByClassName(elementId)[0];
  this.initialPosition = 0;
  this.lastPosition    = 0;
  this.threshold       = 200;
  this.diffInPosition  = null;
  this.diffVsThreshold = null;
  this.gestureState    = 0;

  this.getTouchStart = (event) => {
    event.preventDefault();
    if (window.PointerEvent) {
      this.e.setPointerCapture(event.pointerId);
    }
    return this.initalTouchPos = this.getGesturePoint(event);
  }

  this.getTouchMove  = (event) => {
    event.preventDefault();
    return this.lastPosition = this.getGesturePoint(event);
  }

  this.getTouchEnd   = (event) => {
    event.preventDefault();
    if (window.PointerEvent) {
      this.e.releasePointerCapture(event.pointerId);
    }
    this.doSomething();
    this.initialPosition = 0;
  }

  this.getGesturePoint = (event) => {
    this.point = event.pageX
    return this.point;
  }

  this.whatGestureDirection = (event) => {
    this.diffInPosition  = this.initalTouchPos - this.lastPosition;
    this.diffVsThreshold = Math.abs(this.diffInPosition) > this.threshold;
    (Math.sign(this.diffInPosition) > 0) ? this.gestureState = 'L' : (Math.sign(this.diffInPosition) < 0) ? this.gestureState = 'R' : this.gestureState = 'N';
    
    return [this.diffInPosition, this.diffVsThreshold, this.gestureState];
  }

  this.doSomething = (event) => {
    let [gestureDelta,gestureThreshold,gestureDirection] = this.whatGestureDirection();

    // USE THIS TO DEBUG
    console.log(gestureDelta,gestureThreshold,gestureDirection);

    if (gestureThreshold) {
      (gestureDirection == 'L') ? // LEFT ACTION : // RIGHT ACTION
    }
  }

  if (window.PointerEvent) {
    this.e.addEventListener('pointerdown', this.getTouchStart, true);
    this.e.addEventListener('pointermove', this.getTouchMove, true);
    this.e.addEventListener('pointerup', this.getTouchEnd, true);
    this.e.addEventListener('pointercancel', this.getTouchEnd, true);
  }
}

可以使用 new 调用该函数。

window.addEventListener('load', () => {
  let test = new getSwipeX({
    elementId: 'your_div_here'
  });
})
0赞 Segebee 7/31/2021 #22

我重新设计了@ruben-martinez的答案,使用@givanse的惊人解决方案来使用自定义react钩子处理滑动事件。

import React, { useEffect, useRef, useState } from "react";

export default function useSwiper() {
  const [domRef, setDomRef] = useState<any>();

  const xDown: React.MutableRefObject<number | null> = useRef(null);
  const yDown: React.MutableRefObject<number | null> = useRef(null);

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

function getTouches(event: React.TouchEvent<HTMLDivElement>) {
  return event.touches;
}

function handleTouchStart(event: any) {
  const firstTouch = getTouches(event)[0];
  xDown.current = firstTouch.clientX;
  yDown.current = firstTouch.clientY;
}

function handleTouchMove(event: React.TouchEvent<HTMLDivElement>) {
  if (!xDown.current || !yDown.current) return;

  const firstTouch = getTouches(event)[0];
  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)) {
    // handle horizontal swipes
    if (xDiff > 0) {
      // we swiped right
      console.log("right");
    } else {
      // we swiped left
      console.log("left");
    }
  } else {
    // handle vertical swipes
    if (yDiff > 0) {
      // we swiped down
      console.log("down");
    } else {
      // we swiped up
      console.log("up");
    }
  }
}

function handleTouchEnd(event: React.TouchEvent<HTMLDivElement>) {
  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, false);
    domRef.removeEventListener("touchmove", handleTouchMove, false);
    domRef.removeEventListener("touchend", handleTouchEnd, false);
};
  }, [domRef]);

  return (ref: any) => setDomRef(ref);
}

我在实现他的答案时面临的主要挑战是不知道如何将滑动元素的 ref 绑定到自定义钩子中的 ref。

基本上,我们从自定义钩子返回一个函数。这个函数将允许我们从我们想要侦听的元素中传入一个引用,以滑动操作。然后,收到 ref 的自定义钩子会使用元素的 ref 更新钩子状态,这会触发重新渲染,因此我们有了实际的元素!

这种函数式 ref 样式还允许我们将钩子用于多个元素。如下图所示,我想将它用于项目列表,以启用滑动以删除:)

import useSwiper from "./hooks/useSwipe";

const EntryCard = ({ entry, godMode, reload }: EntryProps) => {
const swiperRef = useSwiper();

const handleEntryClick =
(entry: Entry) => async (event: React.MouseEvent<HTMLDivElement>) => {
  if (!godMode) return;

  try {
    reload((state) => !state);
  } catch (err) {
    console.log("Error deleting entry: ", err);
  }
};

return (
  <div className="item" onClick={handleEntryClick(entry)} ref={swiperRef}>
    <div className="username">{entry.userName}</div>
    <div className="score">{entry.weekScore}</div>
  </div>
 );
};

PS:您可以将函数传入钩子以接收滑动值。谢谢:)如果您喜欢,请投票:)

0赞 javad hemati 12/11/2021 #23

通过 touchStart 和 touchEnd 处理:

var handleSwipe = function(elem,callbackOnRight, callbackOnLeft, callbackOnDown, 
      callbackOnUp) => {

        elem.ontouchstart = handleTouchStart;
        elem.ontouchend = handleTouchEnd;

        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 handleTouchEnd(evt) {
            if (!xDown || !yDown) {
                return;
            }

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

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

            console.log(`xDiff:${xDiff}, yDiff:${yDiff}`);

            if (Math.abs(xDiff) > Math.abs(yDiff)) {
                if (xDiff > minDif) {
                    if (callbackOnLeft)
                        callbackOnLeft();
                } else if (xDiff < -1 * minDif){
                    if (callbackOnRight)
                        callbackOnRight();
                }
            } else {
                if (yDiff > minDif) {
                    if (callbackOnDown)
                        callbackOnDown();
                } else if (yDiff < -1* minDif){
                    if (callbackOnUp)
                        callbackOnUp();
                }
            }
            
            xDown = null;
            yDown = null;
        };
    }
0赞 zurfyx 6/5/2022 #24

您可以监听事件,并根据事件数据(Codepen)计算方向和力:touchstarttouchend

let start = null;
document.addEventListener('touchstart', e => {
  const touch = e.changedTouches[0];
  start = [touch.clientX, touch.clientY];
});
document.addEventListener('touchend', e => {
  const touch = e.changedTouches[0];
  const end = [touch.clientX, touch.clientY];
  document.body.innerText = `${end[0] - start[0]},${end[1] - start[1]}`;
});
Swipe here

或者,您可以围绕相同的概念(Codepen)构建一个更符合人体工程学的 API:

const removeListener = addSwipeRightListener(document, (force, e) => {
  console.info('Swiped right with force: ' + force);
});
// removeListener()

// swipe.js
const {
  addSwipeLeftListener,
  addSwipeRightListener,
  addSwipeUpListener,
  addSwipeDownListener,
} = (function() {
  // <element, {listeners: [...], handleTouchstart, handleTouchend}>
  const elements = new WeakMap();
  
  function readTouch(e) {
    const touch = e.changedTouches[0];
    if (touch == undefined) {
      return null;
    }
    return [touch.clientX, touch.clientY];
  }

  function addListener(element, cb) {
    let elementValues = elements.get(element);
    if (elementValues === undefined) {
      const listeners = new Set();
      const handleTouchstart = e => {
        elementValues.start = readTouch(e);
      };
      const handleTouchend = e => {
        const start = elementValues.start;
        if (start === null) {
          return;
        }
        const end = readTouch(e);
        for (const listener of listeners) {
          listener([end[0] - start[0], end[1] - start[1]], e);
        }
      };
      element.addEventListener('touchstart', handleTouchstart);
      element.addEventListener('touchend', handleTouchend);
      
      elementValues = {
        start: null,
        listeners,
        handleTouchstart,
        handleTouchend, 
      };
      elements.set(element, elementValues);
    }
    elementValues.listeners.add(cb);
    return () => deleteListener(element, cb);
  }
  
  function deleteListener(element, cb) {
    const elementValues = elements.get(element);
    const listeners = elementValues.listeners;
    listeners.delete(cb);
    if (listeners.size === 0) {
      elements.delete(element);
      element.removeEventListener('touchstart', elementValues.handleTouchstart);
      element.removeEventListener('touchend', elementValues.handleTouchend);
    }
  }
  
  function addSwipeLeftListener(element, cb) {
    return addListener(element, (force, e) => {
      const [x, y] = force;
      if (x < 0 && -x > Math.abs(y)) {
        cb(x, e);
      }
    });
  }

  function addSwipeRightListener(element, cb) {
    return addListener(element, (force, e) => {
      const [x, y] = force;
      if (x > 0 && x > Math.abs(y)) {
        cb(x, e);
      }
    });
  }
  
  function addSwipeUpListener(element, cb) {
    return addListener(element, (force, e) => {
      const [x, y] = force;
      if (y < 0 && -y > Math.abs(x)) {
        cb(x, e);
      }
    });
  }
  
  function addSwipeDownListener(element, cb) {
    return addListener(element, (force, e) => {
      const [x, y] = force;
      if (y > 0 && y > Math.abs(x)) {
        cb(x, e);
      }
    });
  }

  return {
    addSwipeLeftListener,
    addSwipeRightListener,
    addSwipeUpListener,
    addSwipeDownListener,
  }
})();

// app.js

function print(direction, force) {
  document.querySelector('#direction').innerText = direction;
  document.querySelector('#data').innerText = force;
}

addSwipeLeftListener(document, (force, e) => {
  print('left', force);
});

addSwipeRightListener(document, (force, e) => {
  print('right', force);
});

addSwipeUpListener(document, (force, e) => {
  print('up', force);
});

addSwipeDownListener(document, (force, e) => {
  print('down', force);
});
<h1>Swipe <span id="direction"></span></h1>
Force (px): <span id="data"></span>

2赞 Thales Cardoso 10/31/2022 #25

除了这里建议的内容之外,我会跟踪手指数字,因为如果你同时用两根手指触摸,它会在没有 ~swipe~ 运动的情况下拾取 X 位置,这会导致奇怪的行为,而且,你可能想要设置一个“距离”最小值,这样用户就不会在触摸您的网站或应用程序时错误地触发滑动。

//Swipe 
let touchstartX = 0
let touchendX = 0
let fingerCount = 0
    
const checkDirection = () => {

  const distance = 50 //Minimum distance for the swipe to work

  //left
  if (touchendX < touchstartX && (touchstartX - touchendX) > distance ){

  //Do something cool
   
  } 
  //right
  if (touchendX > touchstartX && (touchendX - touchstartX) > distance){

    //Do something cooler
}

document.addEventListener('touchstart', e => {

    fingerCount = e.touches.length
    touchstartX = e.changedTouches[0].clientX  
     
})

document.addEventListener('touchend', e => {

    touchendX = e.changedTouches[0].clientX
    if(fingerCount === 1){ 
        checkDirection() 
    }

})
-1赞 Dima_pos 11/15/2022 #26

功能检查水平和垂直方向,以确定哪个滑动时间更长,以防止执行 2 条指令,因为不可能进行完美的单向滑动。滑动总是在 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();
});
0赞 Pierre 3/15/2023 #27

这是。理想情况下,如果滑动不够重要,您希望跳过滑动操作。JQuery

$('.slider')
    .off('touchstart touchend swipedaction')
    .on('touchstart', function (e) {
        //Set the starting point directly on self
        $(this).touchstartX = e.changedTouches[0].screenX;
    })
    .on('touchend', function (e) {
        //Set the end point directly on self
        let $self = $(this);
        $self.touchendX = e.changedTouches[0].screenX;

        // Swipe more than 50px, else don't action it.
        if (Math.abs($self.touchendX - $self.touchstartX) > 50) {
            if ($self.touchendX < $self.touchstartX) {
                $self.trigger('swipedaction', ['left']);
            } else {
                $self.trigger('swipedaction', ['right']);
            }
        } else {
            e.stopPropagation();
        }
    })
    .on('swipedaction', function(e, direction) {
        if (direction === 'left') {
            // Swiped left, move right
        } else {
            // Swiped right, move left
        }
    });

评论

0赞 limciana 5/11/2023
我在 上遇到了一个未定义的错误,但将其更改为工作 (stackoverflow.com/a/46790285/6247322)。此外,不会存储在这里。我必须在侦听器之外创建一个变量(即),然后在我设置变量e.changedTouches[0]e.originalEvent.changedTouches[0]touchstartX$(this).touchstartX = e.changedTouches[0].screenX;let $self.on('touchstart', function(e) {$self = $(this); $self.touchStartX = e.originalEvent.changedTouches[0].screenX;
1赞 Pierre 5/12/2023
如果是这种情况,您也可以将 和 替换为选择器。它将变量存储在元素的 JQuery 实例上。对于您遇到的未定义错误,最好在使用它之前检查两者。$(this)$self$('.slider')e.changedTouchese.originalEvent.changedTouches
-1赞 Robin Hossain 3/24/2023 #28
class Carousel {
    constructor(carouselWrapper, carouselItems, carouselPrev, carouselNext, dotContainer) {
        this.carouselWrapper = document.querySelectorAll(carouselWrapper);
        this.carouselItems = Array.from(document.querySelectorAll(carouselItems));
        this.carouselPrev = document.querySelector(carouselPrev);
        this.carouselNext = document.querySelector(carouselNext);
        this.dotContainer = document.querySelector(dotContainer);
        this.currentItem = 0;
        this.maxItem = this.carouselItems.length;

        this.isDragging = false;
        this.startPos = 0;
        this.currentTranslate = 0;
        this.prevTranslate = 0;

        this.#init();
    }

    #init() {
        document.addEventListener('keydown', this.#keyBoardHandler.bind(this));

        this.carouselPrev.addEventListener('click', this.#prevSlide.bind(this));
        this.carouselNext.addEventListener('click', this.#nextSlide.bind(this));
        this.dotContainer.addEventListener('click', this.#dotHandler.bind(this))

        this.#createDots();
        this.#gotoSlide(0)
        this.#activeDots(0)

        this.#touchHandler();
        this.#disableoncontextmenu();

    }

    #touchHandler() {
        this.carouselItems.forEach((slide, index) => {
            const img = slide.querySelector('.carousel__bgimg');
            if(!img) return;
            
            img.addEventListener('dragstart', (e) => e.preventDefault());

            slide.addEventListener('touchstart', this.#touchStart.bind(this));
            slide.addEventListener('touchend', this.#touchEnd.bind(this));
            slide.addEventListener('touchmove', this.#touchMove.bind(this));

        });
    }

    #touchStart() {
        this.isDragging = true;
        this.startPos = this.#getpositionX(event);
    }

    #touchMove() {
        if(this.isDragging) {
            const currentPosition = this.#getpositionX(event);
            this.currentTranslate = this.prevTranslate + currentPosition - this.startPos;
        }
    }

    #touchEnd() {
        this.isDragging = false;
        const movedBy = this.currentTranslate - this.prevTranslate;
        if(movedBy < -100) {
            this.#nextSlide();
        };

        if(movedBy > 100) {
            this.#prevSlide();
        };

    }

    #getpositionX(event) {
        return event.type.includes('mouse') ? event.pageX : event.touches[0].clientX;
    }

    #createDots() {
        this.carouselItems.forEach((_, i) => {
            this.dotContainer.insertAdjacentHTML('beforeend', `<div class="bullet" data-slide="${i}"></div>`)
        });
    }

    #activeDots(slide) {
        document.querySelectorAll('.bullet').forEach(function(dot) {
            dot.classList.remove('active');
        });

        document.querySelector(`.bullet[data-slide="${slide}"]`)
            .classList.add('active');
    }

    #gotoSlide(slide) {
        this.carouselWrapper.forEach((s, i) => {
            s.style.transform = `translate3d(${100 * (i - slide)}%, 0px, 0px)`;
        });
    }

    #prevSlide() {
        if(this.currentItem === 0) {
            this.currentItem = this.maxItem - 1;
        } else {
            this.currentItem--;
        };
        this.#gotoSlide(this.currentItem);
        this.#activeDots(this.currentItem);
    }

    #nextSlide() {
        if(this.currentItem === this.maxItem -1) {
            this.currentItem = 0;
        } else {
            this.currentItem++;
        };
        this.#gotoSlide(this.currentItem);
        this.#activeDots(this.currentItem);
    }

    #dotHandler(e) {
        if(e.target.classList.contains('bullet')) {
            const { slide } = e.target.dataset;
            this.#gotoSlide(slide);
            this.#activeDots(slide);
        }
    }

    #keyBoardHandler(e) {
        if(e.keyCode === 39) this.#nextSlide();
        e.keyCode === 37 && this.#prevSlide();
    }

    #disableoncontextmenu() {
        this.carouselWrapper.forEach(function(item) {
            item.oncontextmenu = function(event) {
                event.preventDefault()
                event.stopPropagation()
                return false
            }
        });
    }
}

document.addEventListener('DOMContentLoaded', function() {
    const slider = new Carousel(
        '.carousel__wrapper',
        '.carousel__item',
        '.prev-ctrl',
        '.next-ctrl',
        '.dots',
        );
});