javascript封装animate的过程

首先,我们需要先封装一个css方法,用以获取元素的样式。

//elem    元素    attr    元素的属性名
function css(elem,attr){
        return elem.currentStyle ? elem.currentStyle[attr]  :   window.getComputedStyle(elem)[attr];
} 

然后,我们想要一个元素的width动起来,
怎么做?给它一个定时器,每隔一段时间改变它的width值,到了想要的值时就清除定时器。如果还想改变别的样式值呢?那么就给这个定时器封装成函数方法,把里面要改变的变成变量,需要用的时候,调用它,传入想改变的样式名就行了。

此时就有了这样的方法:

//elem 元素  attr  属性名  value 属性的改变值
function animate(elem,attr,value){
        var timer = setInterval(function(){
                //设置变量start存储元素要改变的样式的初值
                var start = parseInt( css(elem,attr) ),
                       
                //设置变量speed样式改变的速度
                var speed = (value-start)/10;
                //当speed负值或者正值时需要给它取整,以免出现start永远不能等于value的情况
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                start += speed;
                if(start == value){
                        clearInterval(timer);
                }else{
                         elem.style[attr] = start + 'px';
                   }
            },13)
}

但是,这样的方法不能实现opacity的改变
那么就有了这样的方法:

function animate(elem,attr,value){
        var timer = setInterval(function(){
                //设置变量end存储元素要改变的样式的最终值
                var start = 0, end = 0;
                if(attr ==  'opacity'){
                     //Math.round防止无限小数兼容ie
                    start = Math.round( parseFloat( css(elem,attr) )*100 );
                    end = value*100; 
                }else{
                        start = parseInt( css(elem,attr) );
                        end = value; 
                    }
                var speed = (end-start)/10;
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                start += speed;
                if(start == end){
                        clearInterval(timer);
                }else{
                        if(attr == 'opacity'){
                                elem.style.opacity = start/100;
                                 //兼容IE 
                                elem.style.filter='alpha(opacity='+start+')';                          
                         }else{
                                elem.style[attr] = start + 'px';
                             }
                 }
            },13)
}

这样的方法,只是在元素本身被设置过属性情况下,那如果元素没有设置这个你想要改变的属性呢?
我们先来看下,当元素没有设置属性值的时候,CSS方法获取的是什么。
比如,获取一个left,css(div,’left’),有的浏览器会返回0px,但是有的浏览器会返回一个undefined或者auto或者其他他非数字的值;同样,关于opacity这个属性,其他浏览器会返回1,但是IE浏览器会返回一个undefined。

一般HTML+CSS布局的时候,大家通常会这样写透明度:filter:alpha(opacity=100);opacity:1;
因为IE浏览器的透明度是filter:alpha(opacity=100);这样的写法就是为了兼容IE的。

知道了原理,那么,这个animate就可以这样写了:

function animate(elem,attr,value){
        var timer = setInterval(function(){
               var start = 0, end = 0;
                if(attr ==  'opacity'){
                    //兼容IE  当没有设置fliter时,默认100;
                    if(!css(elem,attr)){
                           start = 100;
                      }else{
                              start = Math.round( parseFloat( css(elem,attr) )*100 );
                        }
                   end = value*100; 
                }else{
                        //兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
                        if(!parseInt(css(elem,attr))){
                            start = 0; 
                        }else{
                               start = parseInt( css(elem,attr) );
                           }
                        end = value; 
                    }
                var speed = (end-start)/10;
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                start += speed;
                if(start == end){
                        clearInterval(timer);
                }else{
                        if(attr == 'opacity'){
                                elem.style.opacity = start/100;
                                 //兼容IE 
                                elem.style.filter='alpha(opacity='+start+')';                          
                         }else{
                                elem.style[attr] = start + 'px';
                             }
                 }
            },13)
}

好了,元素单个的属性运动方法已经完毕。
那想要多个属性同时运动呢?
我们可以把attr和对应的value用对象存起来,比如这样 {width:100,height:100,opacity:1}。然后遍历这个对象,依次改变对象里的属性对应的值,也就是改变了元素的属性值。对象遍历我们用for in 方法。
比如,

        var json = {width:100,height:100,opacity:1};
           for(var i in json){
                console.log(i)
            } 

你会发现得到的是width,height,opacity这些字符串。那么json[i]就代表了json对象里的属性值。
还有,前面的方法,在实际运用中会出现BUG。因为可能会出现,上一次运动还没完,用户又开始运动,即开了多个定时器的情况。这样就会出现问题。
通常做法是在开始运动前就清除定时器,这样就不会出现问题。
我们的前面的方法里直接在定时器前面写clearInterval(timer)是不行的,会出错 timer is not defined 。
那么我们可以这样,把元素看成一个对象,把timer设置成元素的一个属性 ,即elem.timer。
所以,

function animate(elem,json){
        clearInterval(elem.timer);
        elem.timer = setInterval(function(){
                //遍历json,依次改变每个属性的属性值
               for(var attr in json){
                       var start = 0, end = 0;
                        if(attr ==  'opacity'){
                          //兼容IE 当没有设置fliter时,默认100;
                            if( !css(elem,attr) ){
                                   start = 100;
                              }else{
                                   //Math.round防止出现无限小数
                                  start = Math.round( parseFloat( css(elem,attr) )*100 );
                              }
                              end = parseFloat(json[attr])*100; 
                        }else{
                                 //兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
                              if(!parseInt(css(elem,attr))){
                                start = 0; 
                            }else{
                                   start = parseInt( css(elem,attr) );
                              }
                               end =  parseInt(json[attr]); 
                        }
                    var speed = (end-start)/10;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    start += speed;
                    if(start == end){
                            clearInterval(elem.timer);
                     }else{
                                if(attr == 'opacity'){
                                elem.style.opacity = start/100;
                                 //兼容IE 
                                elem.style.filter='alpha(opacity='+start+')';                          
                        }else{
                                elem.style[attr] = start + 'px';
                             }           
                } 
        },13)
}    

这样貌似写完了,其实还没完,这样写会有BUG,会出现只实现了第一个属性的运动,而其他属性的运动没有到达你想要的最终值。因为方法里是这样判定的,当start end的时候,判定运动终止,但是,现在不止有一个start和end,第一个属性的startend完成了,但是后面的属性的start==end并没有完成。
所以,我们可以用布尔值来做判定条件。

 function animate(elem,json){
        clearInterval(elem.timer);
        elem.timer = setInterval(function(){
                //运动终止条件
                var flag = true;
                //遍历json,依次改变每个属性的属性值
               for(var attr in json){
                       var start = 0, end = 0;
                        if(attr ==  'opacity'){
                        //兼容IE 当没有设置fliter时,默认100;
                            if( !css(elem,attr) ){
                                   start = 100;
                              }else{
                                   //Math.round防止出现无限小数
                                  start = Math.round( parseFloat( css(elem,attr) )*100 );
                              }
                              end = parseFloat(json[attr])*100; 
                        }else{
                               //兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
                              if(!parseInt(css(elem,attr))){
                                start = 0; 
                            }else{
                                   start = parseInt( css(elem,attr) );
                              }
                               end =  parseInt(json[attr]); 
                        }
                    var speed = (end-start)/10;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    start += speed;
                    //当start != end时,flag = false;,运动继续
                    if(start != end){
                            flag = false;
                     }
                      if(attr == 'opacity'){
                           elem.style.opacity = start/100;
                           //兼容IE 
                            elem.style.filter='alpha(opacity='+start+')';                          
                        }else{
                                elem.style[attr] = start + 'px';
                             }           
                } 
                 //当所有的start == end时,flag = true;,运动停止
                if(flag){
                         clearInterval(elem.timer);
                 }
                 
        },13)
}    

如果想把animate做成链式运动方法,很简单,只要多加个参数fn,在最后判定运动完成时回调函数就行。改变运动的快慢也很简单,只要再加个参数time。

所以最终版:

function animate(elem,json,time,fn){
        clearInterval(elem.timer);
        elem.timer = setInterval(function(){
                //运动终止条件
                var flag = true;
                //遍历json,依次改变每个属性的属性值
               for(var attr in json){
                       var start = 0, end = 0;
                        if(attr ==  'opacity'){
                        //兼容IE 当没有设置fliter时,默认100;
                            if( !css(elem,attr) ){
                                   start = 100;
                              }else{
                                   //Math.round防止出现无限小数
                                  start = Math.round( parseFloat( css(elem,attr) )*100 );
                              }
                              end = parseFloat(json[attr])*100; 
                        }else{
                            //兼容ie 没有设置attr时,默认0
                              if( !parseInt(css(elem,attr)) ){
                                start = 0; 
                            }else{
                                   //parseInt防止json对象里属性值带px
                                   start = parseInt( css(elem,attr) );
                              }
                               end =  parseInt(json[attr]); 
                        }
                        //如果有time实参传入,就执行speed = (end-start)/ time ,否则speed = (end-start)/10;
                        var speed = (end-start)/( time || 10 );
                        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                        start += speed;
                        //当start != end时,flag = false;,运动继续
                        if(start != end){
                            flag = false;
                         }
                          if(attr == 'opacity'){
                           elem.style.opacity = start/100;
                           //兼容IE 
                            elem.style.filter='alpha(opacity='+start+')';                          
                          }else{
                                elem.style[attr] = start + 'px';
                            }           
                } 
                 //当所有的start == end时,flag = true;,运动停止
                if(flag){
                         clearInterval(elem.timer);
                       //运动终止时,如果有函数实参传入就执行回调函数
                        fn & fn(); 
                 }
                 
        },13)
}    

如果您觉得上面的内容对您有帮助,可以打赏支持一下!

打赏

关键词:javascript

网友留言(0 条)

发表评论

验证码