缓动动画封装详细讲解

---来自一个前端妹子
  1 <!DOCTYPE html>
  2 <html>
  3 <head lang="en">
  4     <meta charset="UTF-8">
  5     <title></title>
  6 
  7 
  8     <!--
  9            先快后慢
 10                 一开始的步子要迈的大,后面的步子要小
 11 
 12                 步子要越来越小
 13 
 14           被除数 / 10 ,被除数的值越大,结果就越大,被除数越小。值就越小
 15 
 16           所以我们要找到一个越来越小的数,再去除以10,把结果作为步长,就能起到步子越来越小
 17 
 18           到底什么是越来越小的值?到目标的距离,是越来越小的
 19 
 20 
 21        当前距离    目标     基数(除数)
 22          0        400       10        400 - 0 = 400  / 10   = 40   这步走40
 23         40        400       10        400 - 40 = 360 / 10 = 36   这步走36
 24 
 25         76        400       10        400 - 76 = 324  / 10 = 32.4  向上取整变成了33 这步走33
 26         109       400       10        400 - 109 = 291 / 10 = 29.1 取整变 30
 27        ………………………………………………………………………………
 28         390      400        10        400 - 390 = 10  / 10 = 1 这步走1
 29         391      400        10        400 - 391 = 9 / 10 = 0.9  向上取整走1
 30                                       越到后面越慢,而且到最后那几步就是1个像素1个像素的走,能走到,而且绝对不会超
 31 
 32        399       400      10         400 - 399 = 1    / 10   = 0.1  向上取整变1
 33 
 34     -->
 35 
 36 
 37     <style>
 38 
 39         #box {
 40             width: 100px;
 41             height: 100px;
 42             background-color: red;
 43             position: absolute;
 44         }
 45 
 46     </style>
 47 </head>
 48 <body>
 49 
 50 <input type="button" value="移动到400" id="move400"/>
 51 <input type="button" value="移动到800" id="move800"/>
 52 
 53 <div id="box"></div>
 54 
 55 </body>
 56 </html>
 57 
 58 <script>
 59 
 60     //找到div
 61     var box = document.getElementById("box");
 62 
 63     //移动到400的点击事件
 64     document.getElementById("move400").onclick = function () {
 65 
 66         animateSlow(400);
 67     }
 68 
 69 
 70     document.getElementById("move800").onclick = function () {
 71 
 72         animateSlow(800);
 73     }
 74 
 75 
 76     var timerID;
 77     function animateSlow(target) {
 78 
 79         clearInterval(timerID);
 80 
 81         timerID = setInterval(function () {
 82 
 83                     //获取到当前的位置
 84                     var current = box.offsetLeft;
 85 
 86                     //用目标 - 当前位置 得到距离 ,再用距离除以10,再向上取整得到步长
 87                     var step = Math.ceil((target - current) / 10);
 88 
 89                     //因为绝对不会超,所以算出步长后直接走这一步就行了
 90                     current += step;
 91 
 92                     //设置给要移动的元素
 93                     box.style.left = current + "px";
 94 
 95                     console.log("当前位置:" + current + "------步长:" + step);
 96 
 97                     //如果到了目的地,就停止计时器
 98                     if (current == target) {
 99 
100                         clearInterval(timerID);
101                     }
102 
103                 }, 50
104         )
105     }
106 
107 
108 </script>
01-缓动动画的封装之可以移动到不同的目标距离.html


先快后慢
一开始的步子要迈的大,后面的步子要小

步子要越来越小

被除数 / 10 ,被除数的值越大,结果就越大,被除数越小。值就越小

所以我们要找到一个越来越小的数,再去除以10,把结果作为步长,就能起到步子越来越小

到底什么是越来越小的值?到目标的距离,是越来越小的


当前距离 目标 基数(除数)
0 400 10 400 - 0 = 400 / 10 = 40 这步走40
40 400 10 400 - 40 = 360 / 10 = 36 这步走36

76 400 10 400 - 76 = 324 / 10 = 32.4 向上取整变成了33 这步走33
109 400 10 400 - 109 = 291 / 10 = 29.1 取整变 30
………………………………………………………………………………
390 400 10 400 - 390 = 10 / 10 = 1 这步走1
391 400 10 400 - 391 = 9 / 10 = 0.9 向上取整走1
越到后面越慢,而且到最后那几步就是1个像素1个像素的走,能走到,而且绝对不会超

399 400 10 400 - 399 = 1 / 10 = 0.1 向上取整变1

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title></title>
 6 
 7 
 8     <style>
 9         div {
10             width: 100px;
11             height: 100px;
12             background-color: red;
13             position: absolute;
14         }
15 
16         #blue {
17             background-color: blue;
18             top: 150px;
19         }
20 
21     </style>
22 
23 </head>
24 <body>
25 
26 <input type="button" value="移动到红" id="moveRed"/>
27 <input type="button" value="移动到蓝" id="moveBlue"/>
28 
29 <div id="red"></div>
30 <div id="blue"></div>
31 
32 </body>
33 </html>
34 
35 <script>
36     var red = document.getElementById("red");
37     var blue = document.getElementById("blue");
38 
39     document.getElementById("moveRed").onclick = function () {
40         animateSlow(red, 400);
41     };
42 
43     document.getElementById("moveBlue").onclick = function () {
44 
45         animateSlow(blue, 400);
46     };
47 
48 
49 
50     /*
51 
52      第二重封装:
53         解决了可以移动不同元素的问题
54         以及移动第二个会停止第一个元素的问题(自己的只能自己停)
55 
56      */
57 
58     function animateSlow(obj, target) {
59 
60         clearInterval(obj.timerID);
61 
62         obj.timerID = setInterval(function () {
63 
64                     //获取到当前的位置
65                     var current = obj.offsetLeft;
66 
67                     //用目标 - 当前位置 得到距离 ,再用距离除以10,再向上取整得到步长
68                     var step = Math.ceil((target - current) / 10);
69 
70                     //因为绝对不会超,所以算出步长后直接走这一步就行了
71                     current += step;
72 
73                     //设置给要移动的元素
74                     obj.style.left = current + "px";
75 
76                     console.log("当前位置:" + current + "------步长:" + step);
77 
78                     //如果到了目的地,就停止计时器
79                     if (current == target) {
80 
81                         clearInterval(obj.timerID);
82                     }
83 
84                 }, 50
85         )
86     }
87 </script>
02-缓动动画的封装之各种对象可以动.html

 

第二重封装:
解决了可以移动不同元素的问题
以及移动第二个会停止第一个元素的问题(自己的只能自己停)

  1 <!DOCTYPE html>
  2 <html>
  3 <head lang="en">
  4     <meta charset="UTF-8">
  5     <title></title>
  6 
  7     <!--
  8         当前位置    目标位置   基数
  9           800        400      10    400 -800  = -400 /10 = -40
 10           760        400      10    400 - 760 = -360  / 10 = -36
 11             .....................
 12 
 13           409        400      10     400 - 409 = -9  / 10 = -0.9  向下取整变-1
 14           408        400
 15           407
 16           406
 17           400
 18 
 19           如果是往右走
 20 
 21             791      800      10     800-791 = 9 /10 = 0.9  要向上取整才正确
 22 
 23           我们发现有的时候要向上,有的时候要向下
 24 
 25           如果最终算出的结果为负数,一定要向下,如果算出的结果为正数,要向上
 26     -->
 27 
 28 
 29     <style>
 30         div {
 31             width: 100px;
 32             height: 100px;
 33             background-color: red;
 34             position: absolute;
 35         }
 36 
 37 
 38     </style>
 39 
 40 </head>
 41 <body>
 42 
 43 <input type="button" value="移动到400" id="move400"/>
 44 <input type="button" value="移动到800" id="move800"/>
 45 
 46 <div id="red"></div>
 47 
 48 </body>
 49 </html>
 50 
 51 <script>
 52     var red = document.getElementById("red");
 53     var blue = document.getElementById("blue");
 54 
 55     document.getElementById("move400").onclick = function () {
 56 
 57         animateSlow(red, 400);
 58     };
 59 
 60     document.getElementById("move800").onclick = function () {
 61 
 62         animateSlow(red, 800);
 63     };
 64 
 65 
 66     /*
 67 
 68      第三重封装:
 69      解决了既可以往右走,也可以往走的问题
 70 
 71      发现:只要看算出的结果,如果是正就要向上取整,如果是负就要向下取整
 72 
 73 
 74 
 75      */
 76 
 77     function animateSlow(obj, target) {
 78 
 79         clearInterval(obj.timerID);
 80 
 81         obj.timerID = setInterval(function () {
 82 
 83                     //获取到当前的位置
 84                     var current = obj.offsetLeft;
 85 
 86                     //先用目标-当前位置,再除以基数得到结果
 87                     var result = (target - current) / 10;
 88 
 89                     //如果结果是正就用向上取整,如果是负就用 向下取整
 90                     var step = result > 0 ? Math.ceil(result) : Math.floor(result);
 91 
 92                     //因为绝对不会超,所以算出步长后直接走这一步就行了
 93                     current += step;
 94 
 95                     //设置给要移动的元素
 96                     obj.style.left = current + "px";
 97 
 98                     console.log("当前位置:" + current + "------步长:" + step);
 99 
100                     //如果到了目的地,就停止计时器
101                     if (current == target) {
102 
103                         clearInterval(obj.timerID);
104                     }
105 
106                 }, 50
107         )
108     }
109 </script>
03-缓动动画的封装完成往左往右动.html

 

当前位置    目标位置   基数
800 400 10 400 -800 = -400 /10 = -40
760 400 10 400 - 760 = -360 / 10 = -36
.....................

409 400 10 400 - 409 = -9 / 10 = -0.9 向下取整变-1
408 400
407
406
400

如果是往右走

791 800 10 800-791 = 9 /10 = 0.9 要向上取整才正确

我们发现有的时候要向上,有的时候要向下

如果最终算出的结果为负数,一定要向下,如果算出的结果为正数,要向上

第三重封装:
解决了既可以往右走,也可以往走的问题

发现:只要看算出的结果,如果是正就要向上取整,如果是负就要向下取整



  1 <!DOCTYPE html>
  2 <html>
  3 <head lang="en">
  4     <meta charset="UTF-8">
  5     <title></title>
  6 
  7     <style>
  8         #box {
  9             width: 100px;
 10             height: 100px;
 11             background-color: red;
 12             position: absolute;
 13         }
 14 
 15         /*
 16             第四重封装:
 17                    解决了可以改动上下移动和左右移动以及宽高变化的问题
 18 
 19                    加一个参数:参数传入一个你需要改动的属性
 20 
 21                    然后再通过我们封装的获取样式的方法,获取到当前的值
 22                    算法不变
 23 
 24                    只不过赋值时,就根据你传入的参数来赋值到对应的属性
 25 
 26         */
 27     </style>
 28 </head>
 29 <body>
 30 
 31 <input type="button" value="向下走500" id="down500"/>
 32 <input type="button" value="向右走500" id="right500"/>
 33 <input type="button" value="变宽到400" id="width400"/>
 34 <input type="button" value="变高到400" id="height400"/>
 35 
 36 <div id="box"></div>
 37 
 38 </body>
 39 </html>
 40 
 41 
 42 <script src="common.js"></script>
 43 <script>
 44     //找到div
 45     var box = document.getElementById("box");
 46 
 47     document.getElementById("down500").onclick = function () {
 48 
 49         animateSlow(box, 500, "top");
 50     };
 51 
 52 
 53     document.getElementById("right500").onclick = function () {
 54 
 55         animateSlow(box, 500, "left");
 56     };
 57 
 58     document.getElementById("width400").onclick = function () {
 59 
 60         animateSlow(box, 400, "width");
 61     };
 62 
 63     document.getElementById("height400").onclick = function () {
 64 
 65         animateSlow(box, 400, "height");
 66     };
 67 
 68 
 69     function animateSlow(obj, target, attr) {//可能传left,也可能传top
 70 
 71         clearInterval(obj.timerID);
 72 
 73         obj.timerID = setInterval(function () {
 74 
 75                     //获取到当前的位置
 76                     //传入的是什么属性就获取什么属性的样式,因为得到的是带单位的字符串,所以要用parseInt做一个转换
 77                     var current = parseInt(getStyle(obj, attr));
 78 
 79                     //先用目标-当前位置,再除以基数得到结果
 80                     var result = (target - current) / 10;
 81 
 82                     //如果结果是正就用向上取整,如果是负就用 向下取整
 83                     var step = result > 0 ? Math.ceil(result) : Math.floor(result);
 84 
 85                     //因为绝对不会超,所以算出步长后直接走这一步就行了
 86                     current += step;
 87 
 88                     //设置给要移动的元素
 89                     obj.style[attr] = current + "px";
 90 
 91                     console.log("当前位置:" + current + "------步长:" + step);
 92 
 93                     //如果到了目的地,就停止计时器
 94                     if (current == target) {
 95 
 96                         clearInterval(obj.timerID);
 97                     }
 98 
 99                 }, 50
100         )
101     }
102 </script>
04-缓动动画的封装之实现上下宽高可以用动画变化.html

 

第四重封装:
解决了可以改动上下移动和左右移动以及宽高变化的问题

加一个参数:参数传入一个你需要改动的属性

然后再通过我们封装的获取样式的方法,获取到当前的值
算法不变

只不过赋值时,就根据你传入的参数来赋值到对应的属性

  1 <!DOCTYPE html>
  2 <html>
  3 <head lang="en">
  4     <meta charset="UTF-8">
  5     <title></title>
  6 
  7     <style>
  8         #box {
  9             width: 100px;
 10             height: 100px;
 11             background-color: red;
 12             position: absolute;
 13         }
 14 
 15         /*
 16             第五重封装:
 17                    解决了可以同时改多个属性的问题
 18 
 19                    1.把函数的参数精简为2个,1个为接受要改的元素,第二个就是接收对象
 20                             对象里面放你要改的属性(对象的属性)和它对应的目标(属性的值)
 21 
 22                    2.在计时器里遍历接收对象的那个参数,取到key,key就是我们要取到的样式的属性,以及要改的属性
 23                         算法不变,只是取属性时用key来取了
 24 
 25                    3.不能哪一个到了目标就停止计时器,而应该是全部到了才停止计时器
 26                         用一个标记来标记,只要有一个没到,就改成false,循环外面判断是否还是为true,还是为true就代表都到了,都到了就停止计时器
 27 
 28         */
 29     </style>
 30 </head>
 31 <body>
 32 
 33 <input type="button" value="同时向右走到500向下走到400" id="btn"/>
 34 
 35 
 36 <div id="box"></div>
 37 
 38 </body>
 39 </html>
 40 
 41 
 42 <script src="common.js"></script>
 43 <script>
 44 
 45 
 46     //找到div
 47     var box = document.getElementById("box");
 48 
 49     document.getElementById("btn").onclick = function () {
 50 
 51         /*
 52          我们现在需要同时改多个属性,而且多个属性可能有不同的不表
 53          那么如果用以前的那种加参数方式不行,因为假如说要改3个属性,就需要6个,到时再临时加参数肯定不行
 54          我想用一个参数,既可以让你传入一个属性和一个目标
 55          也可以传入多个属性加多个目标
 56          */
 57 
 58         var att = {
 59 
 60             left: 500,
 61             top: 400,
 62             width: 400,
 63             height: 400,
 64         }
 65 
 66         animateSlow(box, att);
 67     };
 68 
 69 
 70     function animateSlow(obj, attrs) {//可能传left,也可能传top
 71 
 72         clearInterval(obj.timerID);
 73 
 74         obj.timerID = setInterval(function () {
 75 
 76             //我先默认认为所有的都到了目的地(标记为true),在循环里,我判断一下,只要有一个不到,我就把标记改为false
 77 
 78             //默认先认为都到了
 79             var flag = true;
 80 
 81             //把所有的属性名取出来
 82             for (var key in attrs) { //        left: 500  top: 400
 83 
 84                 //获取到当前的位置
 85                 //传入的是什么属性就获取什么属性的样式,因为得到的是带单位的字符串,所以要用parseInt做一个转换
 86                 var current = parseInt(getStyle(obj, key));
 87 
 88                 //先用目标-当前位置,再除以基数得到结果
 89                 //注意:此时目标是它的key所对应的值
 90                 var result = (attrs[key] - current) / 10;
 91 
 92                 //如果结果是正就用向上取整,如果是负就用 向下取整
 93                 var step = result > 0 ? Math.ceil(result) : Math.floor(result);
 94 
 95                 //因为绝对不会超,所以算出步长后直接走这一步就行了
 96                 current += step;
 97 
 98                 //设置给要移动的元素
 99                 obj.style[key] = current + "px";
100 
101                 console.log("当前位置:" + current + "------步长:" + step);
102 
103                 //只要有一个没到,就不要停计时器
104                 if (current != attrs[key]) {
105 
106                     flag = false;
107                 }
108             }
109 
110             //如果这个值还是为true,就代表所有的属性都到了目的地
111             if (flag) {
112 
113                 clearInterval(obj.timerID);
114             }
115 
116         }, 50)
117     }
118 </script>
05-缓动动画的封装之实现斜着走.html
第五重封装:
我们现在需要同时改多个属性,而且多个属性可能有不同的不表
那么如果用以前的那种加参数方式不行,因为假如说要改3个属性,就需要6个,到时再临时加参数肯定不行
我想用一个参数,既可以让你传入一个属性和一个目标
也可以传入多个属性加多个目标
  1 <!DOCTYPE html>
  2 <html>
  3 <head lang="en">
  4     <meta charset="UTF-8">
  5     <title></title>
  6 
  7 
  8     <!--
  9         这一重解决了:
 10             1.透明度不可以改的问题(解决为可以改)
 11                    1.1 因为透明度特殊,所以单独在forin里用一个if判断,如果key等于opacity,那么就走opacity的代码,else就走以前写的代码
 12                    1.2 在opacity里,代码跟以前的代码几乎一样,只改动以下几个部分
 13                         1.2.1 把parseInt改成parseFloat ,再取到值以后给当前位置和目标位置先乘以100
 14                         1.2.2 在赋值到元素的opacity之前,先除以100
 15                         1.2.3 在赋值时去掉px
 16 
 17 
 18             2.增加动画完成后可以执行你想做的事的代码(回调函数)
 19                 2.1 先增加一个参数,用来接收用户传入的函数
 20                 2.2 在清除计时器的代码后面,先判断是不是函数,如果是,就调用
 21 
 22     -->
 23 
 24     <style>
 25         #box {
 26             width: 100px;
 27             height: 100px;
 28             background-color: red;
 29             position: absolute;
 30         }
 31 
 32     </style>
 33 </head>
 34 <body>
 35 
 36 <input type="button" value="同时向右走到500向下走到400" id="btn"/>
 37 
 38 
 39 <div id="box"></div>
 40 
 41 </body>
 42 </html>
 43 
 44 
 45 <script src="common.js"></script>
 46 <script>
 47 
 48 
 49     //找到div
 50     var box = document.getElementById("box");
 51 
 52     document.getElementById("btn").onclick = function () {
 53 
 54         /*
 55          我们现在需要同时改多个属性,而且多个属性可能有不同的不表
 56          那么如果用以前的那种加参数方式不行,因为假如说要改3个属性,就需要6个,到时再临时加参数肯定不行
 57          我想用一个参数,既可以让你传入一个属性和一个目标
 58          也可以传入多个属性加多个目标
 59          */
 60 
 61         var att = {
 62 
 63             opacity: 0,
 64             left: 500,
 65             top: 400,
 66             width: 300,
 67             height: 300
 68         }
 69 
 70         animateSlow(box, att, function () {
 71 
 72             var attrs = {
 73 
 74                 top: 31,
 75                 left: 1000,
 76                 width: 100,
 77                 height: 100,
 78                 opacity: 1
 79             }
 80             animateSlow(box, attrs);
 81         });
 82     };
 83 
 84 
 85     function animateSlow(obj, attrs, fn) {//可能传left,也可能传top
 86 
 87         clearInterval(obj.timerID);
 88 
 89         obj.timerID = setInterval(function () {
 90 
 91             //我先默认认为所有的都到了目的地(标记为true),在循环里,我判断一下,只要有一个不到,我就把标记改为false
 92 
 93             //默认先认为都到了
 94             var flag = true;
 95 
 96             //把所有的属性名取出来
 97             for (var key in attrs) { //        left: 500  top: 400
 98 
 99                 if (key == "opacity") {
100 
101                     //获取到当前的位置
102                     //传入的是什么属性就获取什么属性的样式,因为得到的是带单位的字符串,所以要用parseInt做一个转换
103                     var current = parseFloat(getStyle(obj, key)) * 100;
104 
105                     //它的值太小了
106 //先用目标-当前位置,再除以基数得到结果
107                     //注意:此时目标是它的key所对应的值
108                     var result = (attrs[key] * 100 - current) / 10;
109 
110                     //如果结果是正就用向上取整,如果是负就用 向下取整
111                     var step = result > 0 ? Math.ceil(result) : Math.floor(result);
112 
113                     //因为绝对不会超,所以算出步长后直接走这一步就行了
114                     current += step;
115 
116                     //之前你是先乘以100做的运算,所以后面应该把再除以100
117                     current /= 100;
118                     //设置给要移动的元素
119                     obj.style[key] = current;
120 
121                     console.log("当前位置:" + current + "------步长:" + step);
122 
123                     //只要有一个没到,就不要停计时器
124                     if (current != attrs[key]) {
125 
126                         flag = false;
127                     }
128 
129                 } else {
130 
131                     //获取到当前的位置
132                     //传入的是什么属性就获取什么属性的样式,因为得到的是带单位的字符串,所以要用parseInt做一个转换
133                     var current = parseInt(getStyle(obj, key));
134 
135                     //先用目标-当前位置,再除以基数得到结果
136                     //注意:此时目标是它的key所对应的值
137                     var result = (attrs[key] - current) / 10;
138 
139                     //如果结果是正就用向上取整,如果是负就用 向下取整
140                     var step = result > 0 ? Math.ceil(result) : Math.floor(result);
141 
142                     //因为绝对不会超,所以算出步长后直接走这一步就行了
143                     current += step;
144 
145                     //设置给要移动的元素
146                     obj.style[key] = current + "px";
147 
148                     console.log("当前位置:" + current + "------步长:" + step);
149 
150                     //只要有一个没到,就不要停计时器
151                     if (current != attrs[key]) {
152 
153                         flag = false;
154                     }
155                 }
156             }
157 
158             //如果这个值还是为true,就代表所有的属性都到了目的地
159             if (flag) {
160 
161                 clearInterval(obj.timerID);
162 
163                 //这里的代码不要写死,就让别人把要结束时执行的代码传进来
164                 //你传的是什么代码,我执行的就是什么代码
165                 if (fn instanceof  Function) {
166                     fn();
167                 }
168 
169             }
170 
171         }, 50)
172     }
173 </script>
06-缓动动画的封装终极封装.html

 

这一重解决了:
1.透明度不可以改的问题(解决为可以改)
1.1 因为透明度特殊,所以单独在forin里用一个if判断,如果key等于opacity,那么就走opacity的代码,else就走以前写的代码
1.2 在opacity里,代码跟以前的代码几乎一样,只改动以下几个部分
1.2.1 把parseInt改成parseFloat ,再取到值以后给当前位置和目标位置先乘以100
1.2.2 在赋值到元素的opacity之前,先除以100
1.2.3 在赋值时去掉px


2.增加动画完成后可以执行你想做的事的代码(回调函数)
2.1 先增加一个参数,用来接收用户传入的函数
2.2 在清除计时器的代码后面,先判断是不是函数,如果是,就调用
posted @ 2018-01-01 14:26  Fanyee  阅读(418)  评论(0编辑  收藏  举报