程序员的知识教程库

网站首页 > 教程分享 正文

「JavaScript从入门到精通」JS基础运动-02

henian88 2024-10-20 05:54:52 教程分享 168 ℃ 0 评论

缓冲运动

在现实生活中,很多运动并不是完全匀速的,在一个物体停止之前,必然会有一段速度的缓冲。为了尽可能贴近真实,我们希望能够通过JS制作出缓冲运动的效果。缓冲运动的要求是让元素逐渐变慢,最后停止。我们可以理解为,当元素距离终点位置远的时候,其速度较快,而距离近的时候,速度较慢。也就是说,速度与距离成正比,我们可以通过距离来控制速度。

<html>
 <head> 
 <meta charset="utf-8" /> 
 <title>无标题文档</title> 
 <style>
#div1 {width:100px; height:100px; background:red; position:absolute; left:0; top:50px;}
</style> 
 <script>
function startMove()
{
var oDiv=document.getElementById('div1');
setInterval(function (){
var speed=(300-oDiv.offsetLeft)/10;
oDiv.style.left=oDiv.offsetLeft+speed+'px';
}, 30);
}
</script> 
 </head> 
 <body> 
 <input type="button" value="开始运动" onclick="startMove()" /> 
 <div id="div1"> 
 </div> 
 </body>
</html>

效果如下:

通过更改var speed=(300-oDiv.offsetLeft)/10中的分子可以改变缓冲运动的速度。但实际上,如果我们将div块运动后的left值打印出来,会发现并不是300而是291,也就是说div块并没有运动到我们想让它运动到的位置,这是为什么呢?我们来分析一下,当div块运动到291px时,其速度还剩下0.9px,但px(像素)是计算机能接收的最小单位,因此速度小于1(0.9)之后,div块从291px变为了291.9px,这时候计算机会直接将291.9px变为291px,相当于没有发生运动。为了避免这种情况发生,我们需要用到Math类。Math类是JS里一个很常用的类,具体的我们以后再讲。Math类里面有一个ceil方法,其作用为在向上取整,如下所示:

alert(Math.ceil(3.5));

输出结果为4。和ceil相对的方法是floor,作用为向下取整,用法和ceil一样,这里不再多说。

回到我们的缓冲运动,这里只要我们对speed进行向上取整,那么当其速度小于1时就会变为1,这样就可以到达终点了。

function startMove()
{
var oDiv=document.getElementById('div1');
setInterval(function (){
var speed=(300-oDiv.offsetLeft)/10;
speed=Math.ceil(speed);
oDiv.style.left=oDiv.offsetLeft+speed+'px';
document.title=oDiv.offsetLeft+','+speed;
}, 30);
}

这里依然存在一个小问题,现在我们将div1的left值改为600px,那么它也不会到达终点。原因是,当速度达到-1-0之间的时候,向上取整会导致速度直接变为0。因此,我们还需要对速度的正负性进行判断,如果是正数则使用ceil方法,负数则使用floor方法。

<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
<style>
#div1 {width:100px; height:100px; background:red; position:absolute; left:0; top:50px;}
</style>
<script>
function startMove()
{
var oDiv=document.getElementById('div1');
setInterval(function (){
var speed=(300-oDiv.offsetLeft)/10;
//speed=Math.floor(speed);
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 type="button" value="开始运动" onclick="startMove()" />
<div id="div1"></div>
<div id="div2"></div>
</body>
</html>

效果如下:

记得我们在学DOM的时候,学过制作过一个悬浮在右边的框,但实际上那个框会存在一定的问题:拖动页面时框会产生抖动。现在我们要想方法来避免这个抖动,所以我们来看看缓冲菜单是怎么做的。

<html>
 <head> 
 <meta charset="utf-8" /> 
 <title>无标题文档</title> 
 <style>
#div1 {width:100px; height:150px; background:red; position:absolute; right:0; bottom:0;}
</style> 
 <script>
window.onscroll=function ()
{
var oDiv=document.getElementById('div1');
var scrollTop=document.documentElement.scrollTop||document.body.scrollTop;
//oDiv.style.top=document.documentElement.clientHeight-oDiv.offsetHeight+scrollTop+'px';
startMove(document.documentElement.clientHeight-oDiv.offsetHeight+scrollTop);
};
var timer=null;
function startMove(iTarget)
{
var oDiv=document.getElementById('div1');
clearInterval(timer);
timer=setInterval(function (){
var speed=(iTarget-oDiv.offsetTop)/4;
speed=speed>0?Math.ceil(speed):Math.floor(speed);
if(oDiv.offsetTop==iTarget)
{
clearInterval(timer);
}
else
{
oDiv.style.top=oDiv.offsetTop+speed+'px';
}
}, 30);
}
</script> 
 </head> 
 <body style="height:2000px;"> 
 <div id="div1"></div> 
 </body>
</html>

效果如下:

通过将悬浮框导入我们的运动框架就可以打到这样的效果。如果我们将document.documentElement.clientHeight-oDiv.offsetHeight的值除以2,可以看到,div块的悬浮位置从页面底部变为了页面中间。但大家如果运行这个程序,会发现,div块会进行轻微的抖动。这是为什么呢?我们来做一个小小的测试,用一个textbox来显示div块的的top值,会发现div块的top值在858和859之间不断跳动,这是为什么呢?实际上,目标点的实际值是858.5px(因为除以2的原因有可能出现这样的情况),但计时器每次都只能前进1px,因此div块会跑到859px,然后因为超过了要求值又向回跳到858px,如此往复,形成了这样的抖动效果。解决方法也很简单,在将document.documentElement.clientHeight-oDiv.offsetHeight的值除以2之后取整即可。

匀速运动的停止条件

和缓冲运动一样,匀速运动也存在始终到不了精确的终点(相比于最后速度小于1的缓冲运动,匀速运动很容易发生越界的情况)而发生反复抖动的情况。实际上对于匀速运动来说,只要元素和目的位置足够近就算达成了目标。在JS的Math类里有一个abs()方法用于计算数字的绝对值,使用这种方法可以避免匀速运动在终点时的抖动。

<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);
}
</script> 
 </head> 
 <body> 
 <input type="button" value="到100" onclick="startMove(100)" /> 
 <input type="button" value="到300" onclick="startMove(300)" /> 
 <div id="div1"></div> 
 <div id="div2"></div> 
 <div id="div3"></div> 
 </body>
</html>

效果如下:

当我们的元素和目标位置的距离小于等于速度的时候,我们就认为元素已经到达了终点,于是将定时器关闭,同时将元素直接设置在目标位置。这样既防止了元素抖动,也能让元素精确地到达终点。

我是石川(Blue),如果你觉得我的文章还不错,请多帮我推荐给你的朋友,多谢了。

作者简介:前阿里巴巴高级技术经理,现开课吧技术学院院长。精通C/C++、Java、Python、前端开发等多种开发技术,曾参与淘宝网的早期建设和优化,拥有丰富的企业级系统开发经验,对HTML5移动端互联网技术及生态体系有深厚的造诣。

想了解更多前端知识,可以关注文章下的评论。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表