网站首页 > 教程分享 正文
缓冲运动
在现实生活中,很多运动并不是完全匀速的,在一个物体停止之前,必然会有一段速度的缓冲。为了尽可能贴近真实,我们希望能够通过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移动端互联网技术及生态体系有深厚的造诣。
想了解更多前端知识,可以关注文章下的评论。
- 上一篇: 零基础学web前端!可以多看看这些书
- 下一篇: 程序员:推荐书单(程序员的书单)
猜你喜欢
- 2024-10-20 web开发需要学什么?有发展前途吗?
- 2024-10-20 2021年全新升级的前端学习路线图(2021前端新技术)
- 2024-10-20 郑州web前端开发培训:谈谈HTML5的发展前景
- 2024-10-20 程序员:推荐书单(程序员的书单)
- 2024-10-20 零基础学web前端!可以多看看这些书
- 2024-10-20 网络编程零基础如何入门?10年老司机经验分享
- 2024-10-20 HTML5前端开发进阶:如何区别正向代理与反向代理?
- 2024-10-20 新书速览|SQL Server 2022从入门到精通:视频教学超值版
- 2024-10-20 扣丁学堂HTML5培训要学哪些软件(扣丁学堂登录)
- 2024-10-20 html5经典教程都学习哪些内容?(html5入门经典)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- css导航条 (66)
- sqlinsert (63)
- js提交表单 (60)
- param (62)
- parentelement (65)
- jquery分享 (62)
- check约束 (64)
- curl_init (68)
- sql if语句 (69)
- import (66)
- chmod文件夹 (71)
- clearinterval (71)
- pythonrange (62)
- 数组长度 (61)
- javafx (59)
- 全局消息钩子 (64)
- sort排序 (62)
- jdbc (69)
- php网页源码 (59)
- assert h (69)
- httpclientjar (60)
- postgresql conf (59)
- winform开发 (59)
- mysql数字类型 (71)
- drawimage (61)
本文暂时没有评论,来添加一个吧(●'◡'●)