JS运动
JS运动对元素样式操作,
运动基础:
如何让div运动起来?
如下:
1.设置元素为决定定位,只有绝对对位后,left,top等值生效
2.定时器的使用(动态改变值),这里使用setInterval()每隔一段时间执行代码
setInterval(函数,交互时间);
clearInterval(定时器名),
3.获取当前位置、大小等等。offsetLeft(当前元素相对父元素偏执)
4.速度--物体运动的快慢
根据上面的代码实现简单的运动
var box=document.getElementById("box"); function startMove(curEle){ timer=setInterval(function(){ curEle.style.left=curEle.offsetLeft+5+"px"; },30) } startMove(box)
上面的这个动画不会停止,ok,我们设置一个停止条件
var box=document.getElementById("box"); var timer=null; function startMove(curEle,target){ timer=setInterval(function(){ if (curEle.offsetLeft===target){ clearInterval(timer); return; } curEle.style.left=curEle.offsetLeft+5+"px"; },30) } startMove(box,800)
我们发现一个BUG,个别值不会触发停止条件
BUG:
1.运动到某些值无法停止
2.重复点击会加快速度
3.速度无法改变
解决:
1.一会搞
2.每次点击清除定时器
3.把速度以变量保存
var box=document.getElementById("box"); var timer=null; function startMove(curEle,target){ clearInterval(timer) timer=setInterval(function(){ var speed=5; if (curEle.offsetLeft===target){ clearInterval(timer); return; } curEle.style.left=curEle.offsetLeft+speed+"px"; },30) } btnLeft.onclick=function(){startMove(box,800)}
等一下,方向为什么不能变:我现在想向左走了
var box=document.getElementById("box"); var timer=null; function startMove(curEle,target){ clearInterval(timer); var curPos=curEle.offsetLeft; // 判断当前值与目标值谁大谁小 var speed=curPos>target?-7:7; timer=setInterval(function(){ var curLeft=curEle.offsetLeft; // 先加速度,计算后的值进行判断 curLeft+=speed; if (speed>0&&curLeft>=target){ curEle.style.left=target+"px"; clearInterval(timer); return; }else if(speed<0&&curLeft<=target){ curEle.style.left=target+"px"; clearInterval(timer); return; } curEle.style.left=curLeft+"px"; },30) } btnLeft.onclick=function(){startMove(box,document.documentElement.clientWidth-box.offsetWidth)}
如果是透明度,我们怎么办,需要特殊判断
补充的两个方法
getcss获取样式
function getCss(curEle,attr){ var val=null; if (!/MSIE (6|7|8)/.test(window.navigator.userAgent)){ val=window.getComputedStyle(curEle,null)[attr] }else{ if (attr==="opacity"){ val=curEle.currentStyle["filter"]; var reg=/^alpha\(opacity=(\d+(?:\.\d+)?)\)/ val=reg.exec(val)?reg.exec(val)[1]/100:1; }else{ val=curEle.currentStyle[attr]; } } // 去单位 var reg=/^(-?\d+(\.\d+)?)(px|pt|rem|em)$/; return reg.test(val)?parseFloat(val):val; }
setcss设置样式
function setCss(curEle,attr,value){ if (attr==="opacity"){ curEle["style"]["opacity"]=value; curEle["style"]["filter"]="alpha(opacity="+value*100+")"; return } if (attr==="float"){ curEle["style"]["cssFloat"]=value; curEle["style"]["styleFloat"]=value; return } var reg=/^(width|height|left|top|bottom|right|((maring|padding)|(Left|Right|Top|Bottom)?))$/i; if (reg.test(attr)){ if (!isNaN(value)){ value+="px"; } } curEle["style"][attr]=value; }
思路一:步长法
var duration=2000,interval=10,target=600,change=target-getCss(box,"left"); var step=change/duration*interval; var timer=setInterval(function(){ var curLeft=getCss(box,"left"); curLeft+=step; if (curLeft>target){ setCss(box,target) clearInterval(timer); return } curLeft+=step; setCss(box,"left",curLeft) },interval)
思路二、
function Linear(t,d,c,b){ return t/d*c+b } var duration=2000,time= 0,begin=getCss(box,"left"),target=1200; var change=target-begin; var timer=setInterval(function(){ time+=10; var pos=Linear(time,duration,change,begin); console.log(pos) if (time>duration){ setCss(box,target) clearInterval(timer) return } setCss(box,"left",pos) },10)
思路三:使用setTimeout实现定时器
//使用递归思想实现
var timer=null; function move(){ // 每一次执行,清空定时器 clearTimeout(timer); var duration=2000,interval=10,target=600,change=target-getCss(box,"left"); var step=change/duration*interval; var curLeft=getCss(box,"left"); curLeft+=step;
// 当前left+step大于target时;直接让left=target if (curLeft>target){ setCss(box,"left",curLeft); clearTimeout(timer); return } setCss(box,"left",curLeft); timer=setTimeout(move,interval) } move();
进化:可以用按钮控制方向
function move(target){ _move() //此处为什么+小_move,看定时器 function _move(){ clearTimeout(box.timer) var duration=2000,interval=10; var curLeft=getCss(box,"left"); if (target>curLeft){ curLeft+=5; if (curLeft>target){ curLeft=target; clearTimeout(box.timer) } }else if(target<curLeft){ curLeft-=5; if (target>curLeft){ curLeft=target; clearTimeout(box.timer) } } setCss(box,"left",curLeft); box.timer=window.setTimeout(_move,interval) //定时器之前因为move有参数,所以要先执行个匿名函数,再匿名函数中执行move(target),每次递归都会创建一个多余的作用域,浪费资源 } } btL.onclick=function(){ var target=0; move(target); } btR.onclick=function(){ var target=(document.documentElement.clientWidth||document.body.clientWidth)-getCss(box,"width"); move(target) }
同元素多样式运动
function linear(t,d,b,c){ return t/d*c+b; } function move(curEle,target,duration,callback){
//先计算出,初始值与距离存在两个变量里 var begin={},change={}; duration=duration||2000; for(var key in target){ if (target.hasOwnProperty(key)){ begin[key]=utils.css(curEle,key); change[key]=target[key]-begin[key]; } }
var time=0; curEle.timer=setInterval(function(){ time+=10; for(var key in target){ if (target.hasOwnProperty(key)){
//当time 时间超过了总时间停止运动 if (time>duration){ utils.css(curEle,key,target[key]); clearInterval(curEle.timer);
//停止运动时执行回调函数 callback&&callback() return; } var curPos=linear(time,duration,begin[key],change[key]); utils.css(curEle,key,curPos) } } },30) } move(box,{ left:"100", top:"100", opacity:"1" },200,function(){ console.log("动画执行完了") })
万能动画框架
//以下是动画的算法 var zhufengEffect = { //当前时间*变化量/持续时间+初始值 zfLinear: function(t,b,c,d){ return c*t/d + b; }, Quad: {//二次方的缓动(t^2); easeIn: function(t,b,c,d){ return c*(t/=d)*t + b; }, easeOut: function(t,b,c,d){ return -c *(t/=d)*(t-2) + b; }, easeInOut: function(t,b,c,d){ if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; } }, Cubic: {//三次方的缓动(t^3) easeIn: function(t,b,c,d){ return c*(t/=d)*t*t + b; }, easeOut: function(t,b,c,d){ return c*((t=t/d-1)*t*t + 1) + b; }, easeInOut: function(t,b,c,d){ if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; } }, Quart: {//四次方的缓动(t^4); easeIn: function(t,b,c,d){ return c*(t/=d)*t*t*t + b; }, easeOut: function(t,b,c,d){ return -c * ((t=t/d-1)*t*t*t - 1) + b; }, easeInOut: function(t,b,c,d){ if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; } }, Quint: {//5次方的缓动(t^5); easeIn: function(t,b,c,d){ return c*(t/=d)*t*t*t*t + b; }, easeOut: function(t,b,c,d){ return c*((t=t/d-1)*t*t*t*t + 1) + b; }, easeInOut: function(t,b,c,d){ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; } }, Sine: {//正弦曲线的缓动(sin(t)) easeIn: function(t,b,c,d){ return -c * Math.cos(t/d * (Math.PI/2)) + c + b; }, easeOut: function(t,b,c,d){ return c * Math.sin(t/d * (Math.PI/2)) + b; }, easeInOut: function(t,b,c,d){ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; } }, Expo: {//指数曲线的缓动(2^t); easeIn: function(t,b,c,d){ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, easeOut: function(t,b,c,d){ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, easeInOut: function(t,b,c,d){ if (t==0) return b; if (t==d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; } }, Circ: {//圆形曲线的缓动(sqrt(1-t^2)); easeIn: function(t,b,c,d){ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; }, easeOut: function(t,b,c,d){ return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, easeInOut: function(t,b,c,d){ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; } }, Elastic: {//指数衰减的正弦曲线缓动; easeIn: function(t,b,c,d,a,p){ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (!a || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; }, easeOut: function(t,b,c,d,a,p){ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (!a || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b); }, easeInOut: function(t,b,c,d,a,p){ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); if (!a || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; } }, Back: {//超过范围的三次方缓动((s+1)*t^3 - s*t^2); easeIn: function(t,b,c,d,s){ if (s == undefined) s = 1.70158; return c*(t/=d)*t*((s+1)*t - s) + b; }, easeOut: function(t,b,c,d,s){ if (s == undefined) s = 1.70158; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, easeInOut: function(t,b,c,d,s){ if (s == undefined) s = 1.70158; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; } }, zfBounce: {//指数衰减的反弹缓动。 easeIn: function(t,b,c,d){ return c - zhufengEffect.zfBounce.easeOut(d-t, 0, c, d) + b; }, easeOut: function(t,b,c,d){ if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } }, easeInOut: function(t,b,c,d){ if (t < d/2) return zhufengEffect.zfBounce.easeIn(t*2, 0, c, d) * .5 + b; else return zhufengEffect.zfBounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b; } } } ////////////////////以上都是算法 /* animate的参数:当effect参数是数字的情况 effect:是指定的动画效果,它可以由一个简单数字来快捷表示某种常用的动画效果,也可以以数组的方式来指定某个不常用的动画效果。还可以不给effect参数传值,则动画使用默认的效果(我们指定减速的效果为默认效果) 如果用数字来快捷表示某种常用效果效,则 0:减速效果Expo 1:匀速效果:linear 2:弹性:Elastic 3:返回:Back 4:反弹:Bounce */ /* effect参数的第二情况:当它是一个数组的情况 animate(ele,{left:600},1500,['zfBounce','easeInOut'],callback) zhufengEffect.zfBounce.easeInOut zhufengEffect["zfBounce"]["easeInOut"]; var a=['zfBounce','easeInOut']; zhufengEffect[a[0]][a[1]];//要明白这一行和161,162是一样的 */ /* animate(ele,{left:600},1500,callback) 当第四个参数是function的时候(按理说第五个参数才应该是回调函数呢,现在由于第四个参数没有传,但是回调函数传进来了,则会出现回调函数成为第四个参数的可能) 现在则需要把这个方法当成回调方法来执行。在这种情况下,则认为没指定动画效果,而是把第四个参数按成第五个参数:回调方法去执行 */ /* 方法重载和方法重写,是面向对象的“多态”中的概念。面向对象有三大基本特征:封装、继承、多态(一个方法有多种形态,方法重载) JS中是不支持方法重载的,但是可以使用判断参数的类型或参数个数来模拟实现方法重载 我们的animate方法就是这样 方法重写是指写义在子类上的方法覆盖了父类上的方法,比如Array.prototype.toString覆盖了Object.prototype.toString,在Array类的实例上再使用toString方法,就访问不到Object类上的toString方法了 */ function animate(ele,obj,duration,effect,callback){ var fnEffect=zhufengEffect.Expo.easeOut; if(typeof effect == "number"){ switch(effect){ case 0: break; case 1: fnEffect=zhufengEffect.zfLinear; break; case 2: fnEffect=zhufengEffect.Elastic.easeOut; break; case 3: fnEffect=zhufengEffect.Back.easeOut; break; case 4: fnEffect=zhufengEffect.zfBounce.easeOut; break; } }else if(effect instanceof Array){ if(effect.length==2) fnEffect=zhufengEffect[effect[0]][effect[1]]; }else if(typeof effect =="function"){ callback=effect; } var oBegin={};//用来保存多个方向begin; var oChange={};//用来保存多个方向的change; var flag=0;//用来记录各个方向的距离是否有效 for(var attr in obj){ var target=obj[attr] var begin=animate.getCss(ele,attr); var change=target-begin; if(change){//判断一下此方向的运动距离有效,不为0 oBegin[attr]=begin; oChange[attr]=change; flag++; } }//for in 循环结束 if(!flag)return;//如果各个方向的运动距离都是0,则结束动画的执行 var interval=15; var times=0; clearInterval(ele.timer); function step(){ times+=interval; if(times<duration){ for(var attr in oChange){ var change=oChange[attr]; var begin=oBegin[attr]; //var val=times/duration*change+begin; var val=fnEffect(times,begin,change,duration); animate.setCss(ele,attr,val); } }else{ for(var attr in oChange){ var target=obj[attr]; animate.setCss(ele,attr,target); } clearInterval(ele.timer); ele.timer=null; if(typeof callback == "function"){ callback.call(ele); } } } ele.timer=setInterval(step,interval); }; animate.getCss=function(ele,attr){ if(window.getComputedStyle){ return parseFloat(window.getComputedStyle(ele,null)[attr]); }else{ if(attr=="opacity"){ var val=ele.currentStyle.filter; //"alpha(opacity=50)";//匹配到这样的一个字符串,然后把这个字符串中的数字部分拿到 var reg=/alpha\(opacity=(\d+(?:\.\d+)?)\)/; if(reg.test(val)){ return RegExp.$1/100; }else{ //如果没有给IE中的不透明度赋值,则上边的正则为false return 1;//如果没有给不透明度赋值,则应该把默认值1返回 } //方法没有返回值,则此方法执行结束后留下一个undefined。即:没有返回值的方法返回的是undefined }else{ return parseFloat(ele.currentStyle[attr]); } } } animate.setCss=function(ele,attr,val){ if(attr=="opacity"){ ele.style.opacity=val; ele.style.filter="alpha(opacity="+val*100+")"; }else{ ele.style[attr]=val+"px"; } }