pc端的鼠标事件写多了,但移动端没有鼠标,所以来写写移动端的touch事件。touch事件主要用到touchstart,touchmove,touchend三个事件,事件没什么好说的,做效果是最主要用到的是touch事件对象给我们提供的几个属性
touches 屏幕触点集合
targetTouches touch事件绑定元素身上的触点集合
changesTouches 发生改变了的触点集合
以下引用segmentfault@pangpang的图:
手势
说到手势,那么可以确定当前至少有两个以上触点,下面就来简单的说说两个触点的旋转手势和缩放手势。其实很简单,在touchstart事件中确定两个触点(e.targetTouches[0],e.targetTouches[1])的初始坐标,然后在touchmove事件中得到移动后的两触点坐标,然后进行计算判断,下面是我画的计算图
无缝滚动
pc端的无缝轮播写了很多次了,其实移动端的无缝滚动原理也差不多。这世界没有真正的无缝滚动,都是些障眼法,在pc端要将第一个滑块复制一份加到最后,而移动端要将原来第一和最后一个滑块复制出来分别加到最后面和最前面。举例原来有四个滑块排列1、2、3、4,现在将它变成4、1、2、3、4、1共六块,如果当前touch事件target元素为第一个4时,则将当前可视区平移到第二个4,如果当前touch事件target元素为最后一个1时,则将当前可视区平移到第一个1。
具体实现看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>touch无缝滚动</title>
<style>
*{
margin: 0;
padding: 0;
}
html, body{
width: 100%;
height: 100%;
}
html{
font-size: 20px;
}
#banner{
width: 100%;
height: 8rem;
}
#banner ul{
width: 75rem;
overflow: hidden;
list-style: none;
}
#banner ul li{
width: 18.75rem;
height: 8rem;
float: left;
color: white;
font-size: 26px;
line-height: 8rem;
text-align: center;
}
</style>
</head>
<body>
<div id="banner">
<ul>
<li style="background: pink">1</li>
<li style="background: orange">2</li>
<li style="background: purple">3</li>
<li style="background: skyblue">4</li>
</ul>
</div>
<script>
//移动端rem兼容
document.documentElement.style.fontSize = 20*document.documentElement.clientWidth/375 + 'px';
var banner = document.getElementById('banner');
var ul = banner.getElementsByTagName('ul')[0];
var w = ul.firstElementChild.offsetWidth;
var i = 1;
//在ul首尾添加li,为了无缝滚动
ul.insertBefore(ul.lastElementChild.cloneNode(true), ul.children[0]);
ul.appendChild(ul.children[1].cloneNode(true));
ul.style.width = ul.children.length*w+'px';
//当前ul滑动到什么位置了
var bannerLeft = -w * i;
ul.style.transform = 'translateX('+bannerLeft+'px)';
var startX = 0, startY = 0;
var disX = 0, dir = '';
banner.addEventListener('touchstart', function(e){
e.preventDefault()
// e.cancelBubble = true;
ul.style.transition = ''; //先清除ul上的transition
startX = e.targetTouches[0].clientX;
dir = ''; //每开始touch一次先清除滑动方向
//如果当前touch的是第一个li,则将ul平移到倒数第二个li的位置
if(e.target == ul.firstElementChild){
i = ul.children.length-2;
bannerLeft = -w * i;
ul.style.transform = 'translateX('+bannerLeft+'px)';
//如果当前touch的是最后一个li,则将ul平移到第二个li的位置
}else if(e.target == ul.lastElementChild){
i = 1;
bannerLeft = -w * i;
ul.style.transform = 'translateX('+bannerLeft+'px)';
}
disX = startX - bannerLeft;
banner.addEventListener('touchmove', function(e){
e.preventDefault()
//如果滑动方向还没确定,先确定一下
if(!dir){
if(Math.abs(e.targetTouches[0].clientX - startX) > 5){//如果先横向滑动了5px
dir = 'x';
}
}else{
if(dir == 'x'){ //如果当前是横向滑动
bannerLeft = e.targetTouches[0].clientX - disX;
ul.style.transform = 'translateX('+bannerLeft+'px)';
}
}
}, false);
banner.addEventListener('touchend', function(){
//当前松手位置与屏幕垂直中线比较,判断松手后滑块回弹到哪里
i = Math.round(-bannerLeft/w);
bannerLeft = -w *i;
ul.style.transition = '0.3s'; //回弹时加上过度效果
ul.style.transform = 'translateX('+bannerLeft+'px)';
}, false);
}, false);
</script>
</body>
</html>
效果
下拉或上拉
简单写一个类似iscroll的类,这里只实现了纵向的下拉或上拉,其实原理都差不多,实现起来不难,主要就是判断什么时候可以下拉,什么时候可以上拉,其余时候都是元素默认滚动(注意需要外包元素overflow样式值为auto)。
实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>iscroll与hammer demo1</title>
<style>
*{
margin: 0;
padding: 0;
}
html, body{
width: 100%;
height: 100%;
}
ul{
list-style: none;
}
#wrap{
height: 100%;
overflow: auto;
background: #ccc;
}
.list li{
line-height: 50px;
background: white;
border-bottom: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="wrap">
<ul class="list">
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>Pretty row 5</li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>Pretty row 9</li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>Pretty row 13</li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>Pretty row 17</li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>Pretty row 21</li>
<li>Pretty row 22</li>
<li>Pretty row 23</li>
<li>Pretty row 24</li>
<li>Pretty row 25</li>
<li>Pretty row 26</li>
<li>Pretty row 27</li>
<li>Pretty row 28</li>
<li>Pretty row 29</li>
<li>Pretty row 30</li>
<li>Pretty row 31</li>
<li>Pretty row 32</li>
<li>Pretty row 33</li>
<li>Pretty row 34</li>
<li>Pretty row 35</li>
<li>Pretty row 36</li>
<li>Pretty row 37</li>
<li>Pretty row 38</li>
<li>Pretty row 39</li>
</ul>
</div>
<!-- <script src="https://cdn.bootcss.com/iScroll/5.2.0/iscroll.js"></script> -->
<script>
window.onload = function(){
var iScroll = new MyScroll('#wrap', {})
iScroll.on('beforestart', function(){
console.log('beforestart');
})
iScroll.on('scroll', function(){
console.log('scroll');
})
iScroll.on('scrollend', function(){
console.log('scrollend');
})
}
;(function(){
function MyScroll(selector, options){
if(options == undefined){
options = {}
}
this.options = {}
this.eventQueue = []; //MyScroll对象绑定事件队列
//默认参数,暂时没用到
this.options.bounce = options.bounce || true;
this.options.scrollX = options.scrollX || false;
this.options.scrollY = options.scrollY || true;
var wrapper = document.querySelector(selector); //外包元素,overflow样式值要为auto
var slider = wrapper.children[0]; //滑块元素
var startY = 0, top = 0, disY = 0; //一些初始值,top为滑块移动距离
var that = this;
slider.addEventListener('touchstart', start, false);
slider.addEventListener('touchmove', move, false);
slider.addEventListener('touchend', end, false);
function start(e){
// e.preventDefault();
that.eventQueue.forEach(function(json){
if(json.type == 'beforestart'){
json.fn();
}
})
slider.style.transition = ''; //先清除滑块上的transition
startY = e.targetTouches[0].clientY; //该效果只写了纵向滑动的情况
disY = startY - top;
}
function move(e){
// e.preventDefault();
that.eventQueue.forEach(function(json){
if(json.type == 'scroll'){
json.fn();
}
})
// console.log(wrapper.scrollTop)
//当外包元素的scrollTop值为0时才有下拉效果
if(wrapper.scrollTop == 0 && e.targetTouches[0].clientY - disY > 0){
top = e.targetTouches[0].clientY - disY;
slider.style.transform = 'translateY('+top+'px)';
//当外包元素的scrollTop值为滑块元素的高减去外包元素的高时才有上拉效果
}else if(wrapper.scrollTop == slider.offsetHeight-wrapper.offsetHeight && e.targetTouches[0].clientY - disY < 0){
top = e.targetTouches[0].clientY - disY;
slider.style.transform = 'translateY('+top+'px)';
}
}
function end(){
that.eventQueue.forEach(function(json){
if(json.type == 'scrollend'){
json.fn();
}
})
top = 0;
slider.style.transition = '500ms';
slider.style.transform = 'translateY('+top+'px)';
}
}
//MyScroll对象绑定事件函数
MyScroll.prototype.on = function(name, fn){
this.eventQueue.push({type: name, fn: fn});
}
window.MyScroll = MyScroll;
})();
</script>
</body>
</html>
效果
iscroll-probe.js下拉刷新
代码
<!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>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
html, body{
width: 100%;
height: 100%;
}
ul{
list-style: none;
}
#wrap{
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
position: relative;
}
#wrap .list{
width: 100%;
height: auto;
position: absolute;
z-index: 2;
background: white;
}
#wrap .list li{
height: 50px;
line-height: 50px;
border-bottom: 1px #999999 solid;
}
#wrap .refresh-bg{
position: absolute;
top: 0;
left: 0;
line-height: 50px;
z-index: 1;
width: 100%;
height: 300px;
background: #cccccc;
text-align: center;
}
</style>
<script>window.PointerEvent = void 0</script>
</head>
<body>
<div id="wrap">
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div class="refresh-bg">下拉刷新</div>
</div>
<button class="btn">点我</button>
<script src="https://cdn.bootcss.com/iScroll/5.2.0/iscroll-probe.min.js"></script>
<script>
//可滑动元素
var scroller = document.querySelector('.list');
//下拉刷新那几个字
var refreshBg = document.querySelector('.refresh-bg');
var myScroll = new IScroll('#wrap', {
bounceTime: 500,
probeType: 2
})
console.log(myScroll)
//scroll事件只在iscoll-probe版本可用
myScroll.on('scroll', function(){
console.log('scroll')
//如果下拉超过100px就改变“下拉刷新”样式
if(myScroll.y >= 100){
refreshBg.innerText = '赶紧松手'
}else if (myScroll.y>0 && myScroll.y<100){
refreshBg.innerText = '下拉刷新'
}
})
//用于测试
myScroll.on('scrollEnd', function(){
console.log(myScroll.y)
})
//开始下拉滑块时去除滑块transition样式
scroller.addEventListener('touchstart', function(){
scroller.style.transition = '';
})
//下拉滑块松手后
scroller.addEventListener('touchend', function(){
console.log(myScroll.y)
//如果松手时下拉超过了100px,那就ajax请求新数据
if(myScroll.y >= 100){
scroller.style.marginTop = '50px'
refreshBg.innerText = '加载中...'
//这里用定时器模拟ajax
setTimeout(() => {
scroller.style.marginTop = '0px';
scroller.style.transition = '0.3s';
refreshBg.innerText = '下拉刷新'
}, 2000);
}
})
</script>
</body>
</html>
效果图