移动端touch事件

  • touchstart
  • touchmove
  • touchend

事件点透

mouse 事件在移动端可以执行,但是要注意点透问题

  • 在移动端中,手指触碰时,如果有 touch 事件,会立即执行,
  • 并记录触碰坐标,在一定时间延迟后(300ms以内),在该坐标上查找元素,如果元素有mouse事件,就执行。
  • 可以使用e.preventDefault();解决

    1. box.addEventListener("touchstart",(e)=>{
    2. e.preventDefault();
    3. box.style.display = "none";
    4. })

    touchEvent 事件参数

  • touches 当前屏幕上的手指

  • targetTouches 当前元素上的手指
  • changedTouches 触发当前事件的手指列表
    1. let box = document.querySelector("#box");
    2. document.addEventListener("touchstart",(e)=>{
    3. e.preventDefault();
    4. },{passive:false})
    5. box.addEventListener("touchmove",(e)=>{
    6. box.innerHTML = `
    7. 当前屏幕上的手指个数:${e.touches.length}<br/>
    8. 当前元素上的手指个数:${e.targetTouches.length}<br/>
    9. 触发当前事件的手指个数:${e.changedTouches.length}
    10. `;
    11. });

    划屏操作

    和web端的鼠标拖拽是一个思路
    1. let wrap = document.querySelector("#wrap");
    2. let list = document.querySelector(".list");
    3. let startPoint = 0;
    4. let startPosition = 0;
    5. let translateY = 0;
    6. wrap.addEventListener("touchstart", (e) => {
    7. console.log(e.changedTouches[0])
    8. startPoint = e.changedTouches[0].pageY;
    9. startPosition = translateY;
    10. });
    11. wrap.addEventListener("touchmove", (e) => {
    12. let nowPoint = e.changedTouches[0].pageY;
    13. let disPoint = nowPoint - startPoint;
    14. translateY = startPosition + disPoint;
    15. list.style.transform = `translateY(${translateY}px)`;
    16. });

    划屏的技巧

    回弹动画幻灯片

  1. 当滑动距离超过 wrap.width*threshold 就进行切换,否则还是显示当前张
    1. // ...
    2. let threshold = .3;//当滑动距离超过 wrap.width*threshold 就进行切换,否则还是显示当前张
    3. let wrapW = wrap.clientWidth;
    4. wrap.addEventListener("touchend", (e) => {
    5. // ...
    6. // 判断滑动的距离是多少,大于某个节点时,让它切换到上一张或下一张
    7. if (Math.abs(disPoint.x) > threshold * wrapW) {
    8. //需要进行切换
    9. now -= disPoint.x / Math.abs(disPoint.x);
    10. }
    11. //根据 now 重新计算位置
    12. translateX = -now * wrapW;
    13. list.style.transition = ".3s";
    14. list.style.transform = `translateX(${translateX}px)`;
    15. });

无缝滚动

前后各补一张卡,或者说直接赋值两个图片列表

  1. {
  2. let wrap = document.querySelector("#wrap");
  3. let list = document.querySelector("#list");
  4. let navs = [...document.querySelectorAll(".nav a")];
  5. let startPoint = {};//记录摁下时手指坐标
  6. let startX = 0;//记录摁下时元素位置
  7. let translateX = 0; //记录元素位移值
  8. let now = 0;//记录当前显示的时第几张
  9. let threshold = .3;//当滑动距离超过 wrap.width*threshold 就进行切换,否则还是显示当前张
  10. let wrapW = wrap.clientWidth;
  11. list.innerHTML += list.innerHTML;
  12. wrap.addEventListener("touchstart", (e) => {
  13. list.style.transition = "none";
  14. let touches = e.changedTouches[0];
  15. startPoint = {
  16. x: touches.pageX,
  17. y: touches.pageY
  18. };
  19. if (now === 0) { //第0组的第0张
  20. now = navs.length; //第1组第0张
  21. } else if (now === navs.length * 2 - 1) {//第1组最后一张
  22. now = navs.length - 1;//第0组最后一张;
  23. }
  24. translateX = -now * wrapW;
  25. list.style.transform = `translateX(${translateX}px)`;
  26. startX = translateX;
  27. });
  28. wrap.addEventListener("touchmove", (e) => {
  29. let touches = e.changedTouches[0];
  30. let nowPoint = {
  31. x: touches.pageX,
  32. y: touches.pageY
  33. };
  34. let disPoint = {
  35. x: nowPoint.x - startPoint.x,
  36. y: nowPoint.y - startPoint.y
  37. };
  38. translateX = startX + disPoint.x;
  39. list.style.transform = `translateX(${translateX}px)`;
  40. });
  41. wrap.addEventListener("touchend", (e) => {
  42. let touches = e.changedTouches[0];
  43. let nowPoint = {
  44. x: touches.pageX,
  45. y: touches.pageY
  46. };
  47. let disPoint = {
  48. x: nowPoint.x - startPoint.x,
  49. y: nowPoint.y - startPoint.y
  50. };
  51. // 判断滑动的距离是多少,大于某个节点时,让它切换到上一张或下一张
  52. if (Math.abs(disPoint.x) > threshold * wrapW) {
  53. //需要进行切换
  54. now -= disPoint.x / Math.abs(disPoint.x);
  55. }
  56. //根据 now 重新计算位置
  57. translateX = -now * wrapW;
  58. list.style.transition = ".3s";
  59. list.style.transform = `translateX(${translateX}px)`;
  60. navs.forEach(item => {
  61. item.classList.remove("active");
  62. });
  63. navs[now % navs.length].classList.add("active");
  64. });
  65. }

滑动方向的处理

上下左右滑动可能会产生冲突,设置为只能同时滑动一个方向

  • 当用户想要进行左右滑动时,就只是左右可以滑动(禁止上下滑动)
  • 当用户,想要进行上下滑动时,把左右滑动禁止了
  • 当滑动方向一旦判定,则滑动过程中就一直认定用户想要做该方向的滑动,知道下次再触发滑动时,重新判断。
  1. let isDir = false; // 判断用户是否认定方向
  2. let isMove = true;// 判读是否进行幻灯片切换
  3. wrap.addEventListener("touchmove", (e) => {
  4. // ...
  5. if (!isDir) {
  6. if (Math.abs(disPoint.x) - Math.abs(disPoint.y) > 5) {
  7. //console.log("左右滑动");
  8. isDir = true;
  9. } else if (Math.abs(disPoint.y) - Math.abs(disPoint.x) > 5) {
  10. //console.log("上下滑动");
  11. isDir = true;
  12. isMove = false;
  13. }
  14. }
  15. if (isMove) {
  16. isDir && (translateX = startX + disPoint.x);
  17. isDir && (list.style.transform = `translateX(${translateX}px)`);
  18. e.preventDefault();
  19. }
  20. });

阻止默认事件

如果要给 document、html、body这些元素的touch事件中阻止默认事件,一定记得设置,passive:false (允许阻止默认事件)

  1. let box = document.querySelector("#box");
  2. box.addEventListener("touchstart",(e)=>{
  3. box.style.display = "none";
  4. });
  5. document.body.addEventListener("touchstart",(e)=>{
  6. e.preventDefault();
  7. },{
  8. passive: false
  9. });

阻止 touchstart 默认事件带来的影响

  1. 默认双指缩放被禁止
  2. 滚动条被禁止
  3. 鼠标事件被禁止(包括a标签的href)
  4. 禁止长按菜单弹出
  5. 阻止元素获得焦点和失去焦点

阻止 touchmove 默认事件带来的影响

  1. 默认双指缩放被禁止
  2. 滚动条被禁止

其他问题

  • 当事件中已经触发了滚动条滚动,则不能再阻止默认事件
  • 事件本身不阻止默认,但是中阻止默认事件,会引发报错,并且阻止默认事件不生效
    1. list.addEventListener("touchstart",()=>{
    2. setTimeout(()=>{
    3. isOff = true;
    4. },1000);
    5. });
    6. list.addEventListener("touchmove",(e)=>{
    7. if(isOff){
    8. e.preventDefault(); // 无法阻止
    9. }
    10. console.log(1);
    11. });
    image.png

    better-scroll