如何为移动设备实现滑动手势?

49
我有一个使用AngularJS制作的应用程序,其中包含箭头键导航以切换视图。
我想使用滑动来实现触摸设备上的导航。我尝试了jGestures库,但它不适合滑动操作。 我已被建议不要使用jquery mobile
是否有其他方法可以实现滑动?
编辑: 它不能清晰地检测到滑动。我在多个设备上进行了测试,包括iPad,在我的情况下需要多次滑动才能完成一个操作(路由)。

你可能想详细说明一下你在使用jGestures时遇到的问题。我自己没有使用过它,但它看起来是我为简单手势支持编写的自定义代码的强大版本(也见于其他地方)。 - Tim M.
2
我被建议不要使用jquery mobile。为什么? - givanse
8个回答

67

我为自己的需求编写了这个函数。

欢迎使用。在移动设备上表现很棒。

function detectswipe(el,func) {
  swipe_det = new Object();
  swipe_det.sX = 0; swipe_det.sY = 0; swipe_det.eX = 0; swipe_det.eY = 0;
  var min_x = 30;  //min x swipe for horizontal swipe
  var max_x = 30;  //max x difference for vertical swipe
  var min_y = 50;  //min y swipe for vertical swipe
  var max_y = 60;  //max y difference for horizontal swipe
  var direc = "";
  ele = document.getElementById(el);
  ele.addEventListener('touchstart',function(e){
    var t = e.touches[0];
    swipe_det.sX = t.screenX; 
    swipe_det.sY = t.screenY;
  },false);
  ele.addEventListener('touchmove',function(e){
    e.preventDefault();
    var t = e.touches[0];
    swipe_det.eX = t.screenX; 
    swipe_det.eY = t.screenY;    
  },false);
  ele.addEventListener('touchend',function(e){
    //horizontal detection
    if ((((swipe_det.eX - min_x > swipe_det.sX) || (swipe_det.eX + min_x < swipe_det.sX)) && ((swipe_det.eY < swipe_det.sY + max_y) && (swipe_det.sY > swipe_det.eY - max_y) && (swipe_det.eX > 0)))) {
      if(swipe_det.eX > swipe_det.sX) direc = "r";
      else direc = "l";
    }
    //vertical detection
    else if ((((swipe_det.eY - min_y > swipe_det.sY) || (swipe_det.eY + min_y < swipe_det.sY)) && ((swipe_det.eX < swipe_det.sX + max_x) && (swipe_det.sX > swipe_det.eX - max_x) && (swipe_det.eY > 0)))) {
      if(swipe_det.eY > swipe_det.sY) direc = "d";
      else direc = "u";
    }

    if (direc != "") {
      if(typeof func == 'function') func(el,direc);
    }
    direc = "";
    swipe_det.sX = 0; swipe_det.sY = 0; swipe_det.eX = 0; swipe_det.eY = 0;
  },false);  
}

function myfunction(el,d) {
  alert("you swiped on element with id '"+el+"' to "+d+" direction");
}

要使用这个函数,只需像这样使用它

detectswipe('an_element_id',myfunction);

detectswipe('an_other_element_id',my_other_function);

如果检测到滑动操作,将调用函数“myfunction”,并传递参数元素ID和“l,r,u,d”(左、右、上、下)。
例如:http://jsfiddle.net/rvuayqeo/1/
我(UlysseBN)基于此脚本制作了新版本,使用更现代的JavaScript,它在某些情况下表现得更好。如果您认为它应该是此答案的编辑,请告诉我,如果您是原始作者并最终进行了编辑,我将删除我的答案。

谢谢。我建议动态设置 min_xmin_x = Math.abs(swipe_det.eY - swipe_det.sY)。其他方向同理。因为当你向上滑动400像素,只向右滑动40像素时,你可能想要向上滑动。然而,你的代码会识别为向右滑动。 - Sadık
3
@EscapeNetscape,我喜欢您的滑动检测解决方案。但它会错误地将单击和双击识别为滑动操作。我该如何解决这个问题? - john_science
7
我已经更新了脚本,用了 javascript 1.8 和一些逻辑变化。例如,如果使用增量值,您实际上不需要 min_x、max_x、min_y 和 max_y。另外,通过比较增量值(abs((eX - sX) / (eY - sY))),选择垂直和水平会更精确。您可以在这里看到我的分支版本。如果这条评论得到足够的点赞(比如说 5 个),我将编辑回答。 - Ulysse BN
1
我仍然不能相信原生的jQuery没有适当的滑动事件处理。感谢那位贴出这段代码的善良心灵! - Moseleyi
@Faizan jsfiddle已经恢复正常了。由于进行了完全的重写,我自己提供了答案:https://dev59.com/F2Up5IYBdhLWcg3wvZZV#58719294。希望它能派上用场! - Ulysse BN
显示剩余3条评论

29

我使用了http://quojs.tapquo.com/,效果很好。听说HammerJS也有很多优点,我会在其他项目中尝试一下。感谢您的回复。 - fotuzlab
我编写了一个简单的服务,包装了Hammerjs。在Angular中使用效果很好。 - Stephen Chung
谢谢推荐!hammerjs与当前版本的JQuery兼容,无需迁移jquery.mobile。没有警告,没有错误。太棒了! - J-Roel

8

6

注意:受到EscapeNetscape的回答的启发,我使用现代JavaScript编辑了他的脚本,并在评论中进行了修改。由于用户的兴趣和4小时的jsfiddle.net宕机,我将此问题的答案公布出来。在您使用之前,建议先阅读一下这个detectSwipe函数。如果需要,请随意查看并编辑该答案。


这里有一个detectSwipe函数,工作得非常好(在我的一个网站上使用过)。建议在使用之前先阅读一下它。如有需要,可以自由审核并编辑答案。

// usage example
detectSwipe('swipeme', (el, dir) => alert(`you swiped on element with id ${el.id} to ${dir} direction`))

// source code

// Tune deltaMin according to your needs. Near 0 it will almost
// always trigger, with a big value it can never trigger.
function detectSwipe(id, func, deltaMin = 90) {
  const swipe_det = {
    sX: 0,
    sY: 0,
    eX: 0,
    eY: 0
  }
  // Directions enumeration
  const directions = Object.freeze({
    UP: 'up',
    DOWN: 'down',
    RIGHT: 'right',
    LEFT: 'left'
  })
  let direction = null
  const el = document.getElementById(id)
  el.addEventListener('touchstart', function(e) {
    const t = e.touches[0]
    swipe_det.sX = t.screenX
    swipe_det.sY = t.screenY
  }, false)
  el.addEventListener('touchmove', function(e) {
    // Prevent default will stop user from scrolling, use with care
    // e.preventDefault();
    const t = e.touches[0]
    swipe_det.eX = t.screenX
    swipe_det.eY = t.screenY
  }, false)
  el.addEventListener('touchend', function(e) {
    const deltaX = swipe_det.eX - swipe_det.sX
    const deltaY = swipe_det.eY - swipe_det.sY
    // Min swipe distance, you could use absolute value rather
    // than square. It just felt better for personnal use
    if (deltaX ** 2 + deltaY ** 2 < deltaMin ** 2) return
    // horizontal
    if (deltaY === 0 || Math.abs(deltaX / deltaY) > 1)
      direction = deltaX > 0 ? directions.RIGHT : directions.LEFT
    else // vertical
      direction = deltaY > 0 ? directions.UP : directions.DOWN

    if (direction && typeof func === 'function') func(el, direction)

    direction = null
  }, false)
}
#swipeme {
  width: 100%;
  height: 100%;
  background-color: orange;
  color: black;
  text-align: center;
  padding-top: 20%;
  padding-bottom: 20%;
}
<div id='swipeme'>
  swipe me
</div>


我已经看了你的代码,但我没有发现与原始答案之间有太大的区别。 你能解释一下你做了哪些更改吗?我的应用程序被旧版和新版浏览器上的人使用,所以我认为你答案中的现代JS不会在所有浏览器上都有效。顺便说一句,在原始答案中注释掉e.preventDefault()这一行后,错误似乎已经消失了,我需要这条线吗,还是可以将其作为注释留下来?请确认一下。我正在添加滑动功能的div不需要水平滚动,所以如果我将那一行注释掉就可以了,请再次确认一下。 - Faizan
能否检测到我们可以向上滑动,但无法再向上移动屏幕,也就是已经到达了顶部?我正在尝试解决一个问题,即无法在页面上检测到滚动,但由于您的逻辑,可以检测到滑动,但我需要找出是否无法再通过滑动向上移动,也就是容器元素的顶部在视野中。 - Siva Bathula

6

到目前为止,插件很好用,因为我在使用jquery mobile时遇到了问题! - UnpassableWizard

2

我喜欢你的解决方案并在我的网站上实现了它 - 然而,进行了一些小改进。只是想分享一下我的代码:

function detectSwipe(id, f) {
    var detect = {
        startX: 0,
        startY: 0,
        endX: 0,
        endY: 0,
        minX: 30,   // min X swipe for horizontal swipe
        maxX: 30,   // max X difference for vertical swipe
        minY: 50,   // min Y swipe for vertial swipe
        maxY: 60    // max Y difference for horizontal swipe
    },
        direction = null,
        element = document.getElementById(id);

    element.addEventListener('touchstart', function (event) {
        var touch = event.touches[0];
        detect.startX = touch.screenX;
        detect.startY = touch.screenY;
    });

    element.addEventListener('touchmove', function (event) {
        event.preventDefault();
        var touch = event.touches[0];
        detect.endX = touch.screenX;
        detect.endY = touch.screenY;
    });

    element.addEventListener('touchend', function (event) {
        if (
            // Horizontal move.
            (Math.abs(detect.endX - detect.startX) > detect.minX)
                && (Math.abs(detect.endY - detect.startY) < detect.maxY)
        ) {
            direction = (detect.endX > detect.startX) ? 'right' : 'left';
        } else if (
            // Vertical move.
            (Math.abs(detect.endY - detect.startY) > detect.minY)
                && (Math.abs(detect.endX - detect.startX) < detect.maxX)
        ) {
            direction = (detect.endY > detect.startY) ? 'down' : 'up';
        }

        if ((direction !== null) && (typeof f === 'function')) {
            f(element, direction);
        }
    });
}

使用方式如下:

detectSwipe('an_element_id', myfunction);

或者

detectSwipe('another_element_id', my_other_function);

如果检测到滑动操作,则调用函数myfunction,并带有参数element-id和'left''right''up''down'

非常敏锐!谢谢,已经修改了条件。 - Konrad Holl

2

锤子时间!

我使用了Hammer JS,并且它能够与手势一起工作。从这里阅读详细信息:https://hammerjs.github.io/

好消息是,它比jQuery mobile更轻量级和快速。您也可以在他们的网站上进行测试。


2
这不是和4年前的2013年上面的答案插入了相同的库吗? - KyleMit

0

我看了几个解决方案,但都因为滚动和选择文本而失败,这是最大的困惑。我不是向右滚动,而是关闭框等。

我刚刚完成了我的实现,它可以为我完成所有操作。

https://github.com/webdevelopers-eu/jquery-dna-gestures

这是MIT许可,所以你可以随意使用 - 而且它确实非常简单 - 经过压缩只有800字节。你可以在我的(正在开发中的)网站https://cyrex.tech上查看 - 在触摸设备上向右滑动应该可以关闭弹出窗口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接