移动端项目做了不少了,但之前很多交互还是依托click相关事件实现的,功能上的需求也都实现了,但自己总感觉不合适,这次又拿到个移动端的需求,决定去用touch相关事件去实现。代码的话,也是网上找的,虽然拿过来用的时候直接报错一点都没执行,但自己改了改,最后能用了,就以他为模板了。
我印象里,html标签似乎没有什么ontouch,ontouchstart这样的触发事件,反正我编辑工具似乎不支持,强行写也没触发过。这次的尝试涉及到了很多没了解过的功能,下面开始一一介绍吧。
判断浏览器是否支持touch事件
touch事件嘛,和click事件不一样,最起码设备得支持,要是PC端使,方法全不执行咋整,所以先来个判断。
('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch
这个方法,对于支持触摸事件的浏览器,返回值是 true ,不支持的,就是 undefined 了。在这,模板中判断方法是这样的:
if(!!self.touch) //这里self.touch指的是上面那行代码了
又是一个没见过的科技,找度娘,又涨了一波姿势:
js中!的用法是比较灵活的,它除了做逻辑运算常常会用!做类型判断,可以用!与上对象来求得一个布尔值
!可将变量转换成boolean类型,null、undefined和空字符串取反都为false,其余都为true。
!!常常用来做类型判断,在第一步!(变量)之后再做逻辑取反运算
判断浏览器是否支持touch事件,就这样解决了。
添加 addEventListener 方法
addEventListener用来绑定事件,我不了解JS的addEventListener()和JQ的on()有多大区别,反正在这用on是报错了。在这,给body绑定上了touchstart事件(代码在初始化部分可见)。
拿到模板执行时,控制面板提示.addEventListener is not a function,又去找度娘,得到的答案是这是因为选择器没有正确选择元素对象,我的代码中,是通过类名定位元素,捕捉到的是该类名元素的数组,又解决一个新问题。
判断触发触摸事件后的状态
addEventListener第二个参数可以传一个对象,这样会调用该对象的handleEvent属性(事件会自动在传入对象中寻找handleEvent方法),这就有了去做状态判断的基础,通过判断触摸状态,去执行对应的方法。具体方法如下:
handleEvent:function(event){
var self = this; //this指events对象
if(event.type == 'touchstart'){
self.start(event);
}else if(event.type == 'touchmove'){
self.move(event);
}else if(event.type == 'touchend'){
self.end(event);
}
},
触摸开始,触摸移动,触摸结束
这三个状态的话,触摸开始主要是需要纪录按下的位置,用于之后的计算,并向目标元素绑定touchmove和touchend方法;触摸移动需要做的事就是判断下有没有多指操作,并记录手指位置;触摸结束,判断手指在整个行为过程中移动方向,添加对应的方法。代码如下:
//滑动开始
start:function(event){
var touch = event.targetTouches[0]; //touches数组对象获得屏幕上所有的touch,取第一个touch
startPos = {x:touch.pageX,y:touch.pageY,time:+new Date}; //取第一个touch的坐标值
isScrolling = 0; //这个参数判断是垂直滚动还是水平滚动,默认0位水平,1为垂直
body.addEventListener('touchmove',this,false);
body.addEventListener('touchend',this,false);
},
//移动
move:function(event){
//当屏幕有多个touch或者页面被缩放过,就不执行move操作
if(event.targetTouches.length > 1 || event.scale && event.scale !== 1) return;
var touch = event.targetTouches[0];
endPos = {x:(touch.pageX - startPos.x),y:(touch.pageY - startPos.y)};
isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1:0; //isScrolling为1时,表示纵向滑动,0为横向滑动
//若有需要触摸跟随的实践在此处添加
},
//滑动释放
end:function(event){
var duration = +new Date - startPos.time; //滑动的持续时间
// console.log(isScrolling);
if(isScrolling == 1){ //当为垂直滚动时
if(Number(duration) > 10){ //滑动时间大于10ms
//判断是左移还是右移,当偏移量大于10时执行
if(endPos.y > 10){
console.log("向下");
}else if(endPos.y < -10){
console.log("向上");
}
}
}else{
if(Number(duration) > 10){
//判断是左移还是右移,当偏移量大于10时执行
if(endPos.x > 10){
console.log("向右");
}else if(endPos.x < -10){
console.log("向左");
}
}
}
//解绑事件
body.removeEventListener('touchmove',this,false);
body.removeEventListener('touchend',this,false);
对整个方法来说,手指离开屏幕时,我刚写的方法也完成了,这时候记得解绑按下时绑定的方法。
init 初始化
之前在别人写的小插件中也见过,但我这种野路子出身的,完全不了解,不会用,也就是百度大概看看,有个似懂非懂的了解。近期和其他公司合作完成项目时见到对方后台都在页面上用init,坚定了我这次尝试的想法。
目前我对他的理解,觉得它就是用来初始化的,可以绑定事件,也是一个方法函数,表述不是太清楚,要是有了解的,希望可以指正。顺带贴出这块的相关代码。
<script>
var body = $("body")[0]; //这里就拿body作为绑定事件的对象吧
var slider = {
//判断设备是否支持touch事件
touch:('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
slider:body,
events:{/*这里就不重复了*/},
//初始化
init:function(){
var self = this; //this指slider对象
if(!!self.touch){
self.slider.addEventListener('touchstart',self.events,false);
//addEventListener第二个参数可以传一个对象,会调用该对象的handleEvent属性
}
}
};
slider.init();
</script>
其他小技巧
除了一开始的 !! ,模板中还有好几个值得学习的技巧:
记录触摸位置时,没有设置多个变量,用一组数据完成了:
startPos = {x:touch.pageX,y:touch.pageY,time:+new Date};
使用的时候,直接用startPos.x
,+new Date
也是个我没见过的科技,它等同于Date.prototype.valueOf()
,功能是获取当前的毫秒时间戳。
最后,附上我完整的页面
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>touch事件测试</title>
<style>
.page{
width: 100vw;
height: 100vh;
font-size: 18px;
color: #ffffff;
line-height: 100vh;
text-align: center;
}
.red{
background: #ef4641;
}
.blue{
background: #096bf2;
}
.yellow{
background: yellow;
}
.grey{
background: #f5f5f5;
}
</style>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div class="page red">
滑动切换
</div>
<div class="page yellow">
滑动切换
</div>
<div class="page blue">
滑动切换
</div>
<div class="page grey">
滑动切换
</div>
<script>
var body = $("body")[0];
var slider = {
//判断设备是否支持touch事件
touch:('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
slider:body,
//事件
events:{
//index:0, //显示元素的索引
//slider:this.slider, //this为slider对象
handleEvent:function(event){
var self = this; //this指events对象
if(event.type == 'touchstart'){
self.start(event);
}else if(event.type == 'touchmove'){
self.move(event);
}else if(event.type == 'touchend'){
self.end(event);
}
},
//滑动开始
start:function(event){
var touch = event.targetTouches[0]; //touches数组对象获得屏幕上所有的touch,取第一个touch
startPos = {x:touch.pageX,y:touch.pageY,time:+new Date}; //取第一个touch的坐标值
isScrolling = 0; //这个参数判断是垂直滚动还是水平滚动,默认0位水平,1为垂直
body.addEventListener('touchmove',this,false);
body.addEventListener('touchend',this,false);
},
//移动
move:function(event){
//当屏幕有多个touch或者页面被缩放过,就不执行move操作
if(event.targetTouches.length > 1 || event.scale && event.scale !== 1) return;
var touch = event.targetTouches[0];
endPos = {x:(touch.pageX - startPos.x),y:(touch.pageY - startPos.y)};
isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1:0; //isScrolling为1时,表示纵向滑动,0为横向滑动
//若有需要触摸跟随的事件在此处添加
},
//滑动释放
end:function(event){
var duration = +new Date - startPos.time; //滑动的持续时间
// console.log(isScrolling);
if(isScrolling == 1){ //当为垂直滚动时
if(Number(duration) > 10){ //滑动时间大于10ms
//判断是左移还是右移,当偏移量大于10时执行
if(endPos.y > 10){
console.log("向下");
}else if(endPos.y < -10){
console.log("向上");
}
}
}else{
if(Number(duration) > 10){
//判断是左移还是右移,当偏移量大于10时执行
if(endPos.x > 10){
console.log("向右");
}else if(endPos.x < -10){
console.log("向左");
}
}
}
//解绑事件
body.removeEventListener('touchmove',this,false);
body.removeEventListener('touchend',this,false);
}
},
//初始化
init:function(){
var self = this; //this指slider对象
if(!!self.touch){
self.slider.addEventListener('touchstart',self.events,false);
//addEventListener第二个参数可以传一个对象,会调用该对象的handleEvent属性
}
}
};
slider.init();
</script>
</body>
</html>