作者: Levi丶
https://segmentfault.com/a/1190000017852497

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

1、准备工作

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

  • touchStart当手指触碰到屏幕的时候触发
  • touchmove当手指在屏幕上不断移动的时候触发
  • touchend当手指离开屏幕的时候触发

Touch事件触发的 Event 对象

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


changedTouchestargetTouchestouches都是伪数组,里面装的是手指列表。

三种返回对象的区别

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

有哪些手指信息?

我们可以看下上面的图片,在 changedTouche[0]中,有些值:

clientX:74 // 触摸点相对于浏览器的 viewport 左边缘的 x 坐标,不会包括左边的滚动距离。
clientY:73 // 触摸点相对于浏览器的 viewport 上边缘的 Y 坐标,不会包括上边的滚动距离。
screenX:2202 // 触摸点相对于屏幕左边缘的 x 坐标。
screenY:327 // 触摸点相对于屏幕上边缘的 Y 坐标。
pageX:65 // 触摸点相对于 document 的左边缘的 x 坐标,包括左边的滚动距离
pageY:18 // 触摸点相对于 document 的上边缘的 Y 坐标,包括左边的滚动距离

2、基本结构

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

HTML部分

<div class="main"><div id="draw" class="draw"><ul><li style="background:orange">列表一</li><li style="background:yellowgreen">列表二</li><li style="background:yellow">列表三</li><li style="background:cyan">列表四</li><li style="background:orangered">列表五</li><li style="background:pink">列表六</li><li style="background:red">列表七</li><li style="background:purple">列表八</li><li style="background:violet">列表九</li><li style="background:violet">列表十</li></ul></div>
</div>

CSS部分

* {margin: 0;padding: 0;
}
html,
body {width: 100%;
}
.main {width: 100%;height: 100%;
}
.draw {width: 60px;height: 500px;border: 2px solid #ccc;overflow: hidden;position: fixed;left: 10px;top: 50%;transform: translateY(-50%);
}
li {display: block;list-style: none;width: 60px;height: 60px;line-height: 60px;text-align: center;
}

效果图

3、首次滑动

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

实现原理

  1. touchstart的时候,获取手指触摸的落点 A,通过这个点对象里面的 clientY属性,获取距离顶部可视区的距离;
  2. touchmove的时候,获取手指的点 B,同样的获取移动时距离顶部可视区的距离;
  3. touchmove的时候,还要做另一件事情,就是获取两点的差值( B.clientY-A.clientY),将这个差值动态赋值给 ul, ul只需要设置向 Y轴方向偏移这个距离,就能实现列表随手指滑动。

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

示例代码

var draw = document.querySelector('#draw');
var ul = draw.children[0];// touchstart时,记录手指在Y轴上落点距离可视顶部距离
var startY = 0;
ul.addEventListener('touchstart', function (e) {startY = e.changedTouches[0].clientY;
});// touchmove时,记录手指在Y轴上的位置(滚动距离)
ul.addEventListener('touchmove', function (e) {// 获取差值var dy = e.changedTouches[0].clientY - startY;// 设置 ul 在 Y 轴上的偏移ul.style.transform = 'translateY(' + dy + 'px)';
})

效果图

4、再次滑动

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

问题分析

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

解决方法

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

  • 新增 touchend事件,在该事件里同样的可以获取到本次滑动的距离,将它与上一次的距离相加,赋值给一个全局变量;
  • touchmove事件里有点小改动,就是在给 ul 设置偏移值的时候,除了本次滑动的差值还要加上这个上一次的值;

示意图

示例代码

var draw = document.querySelector('#draw');
var ul = draw.children[0];var startY = 0; // 刚触碰到屏幕的时的手指信息
var centerY = 0; // 用来记录每次触摸时上一次的偏移距离// touchstart时,记录手指在Y轴上落点距离可视顶部距离
ul.addEventListener('touchstart', function (e) {startY = e.changedTouches[0].clientY;
})// touchmove时,记录手指在Y轴上的位置(滚动距离)
ul.addEventListener('touchmove', function (e) {// 获取差值var dy = e.changedTouches[0].clientY - startY;// 上次的滑动距离加上本次的滑动距离var tempY = centerY + dy;// 设置 ul 在 Y 轴上的偏移ul.style.transform = 'translateY(' + tempY + 'px)';
})

效果图

限制滑动区间

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

限制向下滑动最大区间

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

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

限制向上滑动最大区间

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

求出向上滑动最大值

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

var maxDown = 50; // 设定一个最大向下滑动的距离
var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); //最大向上滑动距离// touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener('touchmove', function(e) {// 获取差值var dy = e.changedTouches[0].clientY - startY;// 上次的滑动距离加上本次的滑动距离var tempY = centerY + dy;// 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候if(tempY > maxDown) {// 直接让偏移的值 等于这个设定值tempY = maxDown;}// 当上次滑动的距离加上本次滑动的距离 小于 设定的最大向上距离的时候else if (tempY < maxUp) {tempY = maxUp;}// 设置 ul 在 Y 轴上的偏移ul.style.transform = 'translateY('+ tempY +'px)';
})


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

设定反弹区间

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

设定向上反弹值

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

示例代码

var maxUpBounce = 0; //向上反弹阈值// touchend时,记录当前滚动距离
ul.addEventListener('touchend', function (e) {// 获取差值var dy = e.changedTouches[0].clientY - startY;// 记录移动的距离centerY = centerY + dy;// 两次滑动的距离 大于 设定的 向上 反弹值时if (centerY > maxUpBounce) {// 让两次滑动的距离 等于 设置的值centerY = maxUpBounce;// 添加过渡ul.style.transition = 'transform .5s';ul.style.transform = 'translateY(' + centerY + 'px)';}
})

设定向下反弹值

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

示例代码

var maxUpBounce = 0; //向上反弹阈值
var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight); // 向下反弹阈值// touchend时,记录当前滚动距离
ul.addEventListener('touchend', function (e) {// 获取差值var dy = e.changedTouches[0].clientY - startY;// 记录移动的距离centerY = centerY + dy;// 两次滑动的距离 大于 设定的 向上 反弹值时if (centerY > maxUpBounce) {// 让两次滑动的距离 等于 设置的值centerY = maxUpBounce;// 添加过渡ul.style.transition = 'transform .5s';ul.style.transform = 'translateY(' + centerY + 'px)';}// 两次滑动的距离 小于 设定的 向下 反弹值时else if (centerY < maxDownBounce) {// 让两次滑动的距离 等于 设置的值centerY = maxDownBounce;// 添加过渡ul.style.transition = 'transform .5s';ul.style.transform = 'translateY(' + centerY + 'px)'}
})

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

完成后效果图

完整代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>mobile-touch</title><style>* {margin: 0;padding: 0;}html,body {width: 100%;}.main {width: 100%;height: 100%;}.draw {width: 60px;height: 500px;border: 2px solid #ccc;overflow: hidden;position: fixed;left: 10px;top: 50%;transform: translateY(-50%);}li {display: block;list-style: none;width: 60px;height: 60px;line-height: 60px;text-align: center;}</style>
</head><body><div class="main"><div id="draw" class="draw"><ul><li style="background:orange">列表一</li><li style="background:yellowgreen">列表二</li><li style="background:yellow">列表三</li><li style="background:cyan">列表四</li><li style="background:orangered">列表五</li><li style="background:pink">列表六</li><li style="background:red">列表七</li><li style="background:purple">列表八</li><li style="background:violet">列表九</li><li style="background:violet">列表十</li></ul></div></div><script>var draw = document.querySelector('#draw');var ul = draw.children[0];var startY = 0; //刚触碰到屏幕时的手指信息var centerY = 0; //记录当前滚动位置var maxDown = 50; //最大向下滑动距离var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); //最大向上滑动距离var maxUpBounce = 0; //向上反弹阈值var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight); // 向下反弹阈值// touchstart时,记录手指在Y轴上落点距离可视顶部距离ul.addEventListener('touchstart', function (e) {startY = e.changedTouches[0].clientY;})// touchmove时,记录手指在Y轴上的位置(滚动距离)ul.addEventListener('touchmove', function (e) {var dy = e.changedTouches[0].clientY - startY;var tempY = centerY + dy;if (tempY > maxDown) {tempY = maxDown;} else if (tempY < maxUp) {tempY = maxUp;}ul.style.transition = 'none'ul.style.transform = 'translateY(' + tempY + 'px)';})// touchend时,记录当前滚动距离ul.addEventListener('touchend', function (e) {var dy = e.changedTouches[0].clientY - startY;centerY = centerY + dy;if (centerY > maxUpBounce) {centerY = maxUpBounce;} else if (centerY < maxDownBounce) {centerY = maxDownBounce;}ul.style.transition = 'transform .5s';ul.style.transform = 'translateY(' + centerY + 'px)'})</script>
</body></html>

(≥3≤)

原生 JS 实现移动端 Touch 滑动反弹相关推荐

  1. 原生js实现移动端touch事件,解决穿透问题

    四种touch事件 touchstart: //手指放到屏幕上时触发 touchmove: //手指在屏幕上滑动式触发 touchend: //手指离开屏幕时触发 touchcancel: //系统取 ...

  2. 原生JS实现移动端上下滑动一次一屏(仿抖音)

    功能如下: 头部: 附近.关注.推荐选项卡的切换 左右滑动功能.头部选项卡跟随动画 上下滑动划动一屏,滑动超过头部刷新 双击选项卡回到顶部 上代码: <!DOCTYPE html> < ...

  3. 原生JS实现移动端模块的左右滑动切换效果,基于vue、stylus

    原生JS实现移动端模块的左右滑动动画效果,基于vue.stylus 大概实现方案: 手指touch屏幕的整个过程,会派发touchstart.touchmove.touchend三个事件,对这三个事件 ...

  4. html移动端选择器插件,原生js实现移动端选择器插件

    原生js实现移动端选择器插件 前言 插件功能只满足我司业务需求,如果希望有更多功能的,可在下方留言,我尽量扩展!如果你有需要或者喜欢的话,可以给我github来个star ? 预览 准备 首先在页面中 ...

  5. 原生JS实现移动端选择器插件

    原生js实现移动端选择器插件 仓库地址 在线预览(记得将浏览器切换到手机模式) 预览 准备 首先在页面中引入css,js文件 每次需要弹出该组件时通过new一个实例来生成,代码如下: var data ...

  6. 移动端实现文字轮播_使用原生JS实现移动端图片轮播效果(一)

    PC端上实现图片轮播效果非常简单,只要通过使用click事件就可以非常简单的实现效果,但是在移动端上,就要通过核心的touch事件来实现.话不多说,现在我们就开始移动端轮播效果的实现. 首先就是原生J ...

  7. 移动端 touch 滑动事件

    移动端触屏滑动的效果其实就是图片轮播,在PC的页面上很好实现,绑定click和mouseover等事件来完成.但是在移动设备上,要实现这种轮播的效果,就需要用到核心的touch事件.处理touch事件 ...

  8. pc端html轮播带滑块,原生js实现移动端+pc端 轮播插件

    slide.js 原生js写的轮播兼容 pc+移动端 插件,支持轮播速度,轮播内容,轮播间隔,手势灵敏度自定义,导航圆点点击跳转,手势滑动. 使用说明:slide.js文件包含小部分es6语法编写的文 ...

  9. 用原生js实现移动端图片轮播

    1.实现思路 1.在首尾添加图片     1.在开始位置添加原始的最后一张图片     2.在最后位置添加原始的第一张图片 2.修改页面结构 3.修改对应的样式 .jd_bannerImg{width ...

  10. 原生js实现移动端京东首页搜索框、菜单栏滑动弹回、倒计时、banner动画、缓慢返回顶部效果(HTML+CSS+JS)

    一.实现效果 二.实现功能 搜索框滑动固定,伸缩动画 菜单栏左右滑动,超出区域自动弹回 banner自动轮播,手指左右滑动,滑动距离一半以内自动弹回,超过一半自动跳到下一张,圆点跟随显示颜色,以及实现 ...

最新文章

  1. 测试工程师的好日子来啦?Testin发布AI测试产品,提升易用性和自动化效率
  2. Sun公司开源游戏服务器Project Darkstar Server——(Sun game server , 简称 sgs)学习笔记(一):sgs简介...
  3. 【运筹学】线性规划 单纯形法 ( 基矩阵 | 基变量 | 非基矩阵 | 非基变量 | 矩阵分块形式 | 逆矩阵 | 基解 | 基可行解 )
  4. 【深度学习】Transformer长大了,它的兄弟姐妹们呢?(含Transformers超细节知识点)...
  5. 员外陪你读论文:DeepWalk: Online learning of Social Representations
  6. SGU155(笛卡尔树的构造)
  7. SQLServer还原 指定的转换无效解决方法
  8. 基于Bootstrap 3.x的免费高级管理控制面板主题:AdminLTE
  9. Linux crontab下关于使用date命令和sudo命令的坑
  10. WIN7 远程桌面发生身份验证错误,要求的函数不受支持
  11. 关于家庭无线局域网的连接
  12. java string is empty_从源码分析java.lang.String.isEmpty()
  13. Django 模板系统
  14. oracle linux 版本 uek,在运行 Oracle Linux 7.1 UEK3 或 7.2 或者 RHEL 7.1 或 7.2 的系统上,RDMA 服务无法启动...
  15. Spring,SpringMVC,SpringBoot,SpringCloud有什么区别和联系?
  16. (Android-RTC-8)分析HardwareVideoEncoder—BitrateAdjuster
  17. 分析mrp主要应用范围_MRP软件行业现状调研分析及发展趋势预测报告(2020)
  18. echarts画工作流(流程图)
  19. 一、对文本文件进行数据粒度转换,即将文本文件personnel_data.txt中字段household_register的数据统一成省份,并且输出到文本文档personnel_data_new.tx
  20. nestjs+vue+ts打造一个酷炫的星空聊天室(含完整数据库设计)

热门文章

  1. 省市县及对应编码-json格式
  2. 【Copy攻城狮日志】飞浆学院强化学习7日打卡营-学习笔记
  3. node--压缩文件夹
  4. 教你 用c语言输出乘法口诀表 一giao我嘞gaiogiao
  5. 计算机主机的拆卸的注意事项,拆解笔记本注意事项及技巧!
  6. 全栈云服务是个什么东东?!
  7. 赵绍琴温病学讲座(一)
  8. 租酥雨的NOIP2018赛前日记
  9. 怎么将多张图片打印在一张A4纸上?
  10. sqlserver加密隐私字段(不侵入程序)-Always Encrypted