码迷,mamicode.com
首页 > 编程语言 > 详细

JavaScript学习总结【11】、JS运动

时间:2016-01-19 21:00:29      阅读:310      评论:0      收藏:0      [点我收藏+]

标签:

  动画效果在网站中是一种非常常见的交互式体验效果,比如侧边栏分享、图片淡入淡出,我们把这种动画效果就叫做运动,也就是让物体动起来。如果想让一个物体动起来,无非就是改变它的速度,也就是改变属性值,比如 left、right、width、height、opacity ,那么既然是运动,就可以分为很多种,如匀速运动、缓冲运动、多物体运动、任意值运动、链式运动和同时运动。我们从最简单的动画开始,如何让单个物体运动,逐步深入多物体运动、多动画同时运动到实现完美运动框架的封装,在这个过程中,每一个运动都封装为一个函数,可以更好的培养和锻炼我们的编程思想,增强逻辑思维。

  1、简单运动

  简单运动的实现都是匀速运动,顾名思义就是运动速度不变,通过宽、高、透明度等的变化,实现简单的动画效果,下面我们就让一个 div 运动起来。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>简单运动</title>
 6 <style>
 7 *{margin:0;padding:0;}
 8 #div1{
 9     width:200px;
10     height:200px;
11     background:green;
12     position:absolute;
13 }
14 </style>
15 <script>
16 function startMove(){
17     var oDiv = document.getElementById(div1);
18     setInterval(function (){
19         oDiv.style.left = oDiv.offsetLeft + 10 + px;
20     },30);
21 }
22 </script>
23 </head>
24 <body>
25 <input type="button" value="动起来" onclick="startMove()">
26 <div id="div1"></div>
27 </body>
28 </html>

  让一个 div 动起来,只需要开一个定时器,用于定义速度,告诉物体运动的快慢,上面的代码,当点击按钮后,div 每隔30毫秒从左向右运动10像素。

  这里需要注意,让 div 向右运动,在定义样式时,一定要给运动的物体加绝对定位,也就是相对于哪个位置进行运动,offsetLeft 代表物体的当前位置,所以每次运动,都是给当前的 offsetLeft 加10像素。

  虽然是让 div 动起来了,但是问题多多,动起来后根本停不下来,这样就太任性了,而且当重复点击的话,运动速度还会加快,这都不是我们想要的,下面就我们就让他停止在指定位置处。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>简单运动</title>
 6 <style>
 7 *{margin:0;padding:0;}
 8 #div1{
 9     width:200px;
10     height:200px;
11     background:green;
12     position:absolute;
13 }
14 </style>
15 <script>
16 var timer = null;
17 function startMove(){
18     var oDiv = document.getElementById(div1);
19     clearInterval(timer);
20     timer = setInterval(function (){
21         if(oDiv.offsetLeft == 300){
22             clearInterval(timer);
23         }
24         else{
25             oDiv.style.left = oDiv.offsetLeft + 10 + px;
26         }
27     },30);
28 }
29 </script>
30 </head>
31 <body>
32 <input type="button" value="动起来" onclick="startMove()">
33 <div id="div1"></div>
34 </body>
35 </html>

  上面的代码,点击按钮后,div 从左向右运动到300像素时停止运动。

  若要停止一个物体的运动,只要关闭定时器即可,也就是判断 div 的 offsetLeft 值是否等于300,若等于300则清空定时器。这里需要注意,在做判断时,一定要给运动位置加 else,判断语句为二选一,成立或者不成立时执行,这样再点击按钮就不会运动了,否则当 div 运动到300像素时,再点击按钮,div 还会向右移动10像素,虽然在到达300像素时已经关闭了定时器,但是按钮的点击事件,还会执行一次函数,所以加了 else 之后,当再点击按钮时,这时候条件已经成立了,也就不会再执行 else 中的语句了。

  重复点击按钮,运动速度会不断加快,是因为每点击一次按钮,startMove 函数被执行一次,多次点击,也就相当于开了多个定时器,所以需要在执行 startMove 函数时,首先清空定时器,这样重复点击按钮时,会先把之前运行的定时器关闭,再开一个新的定时器运行,这就保证了始终是一个定时器在工作。

  下面我们看两个简单动画的实例:

  实例:侧边栏分享

  实现思路:网站侧边栏菜单是最常见的动画效果,该效果在初始时,只显示一个按钮或者菜单项,当鼠标移上去时,滑出隐藏部分,展示内容。该效果在做布局时,主要用绝对定位实现隐藏,left 的值为内容容器 width 的值,该值为负值,动画效果实现就是改变它的 offsetLeft 的值,当鼠标移入时,增加 offsetLeft 的值,值为0时停止运动,当鼠标移开时,offsetLeft 的值从0减小到 left 的值。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>侧边栏分享</title>
 6 <style>
 7 *{margin:0;padding:0;}
 8 #div1{
 9     width:150px;
10     height:210px;
11     background:lightgreen;
12     position:absolute;
13     left:-150px;
14 }
15 #div1 span{
16     width:20px;
17     height:60px;
18     line-height:20px;
19     color:white;
20     background:green;
21     position:absolute;
22     right:-20px;
23     top:70px;
24 }
25 </style>
26 <script>
27 window.onload = function (){
28     var oDiv = document.getElementById(div1);
29     oDiv.onmouseover = function (){
30         startMove();
31     };
32     oDiv.onmouseout = function (){
33         stopMove();
34     };
35 };
36 var timer = null;
37 function startMove(){
38     var oDiv = document.getElementById(div1);
39     clearInterval(timer);
40     timer = setInterval(function (){
41         if(oDiv.offsetLeft == 0){
42             clearInterval(timer);
43         }
44         else{
45             oDiv.style.left = oDiv.offsetLeft + 10 + px;
46         }
47     },30);
48 }
49 function stopMove(){
50     var oDiv = document.getElementById(div1);
51     clearInterval(timer);
52     timer = setInterval(function (){
53         if(oDiv.offsetLeft == -150){
54             clearInterval(timer);
55         }
56         else{
57             oDiv.style.left = oDiv.offsetLeft - 10+ px;
58         }
59     },30);
60 }
61 </script>
62 </head>
63 <body>
64 <div id="div1">
65     <span>分享到</span>
66 </div>
67 </body>
68 </html>

  上面的代码,当鼠标移入"分享到",隐藏的 div 即内容容器每隔30毫秒从左向右运动10像素,offsetLeft 值为0时停止运动,当鼠标移开时,显示的 div 每隔30毫秒从右向左移动5像素,offsetLeft 值为-150时停止运动。这里要注意的是,offsetLeft 值的变化,从左向右运动时,值为正值,也就是+10,从右向左运动时,值为负值,也就是-10。

  我们可以看到,startMove 和 stopMove 都有相同的代码结构,如果代码中存在大致相同的代码,就可以对代码进行优化,上面的代码,就只有 offsetLeft 的移动位置 和 停止位置不同,因此可以用函数传参的方式将上面的代码简化为:

 1 <script>
 2 window.onload = function (){
 3     var oDiv = document.getElementById(div1);
 4     oDiv.onmouseover = function (){
 5         startMove(10, 0);
 6     };
 7     oDiv.onmouseout = function (){
 8         startMove(-10, -150);
 9     };
10 };
11 var timer = null;
12 function startMove(speed, iTarget){
13     var oDiv = document.getElementById(div1);
14     clearInterval(timer);
15     timer = setInterval(function (){
16         if(oDiv.offsetLeft === iTarget){
17             clearInterval(timer);
18         }
19         else{
20             oDiv.style.left = oDiv.offsetLeft + speed + px;
21         }
22     },30);
23 }
24 </script>

  移动位置也就是速度,用 speed 参数传入,停止位置也就是目标位置,用 iTarget 参数传入。在功能相同的情况下,一个函数传入的参数越少越好,那么还可以简化为:

 1 <script>
 2 window.onload = function (){
 3     var oDiv = document.getElementById(div1);
 4     oDiv.onmouseover = function (){
 5         startMove(0);
 6     };
 7     oDiv.onmouseout = function (){
 8         startMove(-150);
 9     };
10 };
11 var timer = null;
12 function startMove(iTarget){
13     var oDiv = document.getElementById(div1);
14     clearInterval(timer);
15     timer = setInterval(function (){
16         var speed = 0;
17         if(oDiv.offsetLeft > iTarget){
18             speed = -10;
19         }
20         else{
21             speed = 10;
22         }
23         if(oDiv.offsetLeft == iTarget){
24             clearInterval(timer);
25         }
26         else{
27             oDiv.style.left = oDiv.offsetLeft + speed + px;
28         }
29     },30);
30 }
31 </script>

  速度值和目标值,目标值肯定是不能省略的,就好比坐火车,买票肯定得有一个终点,所以速度值可以被省略掉,不管是动车还是普通车,都会到达终点,不同的就是速度的快慢。首先让速度值等于0,再做一个判断,判断目标值与物体当前位置的关系,如果 offsetLeft 值大于目标值,那么就要从右向左运动,所以为负值,否则,也就是目标值大于 offsetLeft 值,这说明此时物体是隐藏的,那么就从左向右运动,速度值为正值。

  该效果如果用缓冲运动做的话,效果更好。

  实例:淡入淡出

  淡入淡出效果是鼠标移入移出改变透明度,透明度动画也属于运动效果,可以使用上例中 startMove 框架完成这种效果。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>淡入淡出</title>
 6 <style>
 7 #div1{
 8     width:200px;
 9     height:200px;
10     background:red;
11     filter:alpha(opacity:30);
12     opacity:0.3;
13 }
14 </style>
15 <script>
16 window.onload = function (){
17     var oDiv = document.getElementById(div1);
18     oDiv.onmouseover = function (){
19         startMove(100);
20     };
21     oDiv.onmouseout = function (){
22         startMove(30);
23     };
24 };
25 var alpha = 30;    //将透明度值存储在变量中
26 var timer = null;
27 function startMove(iTarget){
28     var oDiv = document.getElementById(div1);
29     clearInterval(timer);
30     timer = setInterval(function (){
31         var speed = 0;
32         if(alpha > iTarget){
33             speed = -10;
34         }
35         else{
36             speed = 10;
37         }
38         if(alpha == iTarget){
39             clearInterval(timer);
40         }
41         else{
42             alpha += speed;    //透明度值增加效果
43             oDiv.style.opacity = alpha/100;    //高版本滤镜为:0.1-1,所以除以100
44             oDiv.style.filter = alpha(opacity:+ alpha +);    //IE低版本浏览器使用
45         }
46     },30);
47 }
48 </script>
49 </head>
50 <body>
51 <div id="div1"></div>
52 </body>
53 </html>

   改变透明度的值,可没有 offsetalpha 这样的写法,透明度值没有直接的属性可以改变,但是可以将透明度值存储在变量中,通过判断变量的值和目标值之间的关系,就可以确定速度值,有了速度值之后,再将这个速度值通过变量赋值给他,就可以达到改变透明度值的效果。

  

  2、缓冲运动

  匀速运动的速度始终都是不变的,而缓冲运动的速度则是逐渐变慢,最后停止的,就像火车一样,出站后速度都是很快的,距离目标位置越近,速度会逐渐变慢,到站后停止,也就是缓冲运动的速度是由距离决定的,速度和距离成正比的,距离越远,速度越大。

  那么,缓冲运动的速度就可以用这个公式表示:速度 = (目标值 - 当前值)/缩放系数

  何为缩放系数,为什么要除以缩放系数,我们看下面的实例。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>缓冲运动</title>
 6 <style>
 7 #div1{
 8     width:100px;
 9     height:100px;
10     background:green;
11     position:absolute;
12     left:0;
13     top:50px;
14 }
15 #div2{
16     width:1px;
17     height:300px;
18     background:black;
19     position:absolute;
20     left:300px;
21     top:0;
22 }
23 </style>
24 <script>
25 function startMove(){
26     var oDiv = document.getElementById(div1);
27     setInterval(function (){
28         var speed = 300 - oDiv.offsetLeft;
29         oDiv.style.left = oDiv.offsetLeft + speed + "px";
30     },30);
31 }
32 </script>
33 </head>
34 <body>
35 <input type="button" value="动起来" onclick="startMove()">
36 <div id="div1"></div>
37 <div id="div2">300px处</div>
38 </body>
39 </html>

   上面的代码,在点击按钮后,让 div 运动到300像素处停止,可以看到 div 直接就跳到300像素处了,他的运动速度等于目标位置减去当前位置,初始位置为0,所以点击按钮后,一瞬间就跳到300像素处了。

  速度太大了,就没有缓冲的效果了,所以要除以缩放系数,就是让他有一个缓冲的过程,我们给他除以10,初始位置0,点击按钮后,速度为30,当在最后靠近300像素处,速度就为0,距离越小,则速度就越小。

var speed = (300 - oDiv.offsetLeft)/10;

 

  这样虽然是达到了缓冲的效果,但是可以很明显的看到,div 并不是停在300像素处,还差那么一点,打开调试工具可以看到,此时的 left 值为296.4px,像素的最小单位是 px,我们平时写代码时,不可能这么写100.5px 或者 200.9px,出现这样的情况就是因为这句代码:oDiv.style.left = oDiv.offsetLeft + speed + "px",也就是 div 的 left 值是当前值加速度值,所以296就是当前的 left 值,296到300差4,再除以缩放系数10,就是0.4,因此 div 的 left 值就为296.4px,这并不是我们想要的,速度不能为小数,解决方法也很简单,那就是向上取整。

speed = Math.seil(speed);

 

  那么,问题又来了,如果 div 的初始值是在600像素处,这时候就是从左向右运动,速度小于0,也就是速度为负值,向上取整之后,离要运动到的300px处还差一点,这时候需要向下取整,但是向下取整之后,初始值为为0时,运动到300px处还是差一点,这时候就需要做判断,当速度大于0时,速度为正,向上取整,如果小于0时,速度为负,则向下取整。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>缓冲运动</title>
 6 <style>
 7 #div1{
 8     width:100px;
 9     height:100px;
10     background:green;
11     position:absolute;
12     left:0;
13     top:50px;
14 }
15 #div2{
16     width:1px;
17     height:300px;
18     background:black;
19     position:absolute;
20     left:300px;
21     top:0;
22 }
23 </style>
24 <script>
25 function startMove(){
26     var oDiv = document.getElementById(div1);
27     setInterval(function (){
28         var speed = (300 - oDiv.offsetLeft)/10;
29         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
30         oDiv.style.left = oDiv.offsetLeft + speed + "px";
31     },30);
32 }
33 </script>
34 </head>
35 <body>
36 <input type="button" value="动起来" onclick="startMove()">
37 <div id="div1"></div>
38 <div id="div2">300px处</div>
39 </body>
40 </html>

  

  这里一定要注意,缓冲运动一定要取整,否则就到不了目标位置。下面是一个用缓冲运动做的侧边栏分享效果。

 

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>缓冲分享</title>
 6 <style>
 7 *{margin:0;padding:0;}
 8 #div1{
 9     width:150px;
10     height:210px;
11     background:lightgreen;
12     position:absolute;
13     left:-150px;
14 }
15 #div1 span{
16     width:20px;
17     height:60px;
18     line-height:20px;
19     color:white;
20     background:green;
21     position:absolute;
22     right:-20px;
23     top:70px;
24 }
25 </style>
26 <script>
27 window.onload = function (){
28     var oDiv = document.getElementById(div1);
29     oDiv.onmouseover = function (){
30         startMove(0);
31     };
32     oDiv.onmouseout = function (){
33         startMove(-150);
34     };
35 };
36 var timer = null;
37 function startMove(iTarget){
38     var oDiv = document.getElementById(div1);
39     clearInterval(timer);
40     timer = setInterval(function (){
41         var speed = (iTarget - oDiv.offsetLeft)/10;
42         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
43         if(oDiv.offsetLeft == iTarget){
44             clearInterval(timer);
45         }
46         else{
47             oDiv.style.left = oDiv.offsetLeft + speed + px;
48         }
49     },30);
50 }
51 </script>
52 </head>
53 <body>
54 <div id="div1">
55     <span>分享到</span>
56 </div>
57 </body>
58 </html>

 

  缓冲运动的停止条件是当两点重合时,也就是物体当前的 left 值等于目标值,而匀速运动的停止条件是距离足够近时,也就是物体当前的 left 值大于或小于目标值,再强行让他的 left 值等于目标值,下面我们通过一个实例来更好的理解。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>运动的停止</title>
 6 <style>
 7 #div1{
 8     width:100px;
 9     height:100px;
10     background:green;
11     position:absolute;
12     left:600px;
13     top:50px;
14 }
15 #div2{
16     width:1px;
17     height:300px;
18     background:black;
19     position:absolute;
20     left:200px;
21     top:0;
22 }
23 #div3{
24     width:1px;
25     height:300px;
26     background:black;
27     position:absolute;
28     left:400px;
29     top:0;
30 }
31 </style>
32 <script>
33 var timer = null;
34 function startMove(iTarget){
35     var oDiv = document.getElementById(div1);
36     clearInterval(timer);
37     timer = setInterval(function (){
38         var speed = 0;
39         if(oDiv.offsetLeft > iTarget){
40             speed = -7;
41         }
42         else{
43             speed = 7;
44         }
45         if(Math.abs(iTarget - oDiv.offsetLeft) <= 7){
46             clearInterval(timer);
47             oDiv.style.left = iTarget + px;
48         }
49         else{
50             oDiv.style.left = oDiv.offsetLeft + speed + "px";
51         }
52     },30);
53 }
54 </script>
55 </head>
56 <body>
57 <input type="button" value="到200" onclick="startMove(200)">
58 <input type="button" value="到400" onclick="startMove(400)">
59 <div id="div1"></div>
60 <div id="div2">200px处</div>
61 <div id="div3">400px处</div>
62 </body>
63 </html>

  上面的代码,点击 到200 按钮,div 从右向左每隔30毫秒运动7像素,速度为负,运动到200像素处停止,再点击 到400 按钮,div 从左向右每隔30毫秒运动7像素,速度为正,运动到400像素处停止。匀速运动的速度始终是保持不变的,这里的运动速度为7,初始位置运动到目标位置的移动速度除不尽,这样就会出现左右徘徊的情况,也就是 div 在目标点左右闪动,原因是前进一个7,比目标位置大了,后退一个7,比目标位置小了,所有会出现这种现象。解决办法就是使用绝对值进行判断,因为他的距离可能是正7,也可能是负7。那么使用绝对值做判断,目标位置减去物体当前位置若小于等于7,就算到了,这时候问题就来了,虽然到了不闪动了,但是可以很明显看到还有一点距离,所以最关键的一步,就是在清空定时器后,直接让 div 的 left 值等于目标点的值,这样就完成了匀速运动的停止。

  

  3、多物体运动和任意值运动

  (1)、多物体运动

  我们之前做的都是单个物体的运动,而在网站中,并不是单个物体在运动,而是多个物体的运动,下面我们就套用之前的 startMove 框架让多个 div 运动起来。

 

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>多个div变宽</title>
 6 <style>
 7     div{
 8         width:100px;
 9         height:50px;
10         background:green;
11         margin:10px;
12     }
13 </style>
14 <script>
15 window.onload = function (){
16     var aDiv = document.getElementsByTagName(div);
17     for(var i=0; i<aDiv.length; i++){
18         aDiv[i].onmouseover = function (){
19             startMove(this,400);
20         };
21         aDiv[i].onmouseout = function (){
22             startMove(this,100);
23         };
24     }
25 };
26 var timer = null;
27 function startMove(obj,iTarget){
28     clearInterval(timer);
29     timer = setInterval(function (){
30         var speed=(iTarget - obj.offsetWidth)/6;
31         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
32 
33         if(obj.offsetWidth == iTarget){
34             clearInterval(timer);
35         }
36         else{
37             obj.style.width = obj.offsetWidth + speed + px;
38         }
39     },30);
40 }
41 </script>
42 </head>
43 <body>
44 <div></div>
45 <div></div>
46 <div></div>
47 </body>
48 </html>

 

  上面的代码,有3个 div,在鼠标移入时宽从100像素运动到400像素,而当鼠标移出后从400像素运动回100像素,因为是多个物体的运动,因此要使用 for 循环让每个物体都获得事件,那么要确定当前获得事件的物体,就需要再传入一个参数 this,我们不能确定当前是哪个物体获得事件,所以使用 this 指向,但是我们之前写的 startMove 框架只有一个参数,而多物体的运动传入了2个参数,因为要确定当前运动的物体,所以只要再给 startMove 传入一个参数 obj 就好了。那么多物体的运动框架,就是再传入一个参数,这样就能获取当前运动的物体了。

  这样就算做完了吗?肯定没有,当你鼠标同时滑过3个 div 时,就出事了,会发现变宽之后变不回来了,这是因为定时器,3个 div 只用一个定时器,虽然在刚开始运行时,我们就清空了定时器,但只是清空了整个定时器,不知道清空的是谁的,这样就造成了混乱,肯定就出错了,所以要给每个 div 都设置一个定时器,我们知道可以给对象添加一个自定义属性设置索引号 aDiv[i].index = 0,那么就可以把定时器也作为物体的属性 aDiv[i].timer=null 。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>多个div变宽</title>
 6 <style>
 7     div{
 8         width:100px;
 9         height:50px;
10         background:green;
11         margin:10px;
12     }
13 </style>
14 <script>
15 window.onload = function (){
16     var aDiv = document.getElementsByTagName(div);
17     for(var i=0; i<aDiv.length; i++){
18         aDiv[i].timer = null;
19         aDiv[i].onmouseover = function (){
20             startMove(this,400);
21         };
22         aDiv[i].onmouseout = function (){
23             startMove(this,100);
24         };
25     }
26 };
27 function startMove(obj,iTarget){
28     clearInterval(obj.timer);
29     obj.timer = setInterval(function (){
30         var speed=(iTarget - obj.offsetWidth)/6;
31         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
32 
33         if(obj.offsetWidth == iTarget){
34             clearInterval(obj.timer);
35         }
36         else{
37             obj.style.width = obj.offsetWidth + speed + px;
38         }
39     },30);
40 }
41 </script>
42 </head>
43 <body>
44 <div></div>
45 <div></div>
46 <div></div>
47 </body>
48 </html>

 

  下面再看一个多个 div 淡入淡出的实例。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>多个div淡入淡出</title>
 6 <style>
 7 div{
 8     width:200px;
 9     height:200px;
10     background:red;
11     float:left;
12     margin:20px;
13     filter:alpha(opacity:30);
14     opacity:0.3;
15 }
16 </style>
17 <script>
18 window.onload = function (){
19     var aDiv = document.getElementsByTagName(div);
20     for(var i=0; i<aDiv.length; i++){
21         aDiv[i].onmouseover = function (){
22             startMove(this,100);
23         };
24         aDiv[i].onmouseout = function (){
25             startMove(this,30);
26         };
27     }
28 };
29 var alpha = 30;
30 function startMove(obj,iTarget){
31     clearInterval(obj.timer);
32     obj.timer = setInterval(function (){
33         var speed=(iTarget - alpha)/6;
34         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
35 
36         if(alpha == iTarget){
37             clearInterval(obj.timer);
38         }
39         else{
40             alpha += speed;
41             obj.style.opacity = alpha/100;
42             obj.style.filter = alpha(opacity: + alpha +);
43         }
44     },30);
45 }
46 </script>
47 </head>
48 <body>
49 <div></div>
50 <div></div>
51 <div></div>
52 <div></div>
53 </body>
54 </html>

  上面的代码,4个 div 初始的透明度都为0.3,在鼠标移入后透明度变为1,当鼠标移出后变回0.3,单个移入移出都没有问题,但是当快速移动鼠标时,又出事了,并没有达到我们预期的效果,我们给当前的物体都设置了定时器,但为什么还会出现这种情况呢?其实这并不是定时器的问题,而是用于存储透明度的变量 alpha 导致的,4个 div 公用了一个滤镜,就导致了混乱,从这我们可以看出,凡是多物体运动,所有东西都不能公用,也就是要改变的属性,必须给每个物体设置,要把属性和运动东西绑定。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>多个div淡入淡出</title>
 6 <style>
 7 div{
 8     width:200px;
 9     height:200px;
10     background:red;
11     float:left;
12     margin:20px;
13     filter:alpha(opacity:30);
14     opacity:0.3;
15 }
16 </style>
17 <script>
18 window.onload = function (){
19     var aDiv = document.getElementsByTagName(div);
20     for(var i=0; i<aDiv.length; i++){
21         aDiv[i].alpha = 30;
22         aDiv[i].onmouseover = function (){
23             startMove(this,100);
24         };
25         aDiv[i].onmouseout = function (){
26             startMove(this,30);
27         };
28     }
29 };
30 function startMove(obj,iTarget){
31     clearInterval(obj.timer);
32     obj.timer = setInterval(function (){
33         var speed=(iTarget - obj.alpha)/6;
34         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
35 
36         if(obj.alpha == iTarget){
37             clearInterval(obj.timer);
38         }
39         else{
40             obj.alpha += speed;
41             obj.style.opacity = obj.alpha/100;
42             obj.style.filter = alpha(opacity: + obj.alpha +);
43         }
44     },30);
45 }
46 </script>
47 </head>
48 <body>
49 <div></div>
50 <div></div>
51 <div></div>
52 <div></div>
53 </body>
54 </html>

 

  (2)、任意值运动

  之前我们都做的是物体的单一运动,都是改变宽度或者改变透明度,在网站中,也肯定不只是单一的运动,有可能是变宽,也有可能是变高,下面我们就看一下 div 的变宽和变高。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>div变宽和变高</title>
 6 <style>
 7 div{
 8     width:100px;
 9     height:50px;
10     background:green;
11     margin-bottom:10px;
12 }
13 </style>
14 <script>
15 window.onload = function (){
16     var oDiv1 = document.getElementById(div1);
17     var oDiv2 = document.getElementById(div2);
18 
19     oDiv1.onmouseover = function (){
20         startMove(this,400);
21     };
22     oDiv1.onmouseout = function (){
23         startMove(this,100);
24     };
25 
26     oDiv2.onmouseover = function (){
27         startMove2(this,400);
28     };
29     oDiv2.onmouseout = function (){
30         startMove2(this,50);
31     };
32 };
33 function startMove(obj,iTarget){
34     clearInterval(obj.timer);
35     obj.timer = setInterval(function (){
36         var speed = (iTarget - obj.offsetWidth)/6;
37         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
38         if(obj.offsetWidth == iTarget){
39             clearInterval(obj.timer);
40         }
41         else{
42             obj.style.width = obj.offsetWidth + speed + px;
43         }
44     },30);
45 }
46 function startMove2(obj,iTarget){
47     clearInterval(obj.timer);
48     obj.timer = setInterval(function (){
49         var speed = (iTarget - obj.offsetHeight)/6;
50         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
51         if(obj.offsetHeight == iTarget){
52             clearInterval(obj.timer);
53         }
54         else{
55             obj.style.height = obj.offsetHeight + speed + px;
56         }
57     },30);
58 }
59 </script>
60 </head>
61 <body>
62 <div id="div1">变宽</div>
63 <div id="div2">变高</div>
64 </body>
65 </html>

   上面的代码,div1 在鼠标移入时宽度从100像素运动到400像素,移出后运动回100像素,div2 在鼠标移入时高度从50像素运动到400像素,移出后运动回50像素,我们只要调用2个 startMove 框架就可以很轻松的完成这样的效果,但是代码显得十分繁琐,而且这2个框架的功能是完全相同的,唯一不同的就是一个改变宽度,一个改变高度,之前我们说过,对于功能完全相同的代码,可以对代码进行简化,把不同的东西作为参数传入,这样就可以得到一个任意值的运动框架。

  在这之前,我们先来看一下 offset 这个属性,前边我们在做运动时,只给物体进行了简单的样式定义,宽、高和背景,但在实际的开发中,或许还有其他的样式属性,比如内外边距或者边框等,那如果在定义一个边框属性,会如何呢?看下面的实例:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>offset属性</title>
 6 <style>
 7 #div1{
 8     width:200px;
 9     height:200px;
10     background:red;
11     border:1px solid black;
12 }
13 </style>
14 <script>
15 setInterval(function (){
16     var oDiv = document.getElementById(div1);
17     oDiv.style.width = oDiv.offsetWidth - 1 + px;
18 },30);
19 </script>
20 </head>
21 <body>
22 <div id="div1">变窄</div>
23 </body>
24 </html>

  上面的代码,我们给 div 加了1像素的边框,并且让他每隔30毫秒从右向左运动1像素,速度为负,也就是 offsetWidth - 1,本应该打开 demo 之后,会看到 div 会逐渐变窄,但是事与愿违,div 并没有逐渐变窄,反而逐渐变宽了,如果我们不加边框的话,div 肯定会逐渐变窄,这就是 offset 属性的一个小 bug,在设置了边框后,当前的宽度就要加上边框的宽度,实际上 offsetWidth 的值就为202,计算后为201并赋值给 width,当下一次运行时 width 的值就为201,再加上边框值 offsetWidth 的值则为203,计算后为202并赋值给 width,一次类推,所以就会出现逐渐变宽的现象。

  要解决这个 bug,就是不使用 offset 属性,最直接的办法就是将 width 属性放在行间,style.width 值只考虑本身的 width,而不考虑边框或者内边距及其他的影响因素,但是 style 仅仅只能取行间样式,如果是写在内部样式表或外部样式表,就没法用了,并且这也不符合 W3C 结构和表现相分离的原则,所以该方法是不可取的,但是我们可以看以下他是怎么实现的。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>offset的bug</title>
 6 <style>
 7 #div1{
 8     height:200px;
 9     background:red;
10     border:1px solid black;
11 }
12 </style>
13 <script>
14 setInterval(function (){
15     var oDiv = document.getElementById(div1);
16     oDiv.style.width = parseInt(oDiv.style.width) - 1 + px;
17 },30);
18 </script>
19 </head>
20 <body>
21 <div id="div1" style="width:200px">变窄</div>
22 </body>
23 </html>

  上面的代码,我们将 div 的 width 属性写在了行间,通过 oDiv.style.width 获取,再使用 parseInt 函数将字符串转换为一个整数,再减去速度,这样问题就解决了。

  最佳的解决方法,就是获取非行间样式,我们可以封装一个 getStyle 函数,再使用时传入相应的参数。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>offset的bug</title>
 6 <style>
 7 #div1{
 8     width:200px;
 9     height:200px;
10     background:red;
11     border:1px solid black;
12 }
13 </style>
14 <script>
15 function getStyle(obj,name){
16     if(obj.currentStyle){
17         return obj.currentStyle[name];
18     }
19     else{
20         return getComputedStyle(obj,false)[name];
21     }
22 }
23 setInterval(function (){
24     var oDiv = document.getElementById(div1);
25     oDiv.style.width = parseInt(getStyle(oDiv,width)) - 1 + px;
26 },30);
27 </script>
28 </head>
29 <body>
30 <div id="div1">变窄</div>
31 </body>
32 </html>

   getStyle 函数有2个参数,第一个参数 obj 为要获取的对象,第二个参数 name 为要获取的属性,并且做了兼容处理,currentStyle 针对 IE 浏览器,getComputedStyle 针对火狐浏览器。

  既然 offset 这个属性存在 bug,那么再做动画效果时就不能使用该属性了,用获取非行间样式的方法来做,下面我们就用这个方法来做 div 的变宽和变高。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>div变宽和变高</title>
 6 <style>
 7 div{
 8     width:100px;
 9     height:50px;
10     background:green;
11     margin-bottom:10px;
12     border:5px solid black;
13 }
14 </style>
15 <script>
16 window.onload = function (){
17     var oDiv = document.getElementById(div1);
18     var oDiv2 = document.getElementById(div2);
19     oDiv.onmouseover = function (){
20         startMove(this,400);
21     };
22     oDiv.onmouseout = function (){
23         startMove(this,100);
24     };
25 
26     oDiv2.onmouseover = function (){
27         startMove2(this,400);
28     };
29     oDiv2.onmouseout = function (){
30         startMove2(this,50);
31     };
32 };
33 function getStyle(obj,name){
34     if(obj.currentStyle){
35         return obj.currentStyle[name];
36     }
37     else{
38         return getComputedStyle(obj,false)[name];
39     }
40 }
41 function startMove(obj,iTarget){
42     clearInterval(obj.timer);
43     obj.timer = setInterval(function (){
44         var cur = parseInt(getStyle(obj,width));
45         var speed = (iTarget - cur)/6;
46         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
47         if(cur == iTarget){
48             clearInterval(obj.timer);
49         }
50         else{
51             obj.style[width] = cur + speed + px;
52         }
53     },30);
54 }
55 function startMove2(obj,iTarget){
56     clearInterval(obj.timer);
57     obj.timer = setInterval(function (){
58         var cur = parseInt(getStyle(obj,height));
59         var speed = (iTarget - cur)/6;
60         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
61         if(cur == iTarget){
62             clearInterval(obj.timer);
63         }
64         else{
65             obj.style[height] = cur + speed + px;
66         }
67     },30);
68 }
69 </script>
70 </head>
71 <body>
72 <div id="div1">变宽</div>
73 <div id="div2">变高</div>
74 </body>
75 </html>

   上面的代码,我们给 div 设置了5像素的边框,鼠标移入移出变宽和变高都是没问题的,为了代码的简洁,我们将获取物体当前属性值的代码保存在一个变量中,var cur = parseInt(getStyle(obj,‘width‘)),也方便之后调用,我们知道 obj.style.width 也可以写为 obj.style[‘width‘],只是后者写起来比较麻烦,平时大家不这样写,此处这样写,可以更好的说明问题,仔细观察代码,原本的框架只能让某个值运动起来,如果想要其他值运动起来,就必须修改程序,2个 startMove 框架结构是完全相同的,就只有传入的属性值不同,那么我们就可以进行代码优化了,将属性值作为参数传入  startMove 框架中,在使用时,传入什么属性,就可以改变什么属性,这就是任意值运动框架。

  下面我们就检测以下我们的任意值运动框架,做一个滤镜效果,div 的淡入淡出。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>div淡入淡出</title>
 6 <style>
 7 div{
 8     width:200px;
 9     height:200px;
10     background:red;
11     border:1px solid black;
12     filter:alpha(opacity:30);
13     opacity:0.3;
14 }
15 </style>
16 <script>
17 window.onload = function (){
18     var oDiv = document.getElementById(div1);
19     oDiv.onmouseover = function (){
20         startMove(this,opacity,100);
21     };
22     oDiv.onmouseout = function (){
23         startMove(this,opacity,30);
24     };
25 };
26 function getStyle(obj,name){
27     if(obj.currentStyle){
28         return obj.currentStyle[name];
29     }
30     else{
31         return getComputedStyle(obj,false)[name];
32     }
33 }
34 function startMove(obj,attr,iTarget){
35     clearInterval(obj.timer);
36     obj.timer = setInterval(function (){
37         var cur = parseFloat(getStyle(obj,attr))*100;
38         var speed = (iTarget - cur)/10;
39         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
40         if(cur == iTarget){
41             clearInterval(obj.timer);
42         }
43         else{
44             obj.style.opacity = (cur+speed)/100;
45             obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
46             document.getElementById(txt1).value = obj.style.opacity;
47         }
48     },30);
49 }
50 </script>
51 </head>
52 <body>
53 <input type="text" id="txt1">
54 <div id="div1"></div>
55 </body>
56 </html>

  上面的代码,div 初始透明度为0.3,鼠标移入时变为1,鼠标移开后恢复,还定义了一个文本框,用于显示 div 的透明度值,这里需要注意,透明度的值为小数,所以要使用 parseFloat 函数,将字符串转换为浮点数,而不是使用 parseInt 函数,他用于将字符串转换为整数,最后为了便于计算,在给这个数值乘以100。注意观察文本框显示的属性值,在鼠标移入时,显示为1.00999等一长串数字,而当鼠标移开后,透明度的值一直在0.300001到0.29000001之间徘徊,怎么会出现这情况呢?那是因为计算机存储的小数是一个近似值,所以会有误差,看下面的实例。

1 <script>
2 alert(0.03*100);    //返回:3
3 alert(0.05*100);    //返回:5
4 alert(10/3);        //返回:3.33……35
5 alert(0.07*100);    //返回:7.00……01
6 </script>

  上面的代码,计算0.03*100和0.05*100,结果为3和5,计算结果正确,而计算 10/3 小数点后最后一位为5,更离谱的是计算0.07*100,结果居然不是7,这误差也太大了,因此不建议进行小数点计算,所以解决上面问题的方法,就是取浮点数之后再进行四舍五入。

  那么再观察上面改变透明度的代码,跟之前改变宽和高的代码,虽然使用的框架是相同的,但是结构是不相同的,也就是不能使用改变宽高的代码来进行改变透明度,那这岂不是任意值的运动框架了,要怎么办呢?很简单,对透明度特殊对待,也就是使用判断,如果要改变的属性为透明度,那么就使用改变透明度的方法,否则就是改变其他属性,那就使用改变宽高的方法。

  下面我们看一下任意值运动的综合实例。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>任意值运动框架</title>
 6 <style>
 7 #div1{
 8     filter:alpha(opacity:30);
 9     opacity:0.3;
10 }
11 div{
12     width:100px;
13     height:50px;
14     background:red;
15     margin-bottom:10px;
16     border:1px solid black;
17     font-size:14px;
18 }
19 </style>
20 <script>
21 window.onload = function (){
22     var oDiv = document.getElementById(div1);
23     var oDiv2 = document.getElementById(div2);
24     var oDiv3 = document.getElementById(div3);
25     var oDiv4 = document.getElementById(div4);
26     oDiv.onmouseover = function (){
27         startMove(this,opacity,100);
28     };
29     oDiv.onmouseout = function (){
30         startMove(this,opacity,30);
31     };
32     oDiv2.onmouseover=function (){
33         startMove(this,fontSize,24);
34     };
35     oDiv2.onmouseout=function (){
36         startMove(this,fontSize,12);
37     };
38     oDiv3.onmouseover = function (){
39         startMove(this,width,400);
40     };
41     oDiv3.onmouseout = function (){
42         startMove(this,width,100);
43     };
44     oDiv4.onmouseover = function (){
45         startMove(this,height,400);
46     };
47     oDiv4.onmouseout = function (){
48         startMove(this,height,50);
49     };
50 };
51 function getStyle(obj,name){
52     if(obj.currentStyle){
53         return obj.currentStyle[name];
54     }
55     else{
56         return getComputedStyle(obj,false)[name];
57     }
58 }
59 function startMove(obj,attr,iTarget){
60     clearInterval(obj.timer);
61     obj.timer = setInterval(function (){
62         var cur = 0;    //用于存储物体当前的属性,方便之后调用
63         if(attr == opacity){
64             cur = Math.round(parseFloat(getStyle(obj,attr))*100);
65         }
66         else{
67             cur = parseInt(getStyle(obj,attr));
68         }
69         var speed = (iTarget - cur)/10;
70         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
71         if(cur == iTarget){
72            clearInterval(obj.timer);
73         }
74         else{
75             if(attr == opacity){
76                 obj.style.opacity = (cur+speed)/100;
77                 obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
78             }
79             else{
80                 obj.style[attr] = cur + speed + "px";
81             }
82         }
83     },30);
84 }
85 </script>
86 </head>
87 <body>
88 <div id="div1">改变透明度</div>
89 <div id="div2">改变文字大小</div>
90 <div id="div3">改变宽度</div>
91 <div id="div4">改变高度</div>
92 </body>
93 </html>

  上面的代码,不只改变了 div 的透明度,也改变了宽度和高高度,还改变了字体的大小,当然也可以使用该框架改变他的边框等其他属性。

  

  4、链式运动

  所谓链式运动,就像链条一样,一个扣一个,当一个运动结束后,开始下一个运动,要实现这样的效果,只需要放一个回调函数,也就是在运动停止时,执行的函数,再调用一次 startMove 框架,开始下一次运动,那这样就好办了,既然还是使用使用之前的框架,那么就再给他传入一个参数,这样就可以完成链式运动,下面我们就来看一下在代码中具体是怎么实现的。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>链式运动框架</title>
 6 <style>
 7 div{
 8     width:100px;
 9     height:100px;
10     background:red;
11     filter:alpha(opacity:30);
12     opacity:0.3;
13 }
14 </style>
15 <script>
16 window.onload = function (){
17     var oDiv = document.getElementById(div1);
18     oDiv.onmouseover = function (){
19         startMove(oDiv,width,300,function(){
20             startMove(oDiv,height,300,function (){
21                 startMove(oDiv,opacity,100)
22             });
23         });
24     };
25     oDiv.onmouseout = function (){
26         startMove(oDiv,opacity,30,function (){
27             startMove(oDiv,height,100,function (){
28                 startMove(oDiv,width,100);
29             });
30         });
31     };
32 };
33 function getStyle(obj,name){
34     if(obj.currentStyle){
35         return obj.currentStyle[name];
36     }
37     else{
38         return getComputedStyle(obj,false)[name];
39     }
40 }
41 function startMove(obj,attr,iTarget,fn){
42     clearInterval(obj.timer);
43     obj.timer = setInterval(function (){
44         var cur = 0;
45         if(attr == opacity){
46             cur = Math.round(parseFloat(getStyle(obj,attr))*100);
47         }
48         else{
49             cur = parseInt(getStyle(obj,attr));
50         }
51         var speed = (iTarget - cur)/10;
52         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
53         if(cur == iTarget){
54             clearInterval(obj.timer);
55             if(fn)fn();
56         }
57         else{
58             if(attr == opacity){
59                 obj.style.opacity = (cur+speed)/100;
60                 obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
61             }
62             else{
63                 obj.style[attr] = cur + speed + "px";
64             }
65         }
66     },30);
67 }
68 </script>
69 </head>
70 <body>
71 <div id="div1"></div>
72 </body>
73 </html>

  上面的代码,div 的初始透明度为0.3,宽100px 高100px,鼠标移入后,div 的宽度先从100px 运动到300px 处停止,然后高度再从100px 运动到300px 处停止,最后透明度从0.3变为1,当鼠标移开后,以相反的顺序运动,即 div 的透明度先从1变回0.3,然后高度从300px 运动回100px,最后宽度从300px 运动回100px,这就是一个简易的链式运动。

  观察上面的代码,我们要完成链式运动,那么当一次运动结束后,就需要再执行一次 startMove 框架,那么再传入一个 fn 参数,fn 就是一个函数,当执行完一段代码之后,没有马上结束,而是再调用一次这个函数,再执行一个新的程序。这里最需要注意的是,当传入一个参数进来后,需要检测运动停止,在运动结束时清空定时器后,就要判断一下,if(fn)fn(),只有当这个函数传进来再调用,也就是第一次运动停止后,如果有这个 fn,也就是传入了一个 fn,那么就让这个 fn 执行一次,如果没有最后一个参数,那么就不需要再往下执行,运动就停止了。

  

  5、同时运动

  我们刚封装了链式运动框架,就是一个扣着一个,div 先变宽完成之后再变高,最后再改变透明度,这些都是单一的运动效果,即每一次运动都只能改变一个属性,我们在某些网站应都见过这么一种效果,当鼠标移入时图片的宽度和高度会同时发生变化,这样一种效果就是多物体动画同时运动效果,也可以叫做多值运动。

  那要怎么完成这种动画效果呢?可以先要之前封装好的运动框架尝试一下,既然是同时改变 div 的宽和高,那么就不能像做链式运动那样,传入一个回调函数,当某一个值运动结束后再运动下一个值,这样就成了链式运动,而不是同时运动,那如果定义两次呢,先来看一下。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>现有框架的问题</title>
 6 <style>
 7 div{
 8     width:100px;
 9     height:100px;
10     background:red;
11 }
12 </style>
13 <script>
14 window.onload = function (){
15     oBtn = document.getElementById(btn1);
16     oDiv = document.getElementById(div1);
17     oBtn.onclick = function (){
18         startMove(oDiv,width,300);
19         startMove(oDiv,height,300);
20     };
21 };
22 function getStyle(obj,name){
23     if(obj.currentStyle){
24         return obj.currentStyle[name];
25     }
26     else{
27         return getComputedStyle(obj,false)[name];
28     }
29 }
30 function startMove(obj,attr,iTarget,fn){
31     clearInterval(obj.timer);
32     obj.timer = setInterval(function (){
33         var cur = 0;
34         if(attr == opacity){
35             cur = Math.round(parseFloat(getStyle(obj,attr))*100);
36         }
37         else{
38             cur = parseInt(getStyle(obj,attr));
39         }
40         var speed = (iTarget - cur)/10;
41         speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
42         if(cur == iTarget){
43             clearInterval(obj.timer);
44             if(fn)fn();
45         }
46         else{
47             if(attr == opacity){
48                 obj.style.opacity = (cur+speed)/100;
49                 obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
50             }
51             else{
52                 obj.style[attr] = cur + speed + "px";
53             }
54         }
55     },30);
56 }
57 </script>
58 </head>
59 <body>
60 <input id="btn1" type="button" value="运动">
61 <div id="div1"></div>
62 </body>
63 </html>

  上面的代码,div 的初始宽高都为100px,我们同时定义了让 div 的宽和高都运动到300px,点击运动按钮,会发现 div 的高度从100px 运动到300px 了,而宽并没有改变。想要同时改变 div 的宽和高,而 attr 参数只能传一个,如果定义两次,系统会执行后边的参数设置,因为执行 startMove 前先清空计时器,刚清空了计时器之后,后便的传参就会立马执行,也就是前边的一个参数被覆盖了,这是现有框架的一个小问题,不能让几个值同时运动,要解决这个问题,可以使用 JSON 完成,现在我们先回忆一下 JSON。

1 <script>
2 var json = {a:5,b:12};
3 for(var i in json){
4     alert(i + =  + json[i]);
5 }
6 </script>

  JSON 是一种轻量级的数据交互格式,JSON 语法是 JS 对象表示语法的一个子集,数据在键值对中,是以对值出现的,即 名称/值,名称和值由冒号连接,并用逗号分隔,花括号保存对象,方括号保存数组,JSON 值可以是:数字(整数或浮点数)、字符串(包含在双引号中)、对象(包含在花括号中)、数组(包含在方括号中)、逻辑值(true 或 false)、null。可以使用 for in 循环遍历 JSON 中的数据,var i in json ,i 是定义的变量,in 就是在什么里,那就是在 json 里循环遍历,上面的代码,先弹出 a = 5,再弹出 b = 12,那么 i 就表示 json 中的名称,而 json[i] 就表示 json 中某项的值,也就是 json 中变量 i 所对应的值。

  了解了 JSON 之后,那么我们如何使用 JSON 完成同时运动呢,现有的运动框架有4个参数,分别是 obj(对象)、attr(属性)、iTarget(目标)、fn(回调函数),在这4个参数中,attr 和 iTarget 也就是属性值和目标值是一对值,属性就是 json 中的名称,让谁做运动,目标就是值,到哪个位置时结束运动。那么就是说现有的运动框架,只能改变一对值,而不能实现多对值的变化,如果要实现多对值的变化,就需要用到 JSON 格式,因此就可以把 startMove 写为 startMove(obj, {attr1 : iTarget1, attr2 : iTarget1}, fn),把参数中的这一对值变成 JSON 的格式,用函数传参的方式就写为 startMove(obj, json, fn)。

  使用了 JSON 之后,还需要把 JSON 中的数据循环遍历出来,再配合 for in 循环,那要怎样循环呢,之前的运动框架,先要开启定时器,然后取当前的值,再计算速度,最后检测停止,完成这一系列的过程,当前物体触发的运动才算整个完成,那现在我们要完成多对值同时触发运动,就是让现在这整个过程多做几次循环就可以了,下面我们再来看一下代码的实现。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>同时运动</title>
 6 <style>
 7 div{
 8     width:100px;
 9     height:100px;
10     background:red;
11 }
12 </style>
13 <script>
14 window.onload = function (){
15     oBtn = document.getElementById(btn1);
16     oDiv = document.getElementById(div1);
17     oBtn.onclick = function (){
18         startMove(oDiv, {width:300, height:300});
19     };
20 };
21 function getStyle(obj,name){
22     if(obj.currentStyle){
23         return obj.currentStyle[name];
24     }
25     else{
26         return getComputedStyle(obj,false)[name];
27     }
28 }
29 function startMove(obj, json, fn){
30     clearInterval(obj.timer);
31     obj.timer = setInterval(function (){
32         for(var attr in json){
33             //1、取当前值
34             var cur = 0;
35             if(attr == opacity){
36                 cur = Math.round(parseFloat(getStyle(obj,attr))*100);
37             }
38             else{
39                 cur = parseInt(getStyle(obj,attr));
40             }
41             //2、算速度
42             var speed = (json[attr] - cur)/10;
43             speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
44             //3、检测停止
45             if(cur == json[attr]){
46                 clearInterval(obj.timer);
47                 if(fn)fn();
48             }
49             else{
50                 if(attr == opacity){
51                     obj.style.opacity = (cur+speed)/100;
52                     obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
53                 }
54                 else{
55                     obj.style[attr] = cur + speed + "px";
56                 }
57             }
58         }
59     },30);
60 }
61 </script>
62 </head>
63 <body>
64 <input id="btn1" type="button" value="运动">
65 <div id="div1"></div>
66 </body>
67 </html>

   上面的代码,点击运动按钮后,div 的宽和高同时从100px 变为300px。在开启定时器之后,就使用 for in 循环,将之前框架中 attr 参数定义为一个变量,就是在 json 中循环遍历 attr,也就是存储在 json 中的属性值,此时已经没有 iTarget 参数了,那么要设置目标值,就要把原来的 iTarget 改为 json[attr],最后在调用 startMove 时就可以用 JSON 格式传入多个值了,startMove(oDiv, {width:300, height:300}),这样就完成了同时运动。

  

  6、完美运动框架

  我们已经封装了同时运动框架,到这一刻,我们离完美的运动框架就已经不远了,那为什么还有一点距离呢,我们通过下面的实例来看一下。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>同时运动</title>
 6 <style>
 7 *{margin:0;padding:0}
 8 div{
 9     width:200px;
10     height:100px;
11     background:red;
12     border:2px solid black;
13     filter:alpha(opacity:30);
14     opacity:0.3;
15 }
16 </style>    
17 </head>
18 <script>
19 window.onload = function (){
20     oDiv = document.getElementById(div1);
21     oDiv.onmousemove = function (){
22         startMove(oDiv, {width:201, height:200, opacity:100});
23     };
24     oDiv.onmouseout = function (){
25         startMove(oDiv, {width:200, height:100, opacity:30});
26     };
27 };
28 function getStyle(obj,name){
29     if(obj.currentStyle){
30         return obj.currentStyle[name];
31     }
32     else{
33         return getComputedStyle(obj,false)[name];
34     }
35 }
36 function startMove(obj, json, fn){
37     clearInterval(obj.timer);
38     obj.timer = setInterval(function (){
39         for(var attr in json){
40             //1、取当前值
41             var cur = 0;
42             if(attr == opacity){
43                 cur = Math.round(parseFloat(getStyle(obj,attr))*100);
44             }
45             else{
46                 cur = parseInt(getStyle(obj,attr));
47             }
48             //2、算速度
49             var speed = (json[attr] - cur)/10;
50             speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
51             //3、检测停止
52             if(cur == json[attr]){
53                 clearInterval(obj.timer);
54                 if(fn)fn();
55             }
56             else{
57                 if(attr == opacity){
58                     obj.style.opacity = (cur+speed)/100;
59                     obj.style.filter = "alpha(opacity: ‘+ (cur + speed) +‘)";
60                 }
61                 else{
62                     obj.style[attr] = cur + speed + "px";
63                 }
64             }
65         }
66     },30);
67 }
68 </script>
69 <body>
70 <div id="div1"></div>
71 </body>
72 </html>

  上面的代码,使用了同时运动框架,在其实例中,多值运动并没有什么问题,多个值可以同时变化,观察上面的代码,div 的初始宽度为200px,高度为100px,透明度为0.3,在鼠标移入后,我们让他的宽度变为201px,高度变为200px,透明度变为1,这时候就出事了,打开调试工具进行测试,可以看到在鼠标移入时,div 的宽度确实是从200px 运动到201px,但是他的高度和透明度远远没有达到我们的要求,我们来分析一下为什么会出现这种情况呢,只有宽度到达了目标值,而高度和透明度并没有达到目标值,整个运动就停止了。

  回过头检查同时运动框架,我们不难发现,在做检测停止时,if(cur == json[attr]),我们判断如果当前值达到目标值时,就关闭定时器,同时运动框架和完美运动框架的距离就产生在这了,我们并没有去判断是不是所有的运动都到达了终点,那么只要有一个运动达到了终点,他就会关闭定时器,那这样肯定是不行的,就好比参加旅游团,不可能人没到齐就出发了,这样肯定是不靠谱的。那要怎么解决呢,我们需要去判断是不是所有的运动都到达了目标值,如果所有的运动都达到了目标值,那么才能关闭定时器,也就是说,如果有没有达到的,就不能关闭定时器。

  下面我们就来看看完美的运动框架到底长什么样。

 1 <script>
 2 function startMove(obj, json, fn){
 3     clearInterval(obj.timer);
 4     obj.timer = setInterval(function (){
 5         var bStop = true;    //假设所有的值都达到了目标值,这一次运动就结束了。
 6         for(var attr in json){
 7             //1.取当前值
 8             var cur = 0;
 9             if(attr == ‘opacity‘){
10                 cur = Math.round(parseFloat(getStyle(obj,attr))*100);
11             }
12             else{
13                 cur = parseInt(getStyle(obj,attr));
14             }
15             //2.算速度
16             var speed = (json[attr]-cur)/6;
17             speed = speed>0 ? Math.ceil(speed) : Math.floor(speed);
18             //3.检测停止
19             //如果有一个值不等于当前值。
20             if(cur != json[attr]){
21                 bStop = false;
22             }
23             if(attr == ‘opacity‘){
24                 obj.style.opacity = (cur+speed)/100;
25                 obj.style.filter = "alpha(opacity: "+(cur + speed)+")";
26             }
27             else{
28                 obj.style[attr] = cur + speed + "px";
29             }
30         }
31         //如果所有的值都达到了目标值,再关闭定时器。
32         if(bStop){
33            clearInterval(obj.timer);
34             if(fn){
35                 fn();
36             }
37         }
38     },30);
39 }
40 </script>

  我们把同时运动框架稍微完善以下,就是完美的运动框架了,首先在开启定时器执行循环之前,首先定义一个变量 bStop 值为 true,假设所有值都达到了目标值,那么就一次运动才算是结束了,然后在做检测停止时,再做判断,如果有一个值不等于当前值,也就是有一个值还没有达到目标值,就说明 bStop 为 false,那么就让他继续执行他的运动,在完成了整个循环之后,还要再做一个判断,如果所有值都达到了目标值,那么再关闭定时器。

  到现在,我们这个运动框架就算是真正的完美了,我们可以把这个框架保存为一个单独的 JS 文件,命名为 move.js,对于上面的问题可以简单的验证以下,再做一些其他值的运动。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>完美运动</title>
 6 <style>
 7 *{margin:0;padding:0}
 8 div{
 9     width:200px;
10     height:100px;
11     background:red;
12     border:2px solid black;
13     filter:alpha(opacity:30);
14     opacity:0.3;
15 }
16 </style>
17 <script src="JS/move++.js"></script>
18 <script>
19 window.onload = function (){
20     oDiv = document.getElementById(div1);
21     oDiv.onmousemove = function (){
22         startMove(oDiv, {width:301, height:402, opacity:100, fontSize:30, borderWidth:5});
23     };
24     oDiv.onmouseout = function (){
25         startMove(oDiv, {width:200, height:100, opacity:30, fontSize:12, borderWidth:2});
26     };
27 };
28 </script>
29 </head>
30 <body>
31 <div id="div1">多值同时运动</div>
32 </body>
33 </html>

  上面我们使用了完美运动框架,鼠标移入时,同时改变了 div 的宽、高、透明度、字体大小和边框宽度,鼠标移出时恢复初始定义,一点问题都没有,非常完美。

  

  7、运动框架的总结

  其实我们完全可以在网上找一个完美的运动框架,直接拿来使用,万能的互联网,出产任何你能想到的东西,这样也相当省事,但是对于一个学习者来说,如果所有的东西都直接搬来用,这样是没有任何问题,但是你并不了解他具体是怎么实现的,这个实现的过程才是最值得研究的,也是最有价值的,看着我们写的代码,一步步的趋于完美,在这个过程中,才能更好的锻炼逻辑思维,培养编程的思想,只有最基础的知识掌握了,根基扎实了,才能在编程的道路上走的更高更远,所谓一法通则万法通,多悟多总结,很多东西都可以触类旁通,道法自然,当然每个人都有自己的学习方式,只要是适合自己的,那就是最好的。

  闲话就不多说了,我们来看一下运动框架的演变过程。

  最简单的运动框架:startMove(iTarget)

  我们在刚开始定义了一个 startMove 函数,只需要开一个定时器,就可以让 div 动起来,但是根本停不下来,我们又做了判断,如果达到了目标值就清空定时器,但是还有一点小问题,虽然到目标位置了,定时器也清空了,再点击按钮,div 还是会运动一下,因为点击按钮之后 startMove 还会被执行一次,所以我们在执行 startMove 时,先要清空定时器,之后我们做了匀速运动的分享到侧边栏效果,因为有2个目标值,我们定义了 startMove 和 stopMove 两个函数,用于内容容器的展示和隐藏,这两个函数长的几乎一样,具有相同的功能,我们又做了代码优化,把不同的东西找出来,用参数的方式传入。

  起初我们给 startMove 定义了2个参数,speed 和 iTarget,在功能相同的情况下,传入的参数越少越好,目标值参数是不可以省略的,因为得有一个终点,而速度值是可以省略的,因为不管速度快还是慢,都会到达终点。

  我们还说了侧边栏分享效果使用缓冲运动做效果更好。

  多物体运动框架:startMove(obj, iTarget)

  想让多个物体运动起来,只需要再传入一个参数,也就是好对象参数,我们传入哪个对象,就运动哪个对象。

  任意值运动框架:startMove(obj, attr, iTarget)

  多个物体运动起来还不够,如果想让任意属性值运动,那就再传入一个参数,也就是属性参数,我们传入哪个属性,哪个属性就发生变化。

  我们还了解了 offset 属性的一个 bug,我们想让 div 的宽度逐渐变窄时,给他加了边框属性,不但没有变窄,反而是逐渐变宽了,因为 offset 属性受到其他属性的影响,他判断当前的属性值为宽度值加上边框值,最好的解决办法就是获取非行间样式,我们又封装了一用于获取非行间样式的函数 getStyle。

  链式运动框架:startMove(obj, attr, iTarget, fn)

  链式运动就是让宽变完之后再变高,高变完之后再变其他属性,一环扣一环,那么使用回调函数就可以解决了,当一次运动结束后,再调用一次 startMove,开始一个新的运动,那么再给他传一个参数。

  同时运动框架:startMove(obj, json)

  之前的框架都做的是单一的运动,那么既想让宽变化,也想让高变化,最好的办法就是使用 JSON 格式,我们简单的回忆了一下 JSON,他是一种轻量级的数据交换格式,在 startMove 这4个参数中,分别是对象、属性、目标值、回调函数,属性值和目标值是一对值,那么就以 JSON 的方式传入,再使用 for in 循环执行整个运动,就可以实现多值的运动了。

  完美运动框架:startMove(obj, json, fn)

  在封装了同时运动框架时,我们离完美运动框架就差一个判断了,同时运动框架,在检测停止时,判断如果是达到了目标值就清空定时器,而如果有一个值达到了目标值,其他值并未达到目标值,他也会清空定时器,这显然是不科学的,这并不是先到先到,所以我们定义了一个变量,假设所有值都达到了目标值,这一次运动才算结束,然后再做判断,如果有一个值没有达到目标值,那么就继续执行他的运动,最后直到所有值都达到目标值了,再关闭定时,这就完成了完美运动框架的封装。

  

  现在,我们就可以使用完美运动框架来完成网站中一些常见的动画效果了,比如图片轮播、幻灯片展示、以及微博实时滚动效果等。

 

 

 

 

 

 

 

  

 

JavaScript学习总结【11】、JS运动

标签:

原文地址:http://www.cnblogs.com/Mtime/p/5130927.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!