1.运动原理
Js运动,本质来说,就是让 web 上 DOM 元素动起来。而想要 DOM 动起来,改变其自身的位置属性,比如高宽,左边距,上边距,透明度等。还有一个很重要的前提是,运动的物体必须是绝对定位。
window.onload = function(){ var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ var oDiv = document.getElementById('div1'); //设置定时器 setInterval(function(){ //改变物体位置 oDiv.style.left = oDiv.offsetLeft + 10 + 'px'; },30) } }
上述代码,点击btn之后,就能是物体向左运动。可是会一直向右动,不会停止。因此需要创立一个停止的条件。在条件符合的情况下,清楚定时器。其中对于目标点的判断,尤为重要。
window.onload = function(){ var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ var oDiv = document.getElementById('div1'); //设置定时器 var timer = setInterval(function(){ //判断停止条件 if(oDiv.offsetLeft > 300){ clearInterval(timer); }else{ //改变物体位置 oDiv.style.left = oDiv.offsetLeft + 10 + 'px'; document.title = oDiv.offsetLeft; } },30); } }
上述代码中,但物体的位置大于300的时候,将停止运动。但是上述代码还有个问题,就是连续点击按钮,物体会运动越来越快。因为每点击一次,就开了一个定时器,累加的定时器。造成运动混乱。
2,运动框架 (滑入滑出,淡入淡出)
window.onload = function(){ var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ startMove(); } } var timer = null; function startMove(){ var oDiv = document.getElementById('div1'); clearInterval(timer); //设置定时器 timer = setInterval(function(){ //判断停止条件 if(oDiv.offsetLeft > 300){ clearInterval(timer); }else{ //改变物体位置 oDiv.style.left = oDiv.offsetLeft + 10 + 'px'; document.title = oDiv.offsetLeft; } },30); }
此外,在改变物体位置的时候,那个 “10”则是更改的数量,其实也就是速度。如果更改速度,运动的快慢就能确定。因此,运动框架的原理,基本步骤为:
1.先清除定时器
2.开启定时器,计算速度
3.判断停止条件,执行运动
var timer = null; function startMove(){ var oDiv = document.getElementById('div1'); clearInterval(timer); //计算速度 var iSpeed = 10; //设置定时器 timer = setInterval(function(){ //判断停止条件 if(oDiv.offsetLeft > 300){ clearInterval(timer); }else{ //改变物体位置 oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px'; document.title = oDiv.offsetLeft; } },30); }
对于停止条件,写死在里面了,所以需分离出参数。下面是一个分享到的例子。主要是根据目标判断速度的正负。从而在鼠标滑入画出时候进行运动/恢复的效果。


<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JS运动——分享到</title> <style> *{padding: 0; margin: 0;} #div1{ width: 150px; height: 200px; background: #ccc; position: absolute; left: -150px;} #div1 span{ width: 20px; height: 60px; line-height: 20px; background: #8E91FF; color: #fff; position: absolute; top: 70px; right: -20px;} </style> <script> // 方法一:传一个参数(iTarget) /*window.onload = function(){ var oDiv = document.getElementById('div1'); var timer = null; oDiv.onmouseover = function(){ startMove(0); }; oDiv.onmouseout = function(){ startMove(-150); }; function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ var speed = 0; oDiv.offsetLeft > iTarget ? speed = -10 : speed = 10; if(oDiv.offsetLeft == iTarget){ clearInterval(timer); }else{ oDiv.style.left = oDiv.offsetLeft + speed + 'px'; } },30); }; };*/ // 方法二:传两个参数(speed,iTarget) window.onload = function(){ var oDiv = document.getElementById('div1'); var timer = null; oDiv.onmouseover = function(){ startMove(10,0); }; oDiv.onmouseout = function(){ startMove(-10,-150); }; function startMove(speed,iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ // 判断oDiv距离左边的距离是否等于目标点 if(oDiv.offsetLeft == iTarget){ clearInterval(timer); }else{ oDiv.style.left = oDiv.offsetLeft + speed + 'px'; } },30); }; }; </script> </head> <body> <div id="div1"><span>分享到</span></div> </body> </html>
另外一个小例子,淡入淡出,即改变物体的透明度,由于没有像原生的位置属性那样的offsetLset. 需要一个变量来保存透明度的值,用来和速度加减,最后付给元素的透明度样式。从而实现淡入淡出效果。


<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JS运动——淡入淡出</title> <style> #div1{ width: 100px; height: 100px; background: green; position: absolute; filter:alpha(opacity:30); opacity: .3;} </style> <script> /*window.onload = function(){ var oDiv = document.getElementById('div1'); var timer = null; var alpha = 30; oDiv.onmouseover = function(){ startMove(100); }; oDiv.onmouseout = function(){ startMove(30); }; function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ var speed = 0; if(alpha < iTarget){ speed = 10; }else{ speed = -10; } if(alpha == iTarget){ clearInterval(timer); }else{ alpha += speed; oDiv.style.filter = 'alpha(opacity:' + alpha +')'; oDiv.style.opacity = alpha/100; } },30); }; };*/ window.onload = function(){ var oDiv = document.getElementById('div1'); var timer = null; var alpha = 30; oDiv.onmouseover = function(){ startMove(10,100); }; oDiv.onmouseout = function(){ startMove(-10,30); }; function startMove(speed,iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ if(alpha == iTarget){ clearInterval(timer); }else{ alpha += speed; oDiv.style.filter = 'alpha(opacity:' + alpha +')'; oDiv.style.opacity = alpha/100; } },30); } }; </script> </head> <body> <div id="div1"></div> </body> </html>
匀速运动:
匀速运动的停止条件:距离足够近


<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>匀速运动</title> <style> #div1 {width:100px; height:100px; background:red; position:absolute; left:600px; top:50px;} #div2 {width:1px; height:300px; position:absolute; left:300px; top:0; background:black;} #div3 {width:1px; height:300px; position:absolute; left:100px; top:0; background:black;} </style> <script> /* 匀速运动的停止条件 距离足够近 */ /*var timer=null; function startMove(iTarget) { var oDiv=document.getElementById('div1'); clearInterval(timer); timer=setInterval(function (){ var speed=0; if(oDiv.offsetLeft<iTarget) { speed=7; } else { speed=-7; } if(Math.abs(iTarget-oDiv.offsetLeft)<=7) { clearInterval(timer); oDiv.style.left=iTarget+'px'; } else { oDiv.style.left=oDiv.offsetLeft+speed+'px'; } }, 30); }*/ window.onload = function(){ var oBtn1 = document.getElementById('btn1'); var oBtn2 = document.getElementById('btn2'); var oDiv = document.getElementById('div1'); var timer = null; oBtn1.onclick = function(){ startMove(100); }; oBtn2.onclick = function(){ startMove(300); }; function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ var speed = 0; if(oDiv.offsetLeft < iTarget ){ speed = 7; }else{ speed = -7; } if(Math.abs(iTarget - oDiv.offsetLeft)<=7){ clearInterval(timer); oDiv.style.left = iTarget + 'px'; }else{ oDiv.style.left = oDiv.offsetLeft + speed + 'px'; } },30); }; }; </script> </head> <body> <!-- <input type="button" value="到100" onclick="startMove(100)" /> <input type="button" value="到300" onclick="startMove(300)" /> --> <input id="btn1" type="button" value="运动到100" /> <input id="btn2" type="button" value="运动到300" /> <div id="div1"></div> <div id="div2"></div> <div id="div3"></div> </body> </html>
缓冲运动
缓冲运动原理就是,改变速度的值。每次累加的速度值变小,就是会是整个物体看起来越来越慢,以至于最后停掉。
window.onload = function(){ var btn = document.getElementsByTagName('input')[0]; btn.onclick = function(){ startMove(300); } } var timer = null; function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ //求出带有变化的速度 var iSpeed = (iTarget - oDiv.offsetLeft) / 8; if(oDiv.offsetLeft == iTarget){ clearInterval(timer); }else{ oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px'; } document.title = oDiv.offsetLeft + '...' + iSpeed; },30); }
上述方法可以得到缓冲运动,但是实际运行效果,物体并没有在 300的位置停掉,而是在 293的位置就停掉了。究其原因。因为当物体的速度小于1 的时候。物体位置为293。此时计算的速度是 0.875.通过 oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px'; 物体的位置为 293.875.可是计算机不能识别小数,将小数省略了。此时的 位置offsetLeft仍然是 293.再计算一次,还是同样的结果。定时器没有关掉,但是物体的位置却再也无法改变,故停留在了 293的位置。解决方案,就是将速度进行向上取整。但是,像上述运动,速度是正的,可是,当速度是负的时候,就同样会有相同的结果,因此需要在速度为负的时候,向下取整。
function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ var iSpeed = (iTarget - oDiv.offsetLeft) / 8; //对正的速度向上取整,负的速度向下取整 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); if(oDiv.offsetLeft == iTarget){ clearInterval(timer); }else{ oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px'; } document.title = oDiv.offsetLeft + '...' + iSpeed; },30); }
1、当前值离目标值越近,速度越慢
当前值离目标值越远,速度越快
speed = (目标值-当前值)/10
2、与目标点相差一点,需要进行判断
目标值>当前值,需要对速度向上取整,即speed = Math.ceil(speed)
目标值<当前值,需要对速度向下取整,即speed = Math.floor(speed)
3、如果当前值=目标值,清除动画
完整缓冲运动demo


<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>缓冲运动</title> <style> #div1 {width:100px; height:100px; background:red; position:absolute; left:600px; top:50px;} #div2 {width:1px; height:300px; background:black; position:absolute; left:300px; top:0;} </style> <script> window.onload = function(){ var oBtn = document.getElementById('btn1'); var oDiv = document.getElementById('div1'); var timer = null; oBtn.onclick = function(){ startMove(300); }; function startMove(iTarget){ var oDiv = document.getElementById('div1'); clearInterval(timer); timer = setInterval(function(){ var speed = (iTarget - oDiv.offsetLeft)/10; // 但凡用到缓冲运动,一定要用到向上/向下取整! speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); oDiv.style.left = oDiv.offsetLeft + speed + 'px'; document.title=oDiv.offsetLeft+','+speed; },30); } }; </script> </head> <body> <input id="btn1" type="button" value="缓冲运动到300" /> <div id="div1"></div> <div id="div2"></div> </body> </html>
多物体运动
下一步,就是处理多物体运动,运动函数里面每次都要选取一个元素加事件。如果需要对多个物体进行同样的运动, 需要将运动对象作为参数传进来。
window.onload = function(){ var aDiv = document.getElementsByTagName('div'); for(var i=0;i<aDiv.length;i++){ aDiv[i].onmouseover = function(){ startMove(this,300); } aDiv[i].onmouseout = function(){ startMove(this,100); } } } var timer = null; function startMove(obj,iTarget){ clearInterval(timer); timer = setInterval(function(){ var iSpeed = (iTarget - obj.offsetWidth) / 8; iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); if(obj.offsetWidth == iTarget){ clearInterval(timer); }else{ obj.style.width = obj.offsetWidth + iSpeed + 'px'; } },30) }
通过循环物体,将物体的 this传给运动函数,使得多物体可以运动。但是这样有一个弊端,即当滑入第一个运动的时候,开启了定时器。如果此时,滑入另外一个物体,将会清理上一个定时器。这就造成了,上一次运动,很有可能还没完成结束,定时器就没关闭了。解决的方法,每个运动的物体,都能开了一个属于自己的定时器。因此,把定时器当成物体的属性。清理的时候也就是清理自己的定时器。
window.onload = function(){ var aDiv = document.getElementsByTagName('div'); for(var i=0;i<aDiv.length;i++){ aDiv[i].onmouseover = function(){ startMove(this,300); } aDiv[i].onmouseout = function(){ startMove(this,100); } } } function startMove(obj,iTarget){ //将定时器,变成物体的属性,类似给物体添加索引 clearInterval(obj.timer); obj.timer = setInterval(function(){ var iSpeed = (iTarget - obj.offsetWidth) / 8; iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); if(obj.offsetWidth == iTarget){ clearInterval(obj.timer); }else{ obj.style.width = obj.offsetWidth + iSpeed + 'px'; } },30) }
多物体运动完整demo


<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>多物体运动——改变物体宽度</title> <style> div {width:100px; height:50px; background:red; margin:10px; border:10px solid black;} </style> <script> window.onload = function(){ var aDiv = document.getElementsByTagName('div'); for( var i = 0; i < aDiv.length; i++){ aDiv[i].timer = null; aDiv[i].onmouseover = function(){ startMove(this,400); }; aDiv[i].onmouseout = function(){ startMove(this,100); }; } function startMove(obj,iTarget){ clearInterval(obj.timer); obj.timer = setInterval(function(){ var speed = (iTarget - obj.offsetWidth)/6; speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); if( obj.offsetWidth == iTarget){ clearInterval(obj.timer); }else{ obj.style.width = obj.offsetWidth + speed + 'px'; } },30); }; }; </script> </head> <body> <div></div> <div></div> <div></div> </body> </html>
多物体的淡入淡出的时候,也有类似的问题。因为修改透明度的时候,是先用一个变量保存透明度,必须针对每个物体设立透明度值属性。
多物体运动透明度demo

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>多物体运动——改变物体透明度</title>
<style>
div {width:100px; height:50px; background:red; margin:10px; border:10px solid black; filter:alpha(opacity:30); opacity: .3;}
</style>
<script>
window.onload = function(){
var aDiv = document.getElementsByTagName('div');
for( var i = 0; i < aDiv.length; i++){
aDiv[i].timer = null;
aDiv[i].alpha = 30;
aDiv[i].onmouseover = function(){
startMove(this,100);
};
aDiv[i].onmouseout = function(){
startMove(this,30);
};
}
function startMove(obj,iTarget){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
var speed = (iTarget - obj.alpha)/6;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if( obj.alpha == iTarget){
clearInterval(obj.timer);
}else{
obj.alpha += speed;
obj.style.filter = 'alpha(opacity:'+obj.alpha+')';
obj.style.opacity = obj.alpha / 100;
}
},30);
};
};
</script>
</head>
<body>
<div></div>
<div></div>
<div></div>
</body>
</html>