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";
        }
}

 

posted @ 2018-04-12 15:24  追忆枉然  阅读(173)  评论(0编辑  收藏  举报