JavaScript封装简易动画函数

作者: 是阿详 | 来源:发表于2017-08-16 14:19 被阅读425次
      在学习javascript动画效果的过程,动画函数一定是少不了的,所以在初级学习的过程中,封装好一个动画函数可以直接调用能够帮我们省下更多的学习时间。下面是我一步步完善动画函数的过程。
    
    1、简单的右移函数:鼠标点击按钮,box向右移动一定的位置
    //封装右移动画函数
            function animateMoveRight(element,target){
                //通过offsetLeft获取当前位置的left值
                var left = element.offsetLeft;
    
                //开启定时器
                var timer = setInterval(function(){
                    //当前位置每一次+5 实现匀速运动
                    left += 5;
                    //将每一次改变后的left值设置给元素
                    element.style.left = left + 'px';
                    //判断动画结束的标志 到达目标位置停止计时器
                    if(left >= target){
                        clearInterval(timer);
                    }    
                },30);
            }
            //在window.onload中调用封装好的动画函数
            window.onload =function () {
                var box = document.getElementById('box');
    
                //动态创建按钮
                for(var i = 200; i < 1000; i += 100){
                    //创建按钮
                    var button = document.createElement('button');            
                    button.innerHTML = '右移' + i + 'px';
                    //获取按钮对应的移动值
                    button.index = i;
                    //点击按钮
                    button.onclick = function(){
                        // console.log(this.index);
                        //调用封装好的右移函数
                        animateMoveRight(box,this.index);
                    }
                    //添加按钮
                    document.body.appendChild(button);
                }            
            }
        </script>
    

    实现效果:


    右移效果.png
    2、封装同时解决左右移动的动画函数

    上面的函数只能实现单向的向右移动,点击‘右移500px’按钮后,再点击‘右移200px’按钮无法回到200px处。下面实现这种效果。

    //封装一个函数  同时解决左右移动的问题
            function animateMove(element,target){
                clearInterval(timer)
                var left = element.offsetLeft;
    
                //设置步长 表示一步动作的差值
                //通过比较element当前的left值和target值的大小,来确定平移方向
                var step = (target - left) / 10;
    
                var timer = setInterval(function(){
                    //如果目标值大于当前的left值,step为正数,向右移动
                    //如果目标值小于当前的left值,step为负数,向左移动
                    left += step;
                    box.style.left = left + 'px';
    
                    //判断停止动画
                    //比较差值,取绝对值,当两者的差值小于了步进值时,停止动画,
                    if(Math.abs(target - left) <= Math.abs(step)){
                        clearInterval(timer);
                        element.style.left = target + 'px';
                    }    
                },30);
            }
    
            
            //调用函数
            window.onload =function () {
                var box = document.getElementById('box');
                
                for(var i = 200; i < 1000; i += 100){
                    var button = document.createElement('button');    
    
                    button.innerHTML = '右移' + i + 'px';
                    button.target = i;
                    button.onclick = function(){
                        animateMove(box,this.target);
                    }
                    document.body.appendChild(button);
                }            
            }
    

    效果展示:(当前在400px位置,点击200px按钮会回到200px位置)

    左右移动.png
    3、封装带有指定属性的动画函数

    在设置动画的过程中,不只是左右移动那么简单,我们想要是的是想改变元素的什么属性就能够改变。在开始之前先了解怎么访问并获取css的属性。

    3.1 访问css属性

    我们知道element.style.xxx 只能够获取行内式属性,无法获取在style中设置的属性;offset获取的是本身实际获得的,没有定位,没有设left夜里可以获得。我们通过设置element.currentStyle.xxx(ie浏览器)或者window.getComputedStyle(element, null).xxx(其他浏览器)获得。封装函数如下:

    1 //封装一个函数,用于获取某一个元素的某一条CSS属性值
     2         function getStyle(element, styleName){
     3             if(element.currentStyle){
     4                 return element.currentStyle[styleName];
     5             }else{
     6                 var computedStyle = window.getComputedStyle(element, null);
     7                 return computedStyle[styleName];
     8             }
     9         }
    10 
    11         window.onload = function(){
    12             var box = document.getElementById('box');
    13             var height = getStyle(box,'height');
    14             console.log(height); //200px
    15         }
    
    3.2 封装带有指定属性的动画函数
    //引入getStyle函数
            function getStyle(element, styleName){
                if(element.currentStyle){
                    return element.currentStyle[styleName];
                }else{
                    var computedStyle = window.getComputedStyle(element, null);
                    return computedStyle[styleName];
                }
            }
    
            //封装带有指定属性的的动画函数 (元素名,属性名,目标值)
            var timer;
            function animate(element,styleName,target){
                clearInterval(timer);
                //获取该元素当前的属性
                var current = parseInt(getStyle(element,styleName));
                //设置步长 定值
                var step = (target - current) / 10;
                
                //开启动画设置滚动效果移动 
                timer = setInterval(function(){
                    //通过步长 一点的的改变current  直到达到target值
                    current += step;
                    //判断动画结束的标志 
                    //比较差值 当两者的差值小于了步进值时,停止动画 
                    if(Math.abs(target - current) <= Math.abs(step)){
                        clearInterval(element.timer);
                        current = target;  //存在一点误差 强制将current归为目标值
                    }
    
                    //将改变后当前动画中的style值,设置给动画的元素
                    element.style[styleName] = current + 'px';
                },30);
            }
    
            //调用
            window.onload = function(){
                var box =document.getElementById('box');
                
                animate(box,'width',500);
            } 
    
    4、封装带有多个属性的动画函数(同时运动)

    前面虽然能根据传入的属性参数改变元素运动,但是每次只能设置一种属性,如果同时调用只会显示最后一种效果。所以下面使用json参数传入多个属性。

     <script type="text/javascript">
        //json格式参考
        function f(){
            var json ={left:100,top:50}
            for(var key in json){
                console.log(key); //打印属性 left top
                console.log(json[key]); //打印属性值 100  50
            }
        }
        f();
        </script>
    
    //获取属性的的网页中实际的(当前的)属性值
            function getStyle(element, styleName){
                if(element.currentStyle){
                    return element.currentStyle[styleName];
                }else{
                    var computedStyle = window.getComputedStyle(element, null);
                    return computedStyle[styleName];
                }
            }
    
            //封装带有多个属性的的动画函数    利用json参数
            function animate(element,json){
                clearInterval(element.timer);
                //由于多个属性的运动  为了避免一个属性完成后就停止定时器的现象,所以设置isStop
                //是否停止动画,默认为false表示不停止
                var isStop = false;
    
                //开启动画设置滚动效果移动 
                element.timer = setInterval(function(){
    
                    //1.每一次动画开启之前,默认设置isStop为true(定时器停止)
                    //2.如果只是一个属性完成不需要修改定时器,如果有属性没有    执行完,则设置isStop = false,继续开启定时器
                    //3.最后所有属性都完成后, 判断isStop值     如果为true,表示的属性均执行完成,关闭定时器
                    //1.
                    isStop = true;
    
                    //多个属性  分别计算每个属性当前值(实际值)/目标值/步长
                    //遍历json参数  分别获取key-属性名 json[key]-属性值
                    for(var key in json){
                        console.log(key);        //left top
                        console.log(json[key]);  // 100 50
    
                        //通过getStyle函数获取当前属性(key)的属性值即盒子的当前实际值
                        var current = parseInt(getStyle(element, key));
                        //获取json参数传入的每个属性对应的目标值
                        var target = json[key];
                        //分别设置每个属性步长
                        var step = (target - current) / 10;
                        step = step > 0 ? Math.ceil(step) : Math.floor(step);
    
                        //设置一步步的改变 直至达到目标值
                        current += step;
    
                        //判断(current += step)是否达到目标值 停止计时器
                        //2.其中一个属性完成,就不需要修改定时器
                        if(Math.abs(target -current) > Math.abs(step)){
                            isStop = false;
                        }else{ //强制将此属性设到target
                            current = target;
                        }
    
                        //设置运动后的值给元素, 改变其对应属性的属性值
                        element.style[key] = current + 'px';    
                    }
    
                    //3.所有的属性动画完成(for(key)结束),所有的定时器都为true,关闭定时器
                    if(isStop){
                        clearInterval(element.timer);
                        console.log('完成动画');
                    }
                },30);
            }
            //调用此函数
            window.onload = function(){
                var box =document.getElementById('box');
                document.onclick =function(){
                    //实现点击后,在一定时间内同时完成以下动作
                    animate(box,{
                        left:200,
                        top :200,
                        width:300,
                        height:300
                    });
                }
            }
    

    实现效果:鼠标点击后,在一定时间内盒子同时向左向下移动200px,并且宽高扩大到300px。

    5、函数的回归调用(上一个动画运动完成后下一个动画才开始运动)

    上面的代码实现了一个物体的多个属性同时运动,很多情况下会是一个物体的上一个动画完成后另一个动画才开始运动。所以我们在原来的基础上传入一个函数参数,function animate(element,json,fun){},在上一个动画完成后,开始调用下一个动画的函数参数。

      //回调函数
            function animate(element,json,fun){
                clearInterval(element.timer);
                console.log(element.offsetLeft + 'kaishiqian')
                var isStop = false;
    
                element.timer = setInterval(function(){
    
                    isStop = true;
    
                    for(var key in json){
                        console.log(key);        //left top
                        console.log(json[key]);  // 100 50
                        var current = parseInt(getStyle(element, key));
                        var target = json[key];
                        var step = (target - current) / 10;
                        step = step > 0 ? Math.ceil(step) : Math.floor(step);
                        current += step;
    
                        if(Math.abs(target -current) > Math.abs(step)){
                            isStop = false;
                        }else{ //强制将此属性设到target
                            current = target;
                        }
                        element.style[key] = current + 'px';    
                    }
    
                    if(isStop){
                        clearInterval(element.timer);
                        console.log('完成动画');
                        console.log(element.offsetLeft);
    
                        //上一个动画完成后,开始下一个动画
                        if(typeof fun == 'function'){
                            fun();
                        }
                    }
                },30);
            }
    
            window.onload = function(){
                var box =document.getElementById('box');
                document.onclick =function(){
                    //先向右移动到500px,接着宽高均扩大到300px,下移到150px,字体放大到30px
                    animate(box,{left:500}, function(){
                        animate(box,{width:300, height:300}, function(){
                            animate(box,{top:150}, function(){
                                animate(box,{fontSize:30}, null);
                            });
                        });
                    });
                }
            }
    

    实现效果:鼠标点击后,盒子先向右移动到500px,接着宽高均扩大到300px,下移到150px,字体放大到30px。

    6、封装带有opacity、z-index等属性的动画函数
    //思路:即查看current,target,step的值是否会因为opacity的传入而出错
            function animate(element,json,fun){
                clearInterval(element.timer);
                console.log(element.offsetLeft + 'kaishiqian')
                var isStop = false;
    
                element.timer = setInterval(function(){
    
                    isStop = true;
    
                    for(var key in json){
    
                        var current;
                        //如果传入的属性是opacity,取浮点型数
                        if(key == 'opacity'){
                            current = parseFloat(getStyle(element, key));
                        }else{
                            current = parseInt(getStyle(element, key));
                        }
    
                        //没问题
                        var target = json[key];
    
                        //只要不是opacity 都做向上或向下取整操作
                        var step = (target - current) / 10;
                        if(key != 'opacity'){
                            step = step > 0 ? Math.ceil(step) : Math.floor(step);
                        }
    
                        current += step;
    
                        //判断暂停动画
                        if(key == 'opacity'){
    
                            if(Math.abs(target -current) > 0.01){
                                isStop = false;
                            }else{ 
                                current = target;
                            }
                            element.style[key] = current + '';
    
                        }else{
    
                            if(Math.abs(target -current) > Math.abs(step)){
                                isStop = false;
                            }else{ //强制将此属性设到target
                                current = target;
                            }
                            
                            if(key == 'zIndex'){
                                //四舍五入
                                element.style.zIndex = Math.round(current);
                            }else{
                                element.style[key] = current + 'px';    
                            }
                            
                        }
                    }
                    if(isStop){
                        clearInterval(element.timer);
                        console.log('完成动画');
                        if(typeof fun == 'function'){
                            fun();
                        }
                    }
                },30);
            }
    
            window.onload = function(){
                var box =document.getElementById('box');
                document.onclick =function(){
    
                    animate(box, {opacity:0.3,zIndex:20}, null);
                }
            }
    

    实现效果:透明度由1 变为0.3 ,z-index由10 变为20。

    到这里一个较为完善的动画函数就封装完成了。

    如有任何疑问请留言。

    接下将介绍几种通过js动画效果实现各式轮播图的案例......

    相关文章

      网友评论

        本文标题:JavaScript封装简易动画函数

        本文链接:https://www.haomeiwen.com/subject/cueirxtx.html