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

1. 准备工作
什么是移动端的 Touch事件?在移动端 Touch事件可以细分成三种,分别是: touchstart、touchmove和 touchend,并且 touch事件必须要用 addEventListener去监听。
touchStart当手指触碰到屏幕的时候触发touchmove当手指在屏幕上不断移动的时候触发touchend当手指离开屏幕的时候触发
Touch 事件触发的 Event 对象
// 手指触碰到屏幕时触发
element.addEventListener('touchstart',function (e){
// 打印的事件对象
console.log(e);
})

changedTouches、 targetTouches、 touches都是伪数组,里面装的是手指列表。
三种返回对象的区别
其实这三种返回的对象,都是表示用户触摸事件时的手指信息,之所以是一个伪数组,是因为有可能出现多指同时触摸,但是在实际工作中一般不去考虑多指的情况。而它们唯一的区别就是在 touchstart和 touchmove事件的时候, changedTouches、 targetTouches、 touches都能获取到手指的信息,但是在 touchend事件的时候, targetTouches、 touches对象是不能返回离开屏幕时的手指信息,只有 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部分
<aside class="main">
<div class="draw" id="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:brown">列表十</li>
</ul>
</div>
</aside>
css部分
在列表的父盒子上设定一个 overflow:hidden属性,使超出盒子部分的列表暂时隐藏掉,后面会通过 js去实现滑动。
* {
margin:;
padding:0
}
html,body {
width:100 %
}
aside {
height:100 %;
width:100 %
}
. draw {
width:60px;
height:500px;
border:2px solid #ccc;
overflow:hidden;
position:fixed;
left:10px;
top:50 %;
transform:translateY(- 50 %)
}
ul:after {
content:"";
display:block;
visibility:hidden;
height:;
clear:both
}
ul {
zoom:1
}
li {
list - style:none;
float:left;
width:60px;
height:60px;
line - height:60px;
text - align:center
}
效果图

3. 首次滑动
手指触摸到列表向下滑动的时候,列表应该跟着向下滑动,当手指离开屏幕的时候,列表应该停在滑动的位置。这里就会用到上面准备阶段的知识点了,不明白的可以参考上面的概念。
实现原理
touchstart的时候,获取手指触摸的落点A,通过这个点对象里面的clientY属性,获取距离顶部可视区的距离;touchmove的时候,获取手指的点B,同样的获取移动时距离顶部可视区的距离;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)';
})
// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener('touchend',function(e){
// 获取差值
var dy=e.changedTouches[0].clientY-startY;
// 记录移动的距离
centerY=centerY+dy;
})
效果图

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

限制向下滑动最大区间
设定向下最大区间的值比较简单,直接设定一个值,当上一次滑动的距离加上本次滑动的距离大于这个值的时候,就不让它再继续往下滑了,让他直接等于这个设定的值。
示例代码
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值一直在累加。解决方法请往下看。
6. 设定反弹区间
“滑动反弹”,这里的反弹是本篇文章的最后一步,上面说到的问题,就在这里解决。因为每一次触发 touchend事件的时候, centerY值就累加一次,所以需要在 touchend事件里做判断。我们设定一个反弹区间,就是当 centerY的值大于或者小于某个值的时候,让它触发反弹。
设定向上反弹值
向上的值比较简单,设置成“ 0”。为什么是“ 0”呢?我们限定只要手指离开时,上一次的滑动距离加上本次的距离 >0的时候,就让它触发反弹,并且反弹回 0点的位置,也就是两次滑动的距离和 =0。

示例代码
// 向上反弹
var maxUpBounce = 0;
// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
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 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
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';。
完成后效果图
7. 完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>移动端 Touch 滑动反弹</title>
<style>
/* 样式初始化 */
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
}
aside {
height: 100%;
width: 100%;
}
/* 列表的父盒子,限制宽高 */
/* 注意设置overflow: hidden;样式后,超出这个盒子的ul将不会显示 */
.draw {
width: 60px;
height: 500px;
border: 2px solid #ccc;
overflow: hidden;
position: fixed;
left: 10px;
top: 50%;
transform: translateY(-50%);
}
/* li 设置了浮动, 所以 ul 要清除浮动 */
ul:after {
content: "";
display: block;
visibility: hidden;
height: 0;
clear: both;
}
ul {
zoom: 1;
}
li {
list-style: none;
float: left;
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
}
</style>
</head>
<aside class="main">
<div class="draw" id="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:brown">列表十</li>
</ul>
</div>
</aside>
<body>
<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) {
// 清除过渡
ul.style.transition = 'none';
// 获取差值
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)';
})
// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
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)';
}
})
</script>
</body>
</html>
原生 JS 实现移动端 Touch 滑动反弹的更多相关文章
- 原生js实现一个DIV的碰撞反弹运动,并且添加重力效果
继上一篇... 原生js实现一个DIV的碰撞反弹运动,并且添加重力效果 关键在于边界检测,以及乘以的系数问题,实现代码并不难,如下: <!DOCTYPE html> <html la ...
- 原生js实现一个DIV的碰撞反弹运动
原生js实现一个DIV的碰撞反弹运动: 关键在于DIV的边界检测,进而改变运动方向,即可实现碰撞反弹效果. <!DOCTYPE html> <html lang="en& ...
- 移动端touch滑动事件监听
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 原生js拖拽功能制作滑动条实例教程
拖拽属于前端常见的功能,很多效果都会用到js的拖拽功能.滑动条的核心功能也就是使用js拖拽滑块来修改位置.一个完整的滑动条包括 滑动条.滑动痕迹.滑块.文本 等元素,先把html代码写出来,如下所示: ...
- 原生JS实现移动端的轮播效果
首先 我们想实现的效果是在手指按下拖动的时候图片能够跟随移动(无动画效果)然后松开手指后判断图片移动的位置 和某一个值进行比较 在这里我们默认定为盒子的1/3宽度 当x轴的移动位置大于1/3的时候图片 ...
- 原生js实现网页触屏滑动
前言: 我有一个html格式的2048游戏,可以用键盘上下左右操作,但是放到手机上就抓瞎了.于是想修改一下代码,将键盘事件改成手机触屏事件. html5 的touch事件 html5支持touch事件 ...
- JS封装移动端触摸滑动插件应用于导航banner【精装版】
自己封装了一个小插件,一个小尝试. 可用于类似于京东导航等效果,效果多样化,很方便. 欢迎大家提点意见. mrChenSwiper( {parent, child, type, parentN, c ...
- 原生JS实现移动端轮播图
功能描述: 自动无缝轮播图片,底部小圆点跟图片保持一致:手指左右移动轮播图,移动距离大于50px播放下一张(或上一张),小于50px则回弹 具体功能实现: 1.定时器 自动轮播图片 先声明一个inde ...
- 【转】原生js实现移动端h5长按事件
$("#target").on({ touchstart: function(e) { // 长按事件触发 timeOutEvent = setTimeout(function() ...
随机推荐
- 62. 用流程自带的打印功能,IE浏览器打印出来是空白
用流程自带的打印功能,IE浏览器打印出来是空白的这个问题确认是由于IE启用了兼容模式导致的了把IE的兼容模式关掉就行了
- apache中 MaxClients 与MaxRequestsPerChild
据现象来对APACHE调优,以前用MAXCLIENTS 3000,砖家建议后,改为1500,今天查资料如下: http://www.linuxqq.net/ MaxClients 要加到多少?连接数理 ...
- java 调用apache.commons.codec的包简单实现MD5加密
转自:https://blog.csdn.net/mmd1234520/article/details/70210002/ import java.security.MessageDigest; im ...
- UI5-文档-4.34-Custom Controls
在这一步中,我们将使用自定义控件扩展SAPUI5的功能.我们希望对详细页面上显示的产品进行评级,因此我们使用SAPUI5扩展机制创建了多个标准控件的组合,并添加了一些粘合代码以使它们能够很好地一起工作 ...
- net3.5 无网络环境安装
下载 提取码:t0dq 将下载的文件复制到复制到 C 盘的 Windows 文件夹 后请在“命令提示符(管理员)”中执行下面的命令: dism /online /Enable-Feature /F ...
- http chunked
http chunked传输:将信息分段传输 好处: 不用指定content-length字段(总的要传输文件信息的长度),即可以将一整段信息分为若干段分别发送,最后发送chunked长度为0的信息表 ...
- javascript知识点积累
8年javascript知识点积累 08年毕业就开始接触javascript,当时是做asp.net发现很多功能用asp.net控件解决不了,比如checkbox单选,全选问题,自动计算总价问题, ...
- ComputeSignature 中行支付签名报错(win7 64位系统)
在做中行加密验签的时候出现的问题.原本在XP系统下可以正常运行的,现在换了win7 64位系统就出现了这个问题,没头绪 所以发上来求各位大大支招 有什么好的解决方案.. 我的解决办法: 1.C:\Do ...
- Numpy随机数
Numpy随机数 np.random随机数子库 1: 基本函数 .rand(d0,d1,..dn):创建d0-dn维度的随机数数组,浮点数,范围从0-1,均匀分布 .randn(d0,d1,..dn) ...
- git冲突解决方案 Intellij IDEA
一般在团队合作开发一个项目的过程中,经常出现两个人同时修改一个文件然后都向主master提交commit,这样就会产生冲突(conflict),那么这种情况如何解决? 1 新建分支 如果项目的主分支是 ...