网站首页 > 基础教程 正文
缓冲运动
在现实生活中,很多运动并不是完全匀速的,在一个物体停止之前,必然会有一段速度的缓冲。为了尽可能贴近真实,我们希望能够通过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移动端互联网技术及生态体系有深厚的造诣。
想了解更多前端知识,可以关注文章下的评论。
猜你喜欢
- 2024-11-05 javascript的科普基础二 javascript的介绍
- 2024-11-05 JavaScript-第二章 javascriptj
- 2024-11-05 Javascript一些实用技巧 javascript循环技巧
- 2024-11-05 第31节 类型和对象-Javascript-零点程序员-王唯
- 2024-11-05 Js复习小结 js总结
- 2024-11-05 「收藏」JS数组排序技巧汇总(冒泡、sort、快速、希尔等排序)
- 2024-11-05 JavaScript Promise 详解 js中promise的使用与理解
- 2024-11-05 web前端:原生js全动画企业官网,开机动画、切屏/分屏动画
- 2024-11-05 SpreadJS教程:如何在填报场景中使用数据绑定获取数据源
- 2024-11-05 纯JavaScript实现的MQTT智能门锁 智能门锁近三年的市场数据采集
- 最近发表
- 标签列表
-
- jsp (69)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (86)
- location.href (69)
- dockerexec (65)
- tail-f (79)
- queryselectorall (63)
- location.search (79)
- bootstrap教程 (74)
- 单例 (62)
- linuxgzip (68)
- 字符串连接 (73)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)