当前位置:Gxlcms > JavaScript > 原生js实现移动端Touch滑动反弹的方法(代码示例)

原生js实现移动端Touch滑动反弹的方法(代码示例)

时间:2021-07-01 10:21:17 帮助过:17人阅读

本篇文章给大家带来的内容是关于原生js实现移动端Touch滑动反弹的方法(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

移动端 Touch 滑动反弹

什么是Touch滑动?就是类似于PC端的滚动事件,但是在移动端是没有滚动事件的,所以就要用到Touch事件结合js去实现,效果如下:

49134015-5c37ff2e33c57_articlex.gif

1. 准备工作

什么是移动端的Touch事件?在移动端Touch事件可以细分成三种,分别是:touchstart、touchmove和touchend,并且touch事件必须要用addEventListener去监听。

touchStart当手指触碰到屏幕的时候触发

touchmove当手指在屏幕上不断移动的时候触发

touchend当手指离开屏幕的时候触发

Touch 事件触发的 Event 对象:

  1. // 手指触碰到屏幕时触发
  2. element.addEventListener('touchstart', function (e) {
  3. // 打印的事件对象
  4. console.log(e);
  5. })

3474418864-5c37ff2dc4db4_articlex.jpg

changedTouches、targetTouches、touches都是伪数组,里面装的是手指列表

三种返回对象的区别:

其实这三种返回的对象,都是表示用户触摸事件时的手指信息,之所以是一个伪数组,是因为有可能出现多指同时触摸,但是在实际工作中一般不去考虑多指的情况。而它们唯一的区别就是在touchstart和touchmove事件的时候,changedTouches、targetTouches、touches都能获取到手指的信息,但是在touchend事件的时候,targetTouches、touches对象是不能返回离开屏幕时的手指信息,只有changedTouches对象能返回。

有哪些手指信息?

我们可以看下上面的图片,在changedTouche[0]中,有些值:
  1. clientX:74 // 触摸点相对于浏览器的 viewport 左边缘的 x 坐标,不会包括左边的滚动距离。
  2. clientY:73 // 触摸点相对于浏览器的 viewport 上边缘的 Y 坐标,不会包括上边的滚动距离。
  3. screenX:2202 // 触摸点相对于屏幕左边缘的 x 坐标。
  4. screenY:327 // 触摸点相对于屏幕上边缘的 Y 坐标。
  5. pageX:65 // 触摸点相对于 document 的左边缘的 x 坐标,包括左边的滚动距离
  6. pageY:18 // 触摸点相对于 document 的上边缘的 Y 坐标,包括左边的滚动距离

2. 基本结构

此案例模拟的是移动端的一种滑动菜单效果。

HTML部分:

  1. <aside class="main">
  2. <div class="draw" id="draw">
  3. <ul>
  4. <li style="background:orange">列表一</li>
  5. <li style="background:yellowgreen">列表二</li>
  6. <li style="background:yellow">列表三</li>
  7. <li style="background:cyan">列表四</li>
  8. <li style="background:orangered">列表五</li>
  9. <li style="background:pink">列表六</li>
  10. <li style="background:red">列表七</li>
  11. <li style="background:purple">列表八</li>
  12. <li style="background:violet">列表九</li>
  13. <li style="background:brown">列表十</li>
  14. </ul>
  15. </div>
  16. </aside>

css部分:

在列表的父盒子上设定一个overflow: hidden属性,使超出盒子部分的列表暂时隐藏掉,后面会通过js去实现滑动。

  1. /* 样式初始化 */
  2. * {
  3. margin: 0;
  4. padding: 0;
  5. }
  6. html,
  7. body {
  8. width: 100%;
  9. }
  10. aside {
  11. height: 100%;
  12. width: 100%;
  13. }
  14. /* 列表的父盒子,限制宽高 */
  15. /* 注意设置overflow: hidden;样式后,超出这个盒子的ul将不会显示 */
  16. .draw {
  17. width: 60px;
  18. height: 500px;
  19. border: 2px solid #ccc;
  20. overflow: hidden;
  21. position: fixed;
  22. left: 10px;
  23. top: 50%;
  24. transform: translateY(-50%);
  25. }
  26. /* li 设置了浮动, 所以 ul 要清除浮动 */
  27. ul:after {
  28. content: "";
  29. display: block;
  30. visibility: hidden;
  31. height: 0;
  32. clear: both;
  33. }
  34. ul {
  35. zoom: 1;
  36. }
  37. li {
  38. list-style: none;
  39. float: left;
  40. width: 60px;
  41. height: 60px;
  42. line-height: 60px;
  43. text-align: center;
  44. }

效果图:

943981048-5c37ff2d9680e_articlex.jpg

3. 首次滑动

手指触摸到列表向下滑动的时候,列表应该跟着向下滑动,当手指离开屏幕的时候,列表应该停在滑动的位置。这里就会用到上面准备阶段的知识点了,不明白的可以参考上面的概念。

实现原理:

1、touchstart的时候,获取手指触摸的落点A,通过这个点对象里面的clientY属性,获取距离顶部可视区的距离;

2、touchmove的时候,获取手指的点B,同样的获取移动时距离顶部可视区的距离;

3、touchmove的时候,还要做另一件事情,就是获取两点的差值(B.clientY-A.clientY),将这个差值动态赋值给ul,ul只需要设置向Y轴方向偏移这个距离,就能实现列表随手指滑动

先来张示意图,怎么通过 js 让列表滑动起来:

3840844191-5c37ff2d8f2d8_articlex.jpg

示例代码:

  1. var draw = document.querySelector('#draw');
  2. var ul = draw.children[0];
  3. // touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离
  4. var startY = 0
  5. ul.addEventListener('touchstart', function (e) {
  6. startY = e.changedTouches[0].clientY;
  7. })
  8. // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  9. ul.addEventListener('touchmove', function (e) {
  10. // 获取差值
  11. var dy = e.changedTouches[0].clientY - startY;
  12. // 设置 ul 在 Y 轴上的偏移
  13. ul.style.transform = 'translateY(' + dy + 'px)';
  14. })

效果图:

745534013-5c37ff2d96e73_articlex.gif

4. 再次滑动

上面的效果图,细心的朋友可能已经发现了问题,在第一次的时候,得到了我们想要的效果,但是在第二次的时候,我们继续向下移动了一段距离,但是ul并没有接着第一次的位置继续向下,而是瞬间跳了上去。

问题分析:

虽然第二次是继续向下移动了一段距离,但是触摸结束后,最终是将此时的差值,重新赋值给了ul的Y轴偏移,所以视觉上“跳了上去”。

3135543843-5c37ff2d6b7c5_articlex.jpg

解决方法:

每一次滑动结束之后,都应该记录下此次滑动的距离,与之前的进行累加,待下一次滑动的时候,ul在Y轴的偏移值应该是之前的距离加上本次滑动的距离。

新增touchend事件,在该事件里同样的可以获取到本次滑动的距离,将它与上一次的距离相加,赋值给一个全局变量;

在touchmove事件里有点小改动,就是在给ul设置偏移值的时候,除了本次滑动的差值还要加上这个上一次的值;

示意图:

3478683074-5c37ff2d79d48_articlex.jpg

示例代码:

  1. var draw = document.querySelector('#draw');
  2. var ul = draw.children[0];
  3. var startY = 0 // 刚触碰到屏幕的时的手指信息
  4. var centerY = 0 // 用来记录每次触摸时上一次的偏移距离
  5. // touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离
  6. ul.addEventListener('touchstart', function (e) {
  7. startY = e.changedTouches[0].clientY;
  8. })
  9. // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  10. ul.addEventListener('touchmove', function (e) {
  11. // 获取差值
  12. var dy = e.changedTouches[0].clientY - startY;
  13. // 上次的滑动距离加上本次的滑动距离
  14. var tempY = centerY + dy;
  15. // 设置 ul 在 Y 轴上的偏移
  16. ul.style.transform = 'translateY(' + tempY + 'px)';
  17. })
  18. // touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  19. ul.addEventListener('touchend', function (e) {
  20. // 获取差值
  21. var dy = e.changedTouches[0].clientY - startY;
  22. // 记录移动的距离
  23. centerY = centerY + dy;
  24. })

效果图:

2653866555-5c37ff2e0c00a_articlex.gif

5. 限制滑动区间

到上面一步,我们已经可以实现列表的滑动了,但是也存在一个问题,就是向上或者向下的时候没有限制,上下可以无限的滑动,甚至再用点力,就看不到列表了。为了美观和实用,这样肯定不行的,需要给它设定一个区间,设定向上或者向下最多只能留白多少。

1320041135-5c37ff2d13429_articlex.jpg

限制向下滑动最大区间:

设定向下最大区间的值比较简单,直接设定一个值,当上一次滑动的距离加上本次滑动的距离大于这个值的时候,就不让它再继续往下滑了,让他直接等于这个设定的值。

示例代码:

  1. var maxDown = 50; // 设定一个最大向下滑动的距离
  2. // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  3. ul.addEventListener('touchmove', function (e) {
  4. // 获取差值
  5. var dy = e.changedTouches[0].clientY - startY;
  6. // 上次的滑动距离加上本次的滑动距离
  7. var tempY = centerY + dy;
  8. // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候
  9. if (tempY > maxDown) {
  10. // 直接让偏移的值 等于这个设定值
  11. tempY = maxDown;
  12. }
  13. // 设置 ul 在 Y 轴上的偏移
  14. ul.style.transform = 'translateY(' + tempY + 'px)';
  15. })

限制向上滑动最大区间:

向上滑动时,当ul的底部距盒子底部的距离大于设定值的时候,不让其继续向上滑动,关键是这个值怎么去判断?

2340743329-5c37ff2ce2270_articlex.jpg

求出向上滑动最大值:

3003373871-5c37ff2ce946b_articlex.jpg

注意:因为ul是向上滑动的,所以求得的距离前面要加上一个负号(-

示例代码:

  1. // 设定一个最大向下滑动的距离
  2. var maxDown = 50;
  3. // 求得一个最大向上滑动的距离
  4. var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown);
  5. // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  6. ul.addEventListener('touchmove', function (e) {
  7. // 获取差值
  8. var dy = e.changedTouches[0].clientY - startY;
  9. // 上次的滑动距离加上本次的滑动距离
  10. var tempY = centerY + dy;
  11. // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候
  12. if (tempY > maxDown) {
  13. tempY = maxDown;
  14. }
  15. // 当上次滑动的距离加上本次滑动的距离 小于 设定的最大向上距离的时候
  16. else if (tempY < maxUp) {
  17. // 直接让偏移的值 等于这个设定值
  18. tempY = maxUp;
  19. }
  20. // 设置 ul 在 Y 轴上的偏移
  21. ul.style.transform = 'translateY(' + tempY + 'px)';
  22. })

效果图:

1193236174-5c37ff2d8ee0c_articlex.gif

认真观察上图,虽然成功的设置了最大滑动区间,但是你有没有发现,一直往一个方向滑动的时候,虽然列表不会继续往下滑动,但是接着往相反方向滑动的时候,感觉列表滑不动,需要滑一段距离后,列表才会跟着走,这是为什么呢?因为滑动的过程centerY是一直变的,列表虽然视觉上不动了,但是在touchend事件的时候,它的centerY值一直在累加。解决方法请往下看:

6. 设定反弹区间

“滑动反弹”,这里的反弹是本篇文章的最后一步,上面说到的问题,就在这里解决。因为每一次触发touchend事件的时候,centerY值就累加一次,所以需要在touchend事件里做判断。我们设定一个反弹区间,就是当centerY的值大于或者小于某个值的时候,让它触发反弹。

设定向上反弹值:

向上的值比较简单,设置成“0”。为什么是“0”呢?我们限定只要手指离开时,上一次的滑动距离加上本次的距离> 0的时候,就让它触发反弹,并且反弹回0点的位置,也就是两次滑动的距离和= 0

1329057332-5c37ff2ce6f2d_articlex.jpg

示例代码:

  1. // 向上反弹
  2. var maxUpBounce = 0;
  3. // touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  4. ul.addEventListener('touchend', function (e) {
  5. // 获取差值
  6. var dy = e.changedTouches[0].clientY - startY;
  7. // 记录移动的距离
  8. centerY = centerY + dy;
  9. // 两次滑动的距离 大于 设定的 向上 反弹值时
  10. if (centerY > maxUpBounce) {
  11. // 让两次滑动的距离 等于 设置的值
  12. centerY = maxUpBounce;
  13. // 添加过渡
  14. ul.style.transition = 'transform .5s';
  15. ul.style.transform = 'translateY(' + centerY + 'px)';
  16. }
  17. })

设定向下反弹值:

向下的值其实跟之前求滑动区间差不多,我们参考下图,当列表向上滑动,滑动到列表底部的时候,只要此时再向上滑动,就让它向下反弹。向下反弹值就是-(ul.offsetHeight - draw.offsetHeight),只要滑动的差值小于这个设定值,就让它向下反弹,并且反弹回设定值的位置。

2199699401-5c37ff2ce6fa4_articlex.jpg

示例代码:

  1. // 向上反弹值
  2. var maxUpBounce = 0;
  3. // 向下反弹值
  4. var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);
  5. // touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  6. ul.addEventListener('touchend', function (e) {
  7. // 获取差值
  8. var dy = e.changedTouches[0].clientY - startY;
  9. // 记录移动的距离
  10. centerY = centerY + dy;
  11. // 两次滑动的距离 大于 设定的 向上 反弹值时
  12. if (centerY > maxUpBounce) {
  13. // 让两次滑动的距离 等于 设置的值
  14. centerY = maxUpBounce;
  15. // 添加过渡
  16. ul.style.transition = 'transform .5s';
  17. ul.style.transform = 'translateY(' + centerY + 'px)';
  18. }
  19. // 两次滑动的距离 小于 设定的 向下 反弹值时
  20. else if (centerY < maxDownBounce) {
  21. // 让两次滑动的距离 等于 设置的值
  22. centerY = maxDownBounce;
  23. // 添加过渡
  24. ul.style.transition = 'transform .5s';
  25. ul.style.transform = 'translateY(' + centerY + 'px)';
  26. }
  27. })

注意:touchend事件的时候,给列表添加了transition属性才会有反弹的效果,但是,下一次滑动的时候,touchmove事件的时候,这个属性还存在,所以就会出现滑动的时候有顿挫感,所以在touchmove事件的时候,一进来就清一下过渡ul.style.transition = 'none';

完成后效果图:

49134015-5c37ff2e33c57_articlex.gif

7. 完整代码

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>移动端 Touch 滑动反弹</title>
  8. <style>
  9. /* 样式初始化 */
  10. * {
  11. margin: 0;
  12. padding: 0;
  13. }
  14. html,
  15. body {
  16. width: 100%;
  17. }
  18. aside {
  19. height: 100%;
  20. width: 100%;
  21. }
  22. /* 列表的父盒子,限制宽高 */
  23. /* 注意设置overflow: hidden;样式后,超出这个盒子的ul将不会显示 */
  24. .draw {
  25. width: 60px;
  26. height: 500px;
  27. border: 2px solid #ccc;
  28. overflow: hidden;
  29. position: fixed;
  30. left: 10px;
  31. top: 50%;
  32. transform: translateY(-50%);
  33. }
  34. /* li 设置了浮动, 所以 ul 要清除浮动 */
  35. ul:after {
  36. content: "";
  37. display: block;
  38. visibility: hidden;
  39. height: 0;
  40. clear: both;
  41. }
  42. ul {
  43. zoom: 1;
  44. }
  45. li {
  46. list-style: none;
  47. float: left;
  48. width: 60px;
  49. height: 60px;
  50. line-height: 60px;
  51. text-align: center;
  52. }
  53. </style>
  54. </head>
  55. <aside>
  56. <div id="draw">
  57. <ul>
  58. <li style="background:orange">列表一</li>
  59. <li style="background:yellowgreen">列表二</li>
  60. <li style="background:yellow">列表三</li>
  61. <li style="background:cyan">列表四</li>
  62. <li style="background:orangered">列表五</li>
  63. <li style="background:pink">列表六</li>
  64. <li style="background:red">列表七</li>
  65. <li style="background:purple">列表八</li>
  66. <li style="background:violet">列表九</li>
  67. <li style="background:brown">列表十</li>
  68. </ul>
  69. </div>
  70. </aside>
  71. <body>
  72. <script>
  73. var draw = document.querySelector('#draw');
  74. var ul = draw.children[0];
  75. var startY = 0; // 刚触碰到屏幕的时的手指信息
  76. var centerY = 0; // 用来记录每次触摸时上一次的偏移距离
  77. var maxDown = 50; // 设定一个最大向下滑动的距离
  78. var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); // 求得一个最大向上滑动的距离
  79. var maxUpBounce = 0; // 向上反弹值
  80. var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight); // 向下反弹值
  81. // touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离
  82. ul.addEventListener('touchstart', function (e) {
  83. startY = e.changedTouches[0].clientY;
  84. })
  85. // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  86. ul.addEventListener('touchmove', function (e) {
  87. // 清除过渡
  88. ul.style.transition = 'none';
  89. // 获取差值
  90. var dy = e.changedTouches[0].clientY - startY;
  91. // 上次的滑动距离加上本次的滑动距离
  92. var tempY = centerY + dy;
  93. // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候
  94. if (tempY > maxDown) {
  95. tempY = maxDown;
  96. }
  97. // 当上次滑动的距离加上本次滑动的距离 小于 设定的最大向上距离的时候
  98. else if (tempY < maxUp) {
  99. tempY = maxUp;
  100. }
  101. // 设置 ul 在 Y 轴上的偏移
  102. ul.style.transform = 'translateY(' + tempY + 'px)';
  103. })
  104. // touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
  105. ul.addEventListener('touchend', function (e) {
  106. // 获取差值
  107. var dy = e.changedTouches[0].clientY - startY;
  108. // 记录移动的距离
  109. centerY = centerY + dy;
  110. // 两次滑动的距离 大于 设定的 向上 反弹值时
  111. if (centerY > maxUpBounce) {
  112. // 让两次滑动的距离 等于 设置的值
  113. centerY = maxUpBounce;
  114. // 添加过渡
  115. ul.style.transition = 'transform .5s';
  116. ul.style.transform = 'translateY(' + centerY + 'px)';
  117. }
  118. // 两次滑动的距离 小于 设定的 向下 反弹值时
  119. else if (centerY < maxDownBounce) {
  120. // 让两次滑动的距离 等于 设置的值
  121. centerY = maxDownBounce;
  122. // 添加过渡
  123. ul.style.transition = 'transform .5s';
  124. ul.style.transform = 'translateY(' + centerY + 'px)';
  125. }
  126. })
  127. </script>
  128. </body>
  129. </html>

以上就是原生js实现移动端Touch滑动反弹的方法(代码示例)的详细内容,更多请关注Gxl网其它相关文章!

人气教程排行