挪动端 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: 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;
}
结果图:
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)';
})
// 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>