小白也能看懂的--js的动画效果

1,480 阅读3分钟

一、匀速运动

运动是javascript动画的一个基本操作。通过css3属性的transition和animation可以实现运动。(后面的css3章节会详细讲解)。但是要进行更精细的操作,javascript运动是必不可少的。

1、简单运动

让一个元素在页面中运动起来很简单,设置定时器,改变定位元素的left和top值即可

<!DOCTYPE html>
<html lang="en">
<head>    
<meta charset="UTF-8">   
 <title>01 匀速运动</title>    
<style>       
 *{           
 padding: 0;       
 margin: 0;        
}        
#btn{            
width: 100px;            
height: 30px;        
}        
#box{           
width: 150px;            
height: 150px;            
border: 1px solid pink;            
background-color:pink;            
position: absolute;            
top: 50px;        
}    
</style>
</head>
<body>    
 <button id="btn">运动</button>    
    <div id="box"></div>    
    <script>       
         //1、获取事件源        
        var btn = document.getElementById('btn');        
        var box = document.getElementById('box');        
        var timer = null;        
            //2、给按钮绑定事件        
        btn.onclick = function(){            
          //重复点击按钮的时候要先清除按钮的定时器         
            clearInterval(timer);         
         //3、让盒子运动起来         
         timer = setInterval(function(){              
        //3.1判断临界点              
        if(box.offsetLeft === 500){                  
        clearInterval(timer);              
        }else{                                  
            box.style.left = box.offsetLeft +10 +'px';              
        }                           
    },30)       
 }        
</script>
</body>
</html>

2、实现一个简单的侧边栏分享效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>02 简单的侧边栏分享效果</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #box{
            width: 200px;
            height: 200px;
            background-color:red;
            position: relative;
            left: -200px;
        }
        #box span{
            width: 40px;
            height: 60px;
            color: #ffffff;
            background-color: #000000;
            line-height: 60px;
            text-align: center;
            position: absolute;
            left:200px;
            top: 50%;
            margin-top: -30px;
        }
    </style>
</head>
<body>
    <div id="box">
        <span>拉开</span>
    </div>
    <script>
        window.onload = function(){
            var box = document.getElementById('box');
            box.onmouseover = function(){
                this.style.left = 0 + 'px';
            }
            box.onmouseout = function(){
                this.style.left = -200 + 'px';
            }
        }
    </script>
</body>
</html>

3、实现侧边栏匀速运动的效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>03 侧边栏的匀速运动效果</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #box{
            width: 200px;
            height: 200px;
            background-color:red;
            position: relative;
            left: -200px;
        }
        #box span{
            width: 40px;
            height: 60px;
            color: #ffffff;
            background-color: #000000;
            line-height: 60px;
            text-align: center;
            position: absolute;
            left:200px;
            top: 50%;
            margin-top: -30px;
        }
    </style>
</head>
<body>
    <div id="box">
        <span>拉开</span>
    </div>
    <script>
        window.onload = function(){
            var box = document.getElementById('box');
            var timer = null;
            box.onmouseover = function(){
                clearInterval(timer);
                timer = setInterval(function(){
                    if(box.offsetLeft === 0){
                        clearInterval(timer);
                        return;                                             
                    }
                    box.style.left = box.offsetLeft + 5 +'px';

                },30);

            }
            box.onmouseout = function(){
                clearInterval(timer);
                timer = setInterval(function(){
                    if(box.offsetLeft === -200){
                        clearInterval(timer);
                        return;                                             
                    }
                    box.style.left = box.offsetLeft - 5 +'px';

                },30);

            }

        }
    </script>
</body>
</html>

4、匀速运动函数封装

以上代码我们很容易就发现匀速运动代码的冗余性,要解决这个问题呢,我们就要把匀速运动的函数封装起来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>匀速运动的封装函数</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #box{
            width: 200px;
            height: 200px;
            background-color:red;
            position: relative;
            left: -200px;
        }
        #box span{
            width: 40px;
            height: 60px;
            color: #ffffff;
            background-color: #000000;
            line-height: 60px;
            text-align: center;
            position: absolute;
            left:200px;
            top: 50%;
            margin-top: -30px;
        }
    </style>
</head>
<body>
    <div id="box">
        <span>拉开</span>
    </div>
    <script>
        window.onload = function(){
            var box = document.getElementById('box');
            var timer = null, speed = 0;
            box.onmouseover = function(){
                startAnimation(this,0);
            }
            box.onmouseout = function(){
                startAnimation(this,-200);
            }
            //匀速运动的函数封装
            function startAnimation(obj,end){
                clearInterval(timer);
                speed = end > obj.offsetLeft? 5:-5;
                timer = setInterval(function(){
                    if(box.offsetLeft === end){
                        clearInterval(timer);
                        return;                                             
                    }
                    box.style.left = box.offsetLeft + speed +'px';                   
                },30);
            }

        }
    </script>
</body>
</html>

二、缓动运动

1、缓动的基本动画

缓动运动的动画公式:加速度 = (结束值 - 起始值)/ 缓动系数; 加速度是由快到慢的 起始值就是offsetLet 缓动系数是由自己定义的

加速度的值要取整

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>04 侧边栏的缓动运动效果</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #box{
            width: 200px;
            height: 200px;
            background-color:red;
            position: relative;
            left: -200px;
        }
        #box span{
            width: 40px;
            height: 60px;
            color: #ffffff;
            background-color: #000000;
            line-height: 60px;
            text-align: center;
            position: absolute;
            left:200px;
            top: 50%;
            margin-top: -30px;
        }
    </style>
</head>
<body>
    <div id="box">
        <span>拉开</span>
    </div>
    <script>
        //0~200;
        //缓动运动的动画公式:加速度 = (结束值 - 起始值)/ 缓动系数; 加速度是由快到慢的 起始值就是offsetLet   缓动系数是由自己定义的
        window.onload = function(){
            var box = document.getElementById('box');
            var timer = null,end = 0,end2 = -200;
            box.onmouseover = function(){
                clearInterval(timer);
                timer = setInterval(function(){
                    speed = Math.ceil((end - box.offsetLeft)/20);//给加速度的值取整
                    if(box.offsetLeft === end){
                        clearInterval(timer);
                        return;
                    }
                    box.style.left = box.offsetLeft + speed +'px';
                },30)
            }
            box.onmouseout = function(){
                clearInterval(timer);
                timer = setInterval(function(){
                    speed = Math.floor((end2 - box.offsetLeft)/20);//给加速度的值取整
                    if(box.offsetLeft === end2){
                        clearInterval(timer);
                        return;
                    }
                    box.style.left = box.offsetLeft + speed +'px';
                },30)
            }

        }
    </script>
</body>
</html>

2、缓动运动函数封装

   <script>
            function slowAnimation(obj,end){
                clearInterval(timer);
                timer = setInterval(function(){
                    speed = (end - obj.offsetLeft)/20;
                    speed = speed >0 ? Math.ceil(speed) : Math.floor(speed);
                    if(obj.offsetLeft === end){
                        clearInterval(timer);
                        return;
                    }
                    obj.style.left = obj.offsetLeft + speed +'px';
                },30)
            }

        }
    </script>

三、透明度运动

opacity:1属性是表示透明度的,默认为最大值1,最小值是0(但是这个属性在ie8以下的浏览器不兼容)

filter: alpha(opacity:100) 是ie8以下浏览器的透明度属性,最大值为100;

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>05 透明度动画</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #box {
            width: 200px;
            height: 200px;
            background-color: red;
            opacity: 0.3;
            /* filter: alpha(opacity:30);这是ie8以下的浏览器中的透明度属性 */
        }
    </style>
</head>

<body>
    <div id="box">
    </div>
    <script>
        window.onload = function () {
            var box = document.getElementById('box');
            box.onmouseover = function () {
                alphaAnimation(this, 100);
            }
            box.onmouseout = function () {
                alphaAnimation(this, 30);
            }

            var timer = null,speed = 0, alpha = 30;   //speed是透明度变化的速度  alpha是物体起始的透明度
            function alphaAnimation(obj, endAlpha) { //endAlpha是结束时的透明度值
                clearInterval(timer);
                 timer = setInterval(function () {
                    speed = endAlpha > alpha ? 5 : -5;
                    //处理边界
                    if (alpha === endAlpha) {
                        clearInterval(timer);
                        return;
                    }
                    //改变当前的alpha值
                    alpha += speed;

                    //修改透明度的值
                    obj.style.opacity = alpha / 100;
                    // obj.style.filter = ` alpha(opacity:${alpha})`;

                }, 30)
            }
        }
    </script>
</body>

</html>

四、多物体动画

如果在页面中有多个元素利用运动函数进行运动。由于定时器返回值在不同元素上时不同的返回值。所以要将全局的定时器挂载到当前的元素之上。

针对于多物体运动,定时器的返回值要绑定当前的对象中。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>06 多物体运动</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <script>
        window.onload = function () {
            //1、获取标签
            //缓动运动的动画公式:加速度 = (结束值 - 起始值)/ 缓动系数; 加速度是由快到慢的 起始值就是offsetLet   缓动系数是由自己定义的
            var oDiv = document.getElementsByTagName('div');
            for (var i = 0 ; i < oDiv.length; i++) {
               oDiv[i].onmouseover = function () {
                    startAnimation(this, 600);
                }
               oDiv[i].onmouseout = function () {
                    startAnimation(this, 300);
                }
            }

            var speed = 0;
            function startAnimation(obj, end) {
                //开启定时器之前、清除定时器
                clearInterval(obj.timer);
                obj.timer = setInterval(function () {
                    //speed是变化的速度
                    speed = (end - obj.offsetWidth) / 10;
                    speed = end > obj.offsetWidth ? Math.ceil(speed):Math.floor(speed);
                    //临界处理
                    if(end === obj.offsetWidth){
                        clearInterval(obj.timer);
                        return; 
                    }
                    //宽度变化
                    obj.style.width = obj.offsetWidth + speed + 'px';
                }, 30);
            }
        }
    </script>
</body>

</html>

2、get.ComputedStyle()和obj.currentStyle

针对于上面的代码,会有一个bug,当你给元素对象加一个border属性的时候,上面代码就会出现问题,要解决这个问题就需要用到get.ComputedStyle()obj.currentStyle这个两种方法,前者是用于主流浏览器,后者是用于ie浏览器

多物体运动完整版代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>08 多物体运动的完整版</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
            border: 5px solid #000000;
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <script>
        window.onload = function () {
            //1、获取标签
            //缓动运动的动画公式:加速度 = (结束值 - 起始值)/ 缓动系数; 加速度是由快到慢的 起始值就是offsetLet   缓动系数是由自己定义的
            var oDiv = document.getElementsByTagName('div');
            for (var i = 0 ; i < oDiv.length; i++) {
               oDiv[i].onmouseover = function () {
                    startAnimation(this, 600);
                }
               oDiv[i].onmouseout = function () {
                    startAnimation(this, 300);
                }
            }

            var speed = 0;
            function startAnimation(obj, end) {
                //针对于多物体运动,定时器的返回值要绑定在当前的事件中
                clearInterval(obj.timer);
                obj.timer = setInterval(function () {
                    //0、获取样式属性
                    var cur = parseInt(getStyle(obj,'width'));
                    //1、求速度
                    speed = (end - cur) / 10;
                    speed = end > cur ? Math.ceil(speed):Math.floor(speed);
                    //2、临界处理
                    if(end === cur){
                        clearInterval(obj.timer);
                        return; 
                    }
                    //3、运动起来
                    obj.style.width = cur + speed + 'px';
                }, 30);
            }

             //obj是当前元素对象  attr是当前元素对象的属性
             function getStyle(obj, attr) {
                if (obj.currentStyle) {
                    //obj.currentStyle是兼容ie的方法
                    return obj.currentStyle[attr];
                } else {
                    //主流浏览器的方法
                    return getComputedStyle(obj, null)[attr];
                }
            }
        }

五、多值运动

1、元素属性的多值运动

多物体运动同时改变的是同一个属性,如果我想既可以改变一个元素的宽度,又可以改变一个盒子的高度呢?如何去做?

上述代码中,在最后修改元素的width时,可以有如下书写方式ele.style['width'] = cur + speed + 'px';这样的话,就可以将属性当做形参放到封装的函数中。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>09 多值运动</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
            border: 5px solid #000000;
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <script>
        window.onload = function () {
            //1、获取标签
            //缓动运动的动画公式:加速度 = (结束值 - 起始值)/ 缓动系数; 加速度是由快到慢的 起始值就是offsetLet   缓动系数是由自己定义的
            //外部调用
            var oDiv = document.getElementsByTagName('div');
                oDiv[0].onmouseover = function(){
                    startAnimation(this,'width',600)
                };
                oDiv[0].onmouseout = function(){
                    startAnimation(this,'width',300)
                };
                oDiv[1].onmouseover = function(){
                    startAnimation(this,'height',300)
                };
                oDiv[1].onmouseout = function(){
                    startAnimation(this,'height',100)
                };

            //多值运动函数封装
            var speed = 0;
            function startAnimation(obj,attr,end) {
                //针对于多物体运动,定时器的返回值要绑定在当前的事件中
                clearInterval(obj.timer);
                obj.timer = setInterval(function () {
                    //0、获取样式属性
                    var cur = parseInt(getStyle(obj,attr));
                    //1、求速度
                    speed = (end - cur) / 10;
                    speed = end > cur ? Math.ceil(speed):Math.floor(speed);
                    //2、临界处理
                    if(end === cur){
                        clearInterval(obj.timer);
                        return; 
                    }
                    //3、运动起来
                    obj.style[attr] = cur + speed + 'px';
                }, 30);
            }

             //obj是当前元素对象  attr是当前元素对象的属性
             function getStyle(obj, attr) {
                if (obj.currentStyle) {
                    //obj.currentStyle是兼容ie的方法
                    return obj.currentStyle[attr];
                } else {
                    //主流浏览器的方法
                    return getComputedStyle(obj, null)[attr];
                }
            }
        }
    </script>
</body>

</html>

2、透明度样式处理

在上节课我们留下了一个问题,getStyle函数不可以获取什么属性呢?上节课的代码我们的运动框架逐渐成型,但是这远远还不够,比如看个bug。上节课代码修改

注意:由于透明度涉及小数计算,如0.07*100=> 7.000000000000001,所以需要用Math.round()去掉尾巴
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>10 多值运动透明度的变化</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
            border: 5px solid #000000;
            opacity: 0.3;
            /* filter: alpha(opacity: 30); */
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <script>
        window.onload = function () {
            //1、获取标签
            var oDiv = document.getElementsByTagName('div');
                oDiv[0].onmouseover = function(){
                    startAnimation(this,'opacity',100)
                };
                oDiv[0].onmouseout = function(){
                    startAnimation(this,'opacity',30)
                };
                // oDiv[1].onmouseover = function(){
                //     startAnimation(this,'height',300)
                // };
                // oDiv[1].onmouseout = function(){
                //     startAnimation(this,'height',100)
                // };

            var speed = 0;
            function startAnimation(obj,attr,end) {
                //针对于多物体运动,定时器的返回值要绑定在当前的事件中
                clearInterval(obj.timer);
                obj.timer = setInterval(function () {
                    var cur = 0;
                    //0、获取样式属性

                    if(attr === 'opacity'){
                         //Math.round是对*100之后的数值四舍五入取整
                         cur = Math.round(parseFloat(getStyle(obj,attr))*100);
                    }else{
                        cur = parseFloat(getStyle(obj,attr));
                    }

                    //1、求速度
                    speed = (end - cur) / 10;
                    speed = end > cur ? Math.ceil(speed):Math.floor(speed);

                    //2、临界处理
                    if(end === cur){
                        clearInterval(obj.timer);
                        return; 
                    }
                    //3、运动起来
                    if(attr === 'opacity'){
                        obj.style[attr] = (cur + speed)/100 ;
                        obj.style[attr] =`alpha(opacity: ${cur + speed}) `;
                    }else{
                        obj.style[attr] = cur + speed +'px';
                    }

                }, 30);
            }

             //obj是当前元素对象  attr是当前元素对象的属性
             function getStyle(obj, attr) {
                if (obj.currentStyle) {
                    //obj.currentStyle是兼容ie的方法
                    return obj.currentStyle[attr];
                } else {
                    //主流浏览器的方法
                    return getComputedStyle(obj, null)[attr];
                }
            }
        }
    </script>
</body>

</html>

六、链式动画

物体的多个属性可能是同时运动,也可能是一个属性运动完成之后,另一个属性再运动。如果要完成这种效果,就需要用到回调函数。

如果想实现以上效果。我们现在将封装的运动框架存在一个myAnimation.js文件中。

myAnimation.js

/**
 * 动画的函数
 * obj  当前的对象
 * end 末尾位置
 * attr 当前元素对象的属性
 */
//多值运动-函数封装(控制宽、高、及透明度的变化)
var speed = 0;
function startAnimation(obj, attr, end,fn) {
    //针对于多物体运动,定时器的返回值要绑定在当前的事件中
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        var cur = 0;
        //0、获取样式属性
        //透明度变化处理,兼容ie和其他主流浏览器
        if (attr === 'opacity') {
            //由于透明度涉及小数计算,如0.07\*100=> 7.000000000000001,所以需要用Math.round()去掉尾巴*
            cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
        } else {
            cur = parseFloat(getStyle(obj, attr));
        }

        //1、求速度
        speed = (end - cur) / 10;
        speed = end > cur ? Math.ceil(speed) : Math.floor(speed);

        //2、临界处理
        if (end === cur) {
            clearInterval(obj.timer);
            //临界点判断(如果有第四个参数则执行,如果没有就直接跳出)
            if(fn){
                fn();
            }
            return;
        }
        //3、运动起来
        if (attr === 'opacity') {
            obj.style[attr] = (cur + speed) / 100;
            obj.style[attr] = `alpha(opacity: ${cur + speed}) `;
        } else {
            obj.style[attr] = cur + speed + 'px';
        }

    }, 30);
}
//obj是当前元素对象  attr是当前元素对象的属性
function getStyle(obj, attr) {
    if (obj.currentStyle) {
        //obj.currentStyle是兼容ie的方法
        return obj.currentStyle[attr];
    } else {
        //主流浏览器的方法
        return getComputedStyle(obj, null)[attr];
    }
}

外部引用的时候。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>11 链式运动</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #box{
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
            border: 5px solid #000000;
            /* opacity: 0.3; */
            /* filter: alpha(opacity: 30); */
        }
    </style>
</head>

<body>
    <div id="box"></div>
    <script src="js/myAnimation.js"></script>
    <script>
        var box = document.getElementById('box');
        box.onmouseover = function(){
            startAnimation(box, 'width', 600,function(){
                startAnimation(box, 'height', 300)
            })
        }
    </script>
</body>

</html>

七、同时运动的动画

如果之前的运动框架,我们想让元素既可以改变宽度,又可以改变透明度。这个时候我们用到JSON。关于JSON的详细讲解,在后面课程中我们会详细讲解,现在我们先简单用一下

var json = {    
    "name":"mjj",    
    "age":29
    }

    for(var attr in json){  
    alert(attr); 
    //获取属性名 
    alert(json[attr]);
    //获取属性值
}

有了上面JSON的简单使用之后,我们将myAnimation.js进行修改。

/**
 * 动画的函数(同时运动动画)  利用json实现
 * var json = {    
    "name":"mjj",    
    "age":29
    }

    for(var attr in json){  
    alert(attr); 
    //获取属性名 
    alert(json[attr]);
    //获取属性值
}
 * obj  当前的对象
 */
//多值运动-函数封装(控制宽、高、及透明度的变化)
var speed = 0;
function startAnimation(obj, json, fn) {
    //针对于多物体运动,定时器的返回值要绑定在当前的事件中
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        var cur = 0;
        //标杆 如果flag的值为true,证明说有的属性值都达到终点值
        var flag = true;
        //0、获取样式属性
        for (var attr in json) {
            if (attr === 'opacity') {
                //由于透明度涉及小数计算,如0.07\*100=> 7.000000000000001,所以需要用Math.round()去掉尾巴*
                cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
            } else {
                cur = parseFloat(getStyle(obj, attr));
            }

            //1、求速度
            speed = (json[attr] - cur) / 20;
            speed = json[attr] > cur ? Math.ceil(speed) : Math.floor(speed);

            //2、临界处理
            if (json[attr] !== cur) {
                flag = false;
            }

            //3、运动起来
            if (attr === 'opacity') {
                obj.style[attr] = (cur + speed) / 100;
                obj.style[attr] = `alpha(opacity: ${cur + speed}) `;
            } else {
                obj.style[attr] = cur + speed + 'px';
            }
        }
        if(flag){
            clearInterval(obj.timer);
            //临界点判断(如果有第四个参数则执行,如果没有就直接跳出)
                if (fn) {
                    fn();
                }
            return;
        }

    }, 30);
}
//obj是当前元素对象  attr是当前元素对象的属性
function getStyle(obj, attr) {
    if (obj.currentStyle) {
        //obj.currentStyle是兼容ie的方法
        return obj.currentStyle[attr];
    } else {
        //主流浏览器的方法
        return getComputedStyle(obj, null)[attr];
    }
}

外部引用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>11 链式运动</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #box{
            width: 300px;
            height: 100px;
            margin-top: 50px;
            background-color: green;
            border: 5px solid #000000;
            opacity: 0.3;
            /* filter: alpha(opacity: 30); */
        }
    </style>
</head>

<body>
    <div id="box"></div>
    <script src="js/myAnimation-2.js"></script>
    <script>
        var box = document.getElementById('box');
        box.onmouseover = function(){
            startAnimation(box, {"width":600,"height":300,"opacity":100})
        }
        box.onmouseout = function(){
            startAnimation(box, {"width":300,"height":100,"opacity":30})
        }
    </script>
</body>

</html>