首页 > 代码库 > 前端开发之 --- 各种运动
前端开发之 --- 各种运动
pc端动画离不开js计时器,无论是setInterval还是setTimeout。按照运动形式可以分为:匀速运动、缓冲运动、摩擦运动、以及其它高级运动。
缓冲运动:
下面封装了一个缓冲运动:(源于秒味):
function startMove(obj,json,endFn){ clearInterval(obj.timer); obj.timer = setInterval(function(){ var bBtn = true; for(var attr in json){ var iCur = 0; if(attr == ‘opacity‘){ if(Math.round(parseFloat(getStyle(obj,attr))*100)==0){ iCur = Math.round(parseFloat(getStyle(obj,attr))*100); }else{ iCur = Math.round(parseFloat(getStyle(obj,attr))*100) || 100; } }else{ iCur = parseInt(getStyle(obj,attr)) || 0; } var iSpeed = (json[attr] - iCur)/20; iSpeed = iSpeed >0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); if(iCur!=json[attr]){ bBtn = false; } if(attr == ‘opacity‘){ obj.style.filter = ‘alpha(opacity=‘ +(iCur + iSpeed)+ ‘)‘; obj.style.opacity = (iCur + iSpeed)/100; }else{ obj.style[attr] = iCur + iSpeed + ‘px‘; } } if(bBtn){ clearInterval(obj.timer); if(endFn){ endFn.call(obj); } } },30);}function getStyle(obj,attr){ if(obj.currentStyle){ return obj.currentStyle[attr]; }else{ return getComputedStyle(obj,false)[attr]; }}
缓冲运动是js运动世界中的最简单的运动,因为它的速度一直会递减到1,在像素的世界里,这使得物体最后可以恰恰好好运动到终点的位置,一个像素不差。
匀速运动:
运动的过程中速度恒定不变,所以运动到最后不一定恰好到达终点:
所以我们要在运动的最后增加判断,if下一次运动将超过终点(这里是400),就停止计时器,并把物体拉回到终点。示例如下:
<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>无标题文档</title><style>#div1 {width:100px; height: 100px; background: red; position: absolute; left: 0px; top: 30px;}</style><script>window.onload = function() { var oBtn = document.getElementById(‘btn‘); var oDiv = document.getElementById(‘div1‘); var iTimer = null; oBtn.onclick = function() { startMove(30,400); } function startMove(speed,target) { clearInterval(iTimer); iTimer = setInterval(function() { if (oDiv.offsetLeft >= target) { clearInterval(iTimer); oDiv.style.left = target+‘px‘; console.log(target) } else { oDiv.style.left = oDiv.offsetLeft + speed + ‘px‘; } }, 30); }}</script></head><body> <input type="button" value="动起来" id="btn" /> <div id="div1"></div></body></html>
//.....................这里补充pc端的其它运动........................
那么在移动端我们如何做动画呢?
例如我们做一个图片滑动切换效果,从来没有做过移动端的同学一般会把pc端做图片的思路把代码搬过来:
无外乎是pc端的onmousedown、onmousemove、onmouseup等事件...等等 这里可是移动端,这些事件还好使吗?对头,在移动端我们使用的是touch事件:
移动设备上面的click、mouseover、mousedown、mouseup扔然会触发,但是并不会真实的触发,在touchstart触发的300ms之后才会模拟触发click事件,mouseup、mousedown也将会在同一时刻触发。
webkit内的触摸事件:
event.touches数组是一组触摸事件所产生的触摸对象。
touches对象的相关属性:
触摸事件包含下列三个用于跟踪触摸的属性。
- touches:表示当前跟踪的触摸操作的Touch对象的数组(页面上的所有触摸)。
- targetTouches:特定于事件目标的Touch对象的数组(目标元素的所有当前触摸)。
- changedTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组(页面上最新更改的所有触摸-仅列出最后发生的触摸)。
如果你在使用touchend或者gestureend事件,那么changedTouches这个属性 非常重要。在这两种情况下,屏幕上都不会再出现手指,因此targetTouches和touches应该为空,但我们仍然可以通过 changedTouches数组来了解最后发生的事情 。
有了上面的一些参数,我们可以根据pc端的思路来组织一套代码:
<!doctype html><html><head> <meta charset="utf-8"> <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta name="format-detection" content="telephone=no"> <meta content="telephone=no" name="format-detection"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <title>。。。</title><style>*{padding: 0px;margin: 0px;font-size: 12px;}/*photo*/.photo {position:relative;width:100%;height:200px;overflow:hidden;}.photo .number{color:#fff;background:#000;padding:6px 10px;position:absolute;right:0;bottom:0;}.photo .number .page_now,.photo .number .page_all{padding:0 2px;}.photo_tit_box{position: absolute;bottom: 0px;left:0px;width:100%;height: 30px;background: #000;opacity:0.8;}.photo_tit{color: #fff;line-height: 30px;padding: 0 5px;float: left;width:70%;overflow:hidden;text-overflow:ellipsis;white-space: nowrap;}.photo_tit_page{float: right;margin-top: 12px;}.photo_tit_page li{height: 8px;line-height:8px;font-size:0px;width:8px;background: #fff;border-radius: 4px;background: #fff;float: left;margin-right: 5px;opacity:0.5;}.photo_tit_page li.active{opacity:1;}.detailpic_ul{position:absolute;top:0px;left:0px;}.detailpic_li{height:200px;text-align:center;background:#e1e1e1;float:left;background: #aad8f3;overflow: hidden;}.detailpic_li img{display:inline-block;height:200px;}/*photo end*/</style><script src="http://192.168.34.59:8080/target/target-script-min.js#anonymous"></script></head><body style="height:1000px;"> <div class="photo"> <ul class="detailpic_ul"> <li class="detailpic_li"><a href="http://www.baidu.com">1<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">2<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">3<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">4<img src="pic/2-1.jpg" alt="" /></a></li> </ul> </div> <script>function getStyle(obj,attr){ if(obj.currentStyle){ return obj.currentStyle[attr]; } else{ return getComputedStyle(obj,false)[attr]; }};//缓冲类型运动 setInterval 设置function startMove(obj,json,endFn){ clearInterval(obj.timer); obj.timer = setInterval(function(){ var bBtn = true; for(var attr in json){ var iCur = 0; var speed = 0; if(attr == ‘opacity‘){ if(Math.round(parseFloat(getStyle(obj,attr))*100)==0){ iCur = Math.round(parseFloat(getStyle(obj,attr))*100); } else{ iCur = Math.round(parseFloat(getStyle(obj,attr))*100) || 100; } }else{ iCur = parseInt(getStyle(obj,attr)) || 0; } iSpeed = (json[attr] - iCur)/4; iSpeed = iSpeed >0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); if(iCur!=json[attr]){ bBtn = false; } if(attr == ‘opacity‘){ obj.style.filter = ‘alpha(opacity=‘ +(iCur + iSpeed)+ ‘)‘; obj.style.opacity = (iCur + iSpeed)/100; } else{ obj.style[attr] = iCur + iSpeed + ‘px‘; } } if(bBtn){ clearInterval(obj.timer); if(endFn){ endFn.call(obj); } } },30);};//详细页面 图片切换function HousePicQh(option){ this.ulClass=option.ulClass; this.index=0; //记录index}//fn : 滑动完之后执行的函数HousePicQh.prototype.init=function(){ var _this=this; var winW=document.documentElement.clientWidth || document.body.clientWidth; var ulW=0; _this.oUl=document.querySelector("."+_this.ulClass); _this.oLi=_this.oUl.getElementsByTagName("li"); _this.liLen=_this.oLi.length; _this.liW=winW; //初始化 ul li 宽度 for(var i=0;i<_this.liLen;i++){ _this.oLi[i].style.width=winW+"px"; ulW+=winW; }; _this.oUl.style.width = ulW+"px"; _this.oUl.addEventListener("touchstart",function(ev){ var touchs = ev.touches[0]; _this.downX=touchs.clientX; _this.downL=this.offsetLeft; _this.oUl.addEventListener("touchmove",function(ev){ var touchs = ev.touches[0]; var nowX=touchs.clientX; var L=_this.downL+nowX-_this.downX; clearInterval(this.timer) this.style.left=L+"px"; }); //ev.preventDefault(); }) _this.oUl.addEventListener("touchend",function(ev){ var touchs = ev.changedTouches[0]; var nowX=touchs.clientX; var disL=nowX-_this.downX; if(Math.abs(disL)>30){ //默认 滑动需要超过30 disL>0?_this.index--:_this.index++; _this.index=_this.index==-1?0:_this.index; _this.index=_this.index==_this.liLen?_this.liLen-1:_this.index; var L=-_this.index*_this.liW; //运动 startMove(_this.oUl,{"left":L}); _this.oUl.onmousemove=null; }; });};//传入参数:ul的class名var HousePicQh1=new HousePicQh({"ulClass":"detailpic_ul"});HousePicQh1.init();</script></body></html>
上面代码中touchstart事件最后并没有阻止默认事件,如果阻止了默认事件,设备自带的页面滚动不会生效,点击图片上面的链接也不会跳转,不阻止的话会不会在不同的设备上有其它问题...这个怎么处理我也一直再找答案。
另外:touchend中的 var touchs = ev.changedTouches[0] 如果改成 var touchs = ev.touches[0] 是无效的,原因就是应为触发touchend后屏幕上面已经没有触摸点,所以这里只能用changedTouches捕获页面最后发生的触摸,这个需要注意下。
上面的代码是完全按照pc端做动画的思路来的,借用了这篇文章最开始我们封装的缓冲运动函数,这样的动画用到移动端会导致抖动,原因是:
在所有的浏览器中,js是单线程的,在某一时刻只能有一条语句执行,事件定时器等异步任务中,会加入到执行队列,然后等事件变得空闲时执行。如果浏览器在忙,需要等到空闲之后,下一帧的动画才会绘制,这样就造成了动画的抖动(不流畅)。可以想象,如果我们定时器中的代码执行太慢,会降低界面的相应速度,特别是在性能比较低的移动设备上面。所以我们应该尽量避免使用定时器(setInterval、setTimeout)来完成动画。
transition
我们来试试css过渡transition,它可以对付我们大多数的动画要求,让我们对上面的代码做一点改进。
我们要做的就是动态设置transition的值:(transition已经成为标准,要不要添加前缀取决于你想支持的浏览器版本)
在touchend的时候设置 ul的样式: "-webkit-transition:left .2s ease-out; transition:left .2s ease-out; ",实现touchend后的动画;
在touchmove的时候,我们是不需要这样的过渡的,我们需要的是图片列表紧密跟随手机移动,所以设置 "-webkit-transition:none; transition:none; "来取消过渡。代码如下:
<!doctype html><html><head> <meta charset="utf-8"> <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta name="format-detection" content="telephone=no"> <meta content="telephone=no" name="format-detection"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <title>...</title><style>*{padding: 0px;margin: 0px;font-size: 12px;}/*photo*/.photo {position:relative;width:100%;height:200px;overflow:hidden;}.photo .number{color:#fff;background:#000;padding:6px 10px;position:absolute;right:0;bottom:0;}.photo .number .page_now,.photo .number .page_all{padding:0 2px;}.photo_tit_box{position: absolute;bottom: 0px;left:0px;width:100%;height: 30px;background: #000;opacity:0.8;}.photo_tit{color: #fff;line-height: 30px;padding: 0 5px;float: left;width:70%;overflow:hidden;text-overflow:ellipsis;white-space: nowrap;}.photo_tit_page{float: right;margin-top: 12px;}.photo_tit_page li{height: 8px;line-height:8px;font-size:0px;width:8px;background: #fff;border-radius: 4px;background: #fff;float: left;margin-right: 5px;opacity:0.5;}.photo_tit_page li.active{opacity:1;}/*图片切换*/.detailpic_ul{position:absolute;top:0px;left:0px;}.detailpic_li{height:200px;text-align:center;background:#e1e1e1;float:left;background: #aad8f3;overflow: hidden;}.detailpic_li img{display:inline-block;height:200px;}/*photo end*/</style></head><body style="height:1000px;"> <div class="photo"> <ul class="detailpic_ul"> <li class="detailpic_li"><a href="http://www.baidu.com">1<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">2<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">3<img src="pic/2-1.jpg" alt="" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">4<img src="pic/2-1.jpg" alt="" /></a></li> </ul> </div> <script>//详细页面 图片切换function HousePicQh(option){ this.ulClass=option.ulClass; this.index=0; //记录index}//fn : 滑动完之后执行的函数HousePicQh.prototype.init=function(){ var _this=this; var winW=document.documentElement.clientWidth || document.body.clientWidth; var ulW=0; _this.oUl=document.querySelector("."+_this.ulClass); _this.oLi=_this.oUl.getElementsByTagName("li"); _this.liLen=_this.oLi.length; _this.liW=winW; //初始化 ul li 宽度 for(var i=0;i<_this.liLen;i++){ _this.oLi[i].style.width=winW+"px"; ulW+=winW; }; _this.oUl.style.width = ulW+"px"; _this.oUl.addEventListener("touchstart",function(ev){ var touchs = ev.touches[0]; _this.downX=touchs.clientX; _this.downL=this.offsetLeft; _this.oUl.addEventListener("touchmove",function(ev){ var touchs = ev.touches[0]; var nowX=touchs.clientX; var L=_this.downL+nowX-_this.downX; this.style.webkitTransition="none"; //新添加 this.style.transition="none"; //新添加 this.style.left=L+"px"; }); //ev.preventDefault(); }) _this.oUl.addEventListener("touchend",function(ev){ var touchs = ev.changedTouches[0]; var nowX=touchs.clientX; var disL=nowX-_this.downX; if(Math.abs(disL)>30){ //默认 滑动需要超过30 disL>0?_this.index--:_this.index++; _this.index=_this.index==-1?0:_this.index; _this.index=_this.index==_this.liLen?_this.liLen-1:_this.index; var L=-_this.index*_this.liW; this.style.webkitTransition="left .2s ease-out"; //新添加 this.style.transition="left .2s ease-out"; //新添加 this.style.left=L+"px"; //新添加 _this.oUl.onmousemove=null; }; });};//传入参数:ul的class名var HousePicQh1=new HousePicQh({"ulClass":"detailpic_ul"});HousePicQh1.init();</script></body></html>
如果我想再动画完成之后添加一个回调函数,改怎么处理?例如我想在每一次图片切换完之后log一下当前显示的是第几张图片,改如何传入我的回调函数呢?
试试transitionend吧
首先需要针对不同的浏览器版本添加不同的前缀,并赋给eventName:
var trans="transition";var eventName="transitionend";var transitions = { ‘transition‘:‘transitionend‘, ‘OTransition‘:‘oTransitionEnd‘, ‘MozTransition‘:‘transitionend‘, ‘WebkitTransition‘:‘webkitTransitionEnd‘, ‘MsTransition‘:‘msTransitionEnd‘ }for (t in transitions){ if(document.body.style[t]){ trans = t; eventName = transitions[t]; break; }}
剩下的就简单了,只需要给图片列表ul添加事件就可以了
_this.oUl.addEventListener(eventName,function(ev){ console.log(_this.index+1) });
完整代码:
<!doctype html><html><head> <meta charset="utf-8"> <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta name="format-detection" content="telephone=no"> <meta content="telephone=no" name="format-detection"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <title>...</title><style>*{padding: 0px;margin: 0px;font-size: 12px;}/*photo*/.photo {position:relative;width:100%;height:200px;overflow:hidden;}.photo .number{color:#fff;background:#000;padding:6px 10px;position:absolute;right:0;bottom:0;}.photo .number .page_now,.photo .number .page_all{padding:0 2px;}.photo_tit_box{position: absolute;bottom: 0px;left:0px;width:100%;height: 30px;background: #000;opacity:0.8;}.photo_tit{color: #fff;line-height: 30px;padding: 0 5px;float: left;width:70%;overflow:hidden;text-overflow:ellipsis;white-space: nowrap;}.photo_tit_page{float: right;margin-top: 12px;}.photo_tit_page li{height: 8px;line-height:8px;font-size:0px;width:8px;background: #fff;border-radius: 4px;background: #fff;float: left;margin-right: 5px;opacity:0.5;}.photo_tit_page li.active{opacity:1;}/*图片切换*/.detailpic_ul{position:absolute;top:0px;left:0px;}.detailpic_li{height:200px;text-align:center;background:#e1e1e1;float:left;background: #aad8f3;overflow: hidden;}.detailpic_li img{display:inline-block;height:200px;}/*photo end*/</style></head><body style="height:1000px;"> <div class="photo"> <ul class="detailpic_ul"> <li class="detailpic_li"><a href="http://www.baidu.com">1<img src="http://www.mamicode.com/pic/2-1.jpg" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">2<img src="http://www.mamicode.com/pic/2-1.jpg" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">3<img src="http://www.mamicode.com/pic/2-1.jpg" /></a></li> <li class="detailpic_li"><a href="http://www.baidu.com">4<img src="http://www.mamicode.com/pic/2-1.jpg" /></a></li> </ul> </div> <script>//添加浏览器前缀var trans="transition";var eventName="transitionend";var transitions = { ‘transition‘:‘transitionend‘, ‘OTransition‘:‘oTransitionEnd‘, ‘MozTransition‘:‘transitionend‘, ‘WebkitTransition‘:‘webkitTransitionEnd‘, ‘MsTransition‘:‘msTransitionEnd‘ }for (t in transitions){ if(document.body.style[t]){ trans = t; eventName = transitions[t]; break; }}//详细页面 图片切换function HousePicQh(option){ this.ulClass=option.ulClass; this.index=0; //记录index}//fn : 滑动完之后执行的函数HousePicQh.prototype.init=function(){ var _this=this; var winW=document.documentElement.clientWidth || document.body.clientWidth; var ulW=0; _this.oUl=document.querySelector("."+_this.ulClass); _this.oLi=_this.oUl.getElementsByTagName("li"); _this.liLen=_this.oLi.length; _this.liW=winW; //初始化 ul li 宽度 for(var i=0;i<_this.liLen;i++){ _this.oLi[i].style.width=winW+"px"; ulW+=winW; }; _this.oUl.style.width = ulW+"px"; _this.oUl.addEventListener("touchstart",function(ev){ var touchs = ev.touches[0]; _this.downX=touchs.clientX; _this.downL=this.offsetLeft; _this.oUl.addEventListener("touchmove",function(ev){ var touchs = ev.touches[0]; var nowX=touchs.clientX; var L=_this.downL+nowX-_this.downX; this.style.webkitTransition="none"; this.style.transition="none"; this.style.left=L+"px"; }); //ev.preventDefault(); }) _this.oUl.addEventListener("touchend",function(ev){ var touchs = ev.changedTouches[0]; var nowX=touchs.clientX; var disL=nowX-_this.downX; if(Math.abs(disL)>30){ //默认 滑动需要超过30 disL>0?_this.index--:_this.index++; _this.index=_this.index==-1?0:_this.index; _this.index=_this.index==_this.liLen?_this.liLen-1:_this.index; var L=-_this.index*_this.liW; this.style.webkitTransition="left .2s ease-out"; this.style.transition="left .2s ease-out"; this.style.left=L+"px"; _this.oUl.onmousemove=null; }; }); //下面是新添加代码 _this.oUl.addEventListener(eventName,function(ev){ console.log(_this.index+1) });};//传入参数:ul的class名var HousePicQh1=new HousePicQh({"ulClass":"detailpic_ul"});HousePicQh1.init();</script></body></html>
前端开发之 --- 各种运动