JavaScript动画工作原理之(二)
来源:互联网 发布:淘宝休闲鞋怎么样 编辑:程序博客网 时间:2024/05/01 18:26
原作者:Steven Riche
发布时间:2014年1月30日
原文链接:http://code.tutsplus.com/tutorials/javascript-animation-that-works-part-2-of-4--net-35237
翻译:子毅 ------- 将JavaScript进行到底
在上一篇文章中,我们介绍了在JavaScript中使用精灵,使得动画在所有浏览器中都能够工作。我们还介绍了怎样为一个div元素设置精灵背景,并且用一行JavaScript代码来改变背景的位置,使得图片看起来在动一样。
在本篇文章中,我们将使用这种技术来实现跑和跳的动作。为了制作动画,我们必须在有规律的时间间隔内,快速地改变背景位置。再一次看看我们所用的精灵
在我们的例子中,总共有十张图片:一张是J向右站着,三张J向右跑,一张J向右时跳起来(向左也是同样如此)。现在开始让它向右跑。为了使图片看起来在跑动,我们需要做两件事:改变精灵为另一张图片,将div元素向右移动。
向右跑的动画
我们肯定不想通过不停地点击不同的按钮来循环精灵,因此,我们需要创建一些函数来自动地完成这些事情。
我们想要执行函数来完成这些事情:
- 将div元素慢慢地向右移动
- 移动到下一帧动画
- 暂停一秒钟(保留“视觉暂留”幻觉)
- 再次循环函数
幸运的是,这里有一种简单的方式来循环执行函数。在JavaScript有一个原生的命令,叫做setTimeout,允许我们创造一段时间的延时,一段时间后我们将再次执行这个函数(在这个函数内部)
function run_right() {//慢慢地向右移动……//改变动画到下一帧……//200毫秒后再次执行run_right函数setTimeout(function() {run_right();}, 200);}
现在我们就有了一个可以在一秒内调用自己执行五次的函数(这已经足够快,能够达到创建动画的目的)。但记住,各个浏览器并没有精准的计时器,你可以指定毫秒级的时间,但并不意味着你的脚本在那个时间执行!
下一个要解决的问题是,怎样让函数知道下一张精灵是什么?在这个例子中,我们需要在三张图片之间循环(来使得动画有四帧)。为了做到这一定,我们需要想函数传递一些信息来告诉它转化到哪一个slide。一旦函数执行,就检测在哪一个slide上,然后改变背景位置到正确的精灵。当再次执行函数时,将下一个slide作为参数传递给函数。
function run_right(slide) {//慢慢地向右移动……switch(slide) { //此开关语句检查不同可能性的幻灯片case 1: //假如'slide'等于'1'document.getElementById('j').style.backgroundPosition = '-40px 0px';setTimeout(function() {run_right(2);}, 200);break;case 2: //假如'slide'等于'2'document.getElementById('j').style.backgroundPosition = '-80px 0px';setTimeout(function() {run_right(3);}, 200);break;case 3:document.getElementById('j').style.backgroundPosition = '-120px 0px';setTimeout(function() {run_right(4);}, 200);break;case 4:document.getElementById('j').style.backgroundPosition = '-80px 0px';setTimeout(function() {run_right(1);}, 200);break;}}
现在我们执行一次函数,并保证给它传递开始slide
<input type="button" value="Run Right" onclick="run_right(1);" />
相应地,为了将div元素也向右慢慢地移动,我们可以传递div的初始left属性值给函数,然后在函数每次执行的时候靠增加left值来向右移动。
function run_right(slide, left) {//慢慢地向右移动……left = left + 15;document.getElementById('j').style.left = left + 'px';switch(slide) { //此开关语句检查不同可能性的幻灯片case 1: //假如'slide'等于'1'document.getElementById('j').style.backgroundPosition = '-40px 0px';setTimeout(function() {run_right(2, left);}, 200);break;case 2: //假如'slide'等于'2'document.getElementById('j').style.backgroundPosition = '-80px 0px';setTimeout(function() {run_right(3, left);}, 200);break;case 3:document.getElementById('j').style.backgroundPosition = '-120px 0px';setTimeout(function() {run_right(4, left);}, 200);break;case 4:document.getElementById('j').style.backgroundPosition = '-80px 0px';setTimeout(function() {run_right(1, left);}, 200);break;}}
注意,在初始化函数时,要保证给函数传递div元素当前的左偏移量
<input type="button" value="Run Right" onclick="run_right(1, document.getElementById('j').offsetLeft);" />
停止动画
现在我们已经有了一个函数,当调用它的时候,J会向右跑动。不幸的是,我们没有办法让它停下来。首先,当J跑到stage边缘的时候,函数会自动停止执行,为了做到这一点,每次函数执行的时候,我们需要检测一个if声明,判断J是否还有空间来跑动,假如有,函数正常执行,反之则停止函数的执行,并将精灵置于站立状态。
function run_right(slide, left) {//假如将left增加15px,J的右边没有超过stage的边缘if ((left + 15) < (document.getElementById('stage').offsetWidth - document.getElementById('j').offsetWidth)) {//还有空间,函数正常执行} else {//假如J超过stage右边,函数停止执行,将精灵置于站立状态document.getElementById('j').style.backgroundPosition = '0px 0px';}}
最后,当需要的时候,我们想要以一种方式停止函数的执行。可以将setTimeout()赋值给一个变量,需要停止时,将这个变量传递给clearTimeout()。为了做到这一点,需要在函数外面声明一个变量,之后就可以应用它。现在,将这个变量声明为一个全局变量。但是这是一种糟糕的编程实践,但是我们会在下一篇文章中纠正它。现在我们的函数是这个样子。
var timer; function run_right(slide, left){ if ((left + 15) < (document.getElementById('stage').offsetWidth - document.getElementById('j').offsetWidth)){ left = left + 15; // 将它的left属性值增加15px document.getElementById('j').style.left = left+"px"; switch (slide){ //此开关语句检查不同可能性的幻灯片 case 1: //假如'slide'等于'1'... document.getElementById('j').style.backgroundPosition = "-40px 0px"; setTimeout(function(){run_right(2, left);}, 200); break; case 2: //假如'slide'等于'2'... document.getElementById('j').style.backgroundPosition = "-80px 0px"; setTimeout(function(){run_right(3, left);}, 200); break; case 3: document.getElementById('j').style.backgroundPosition = "-120px 0px"; setTimeout(function(){run_right(4, left);}, 200); break; case 4: document.getElementById('j').style.backgroundPosition = "-80px 0px"; setTimeout(function(){run_right(1, left);}, 200); break; } } else { document.getElementById('j').style.backgroundPosition = "0px 0px"; }}
我们可以创建另一个函数来停止跑动计时器,并且将精灵置于站立状态
function stop_running() {document.getElementById('j').backgroundPosition = '0px 0px';clearTimeout(timer);}
向左跑的动画
现在借用run_right()函数的代码,只需要做一点点修改,我们就可以创建另一个向左跑的函数run_left()
function run_left(stage, left){ if ((left - 15) > 0){ left = left - 15; document.getElementById('j').style.left = left+"px"; switch (stage){ case 1: document.getElementById('j').style.backgroundPosition = "-40px -50px"; timer = setTimeout(function(){run_left(2, left);}, 200); break; case 2: document.getElementById('j').style.backgroundPosition = "-80px -50px"; timer = setTimeout(function(){run_left(3, left);}, 200); break; case 3: document.getElementById('j').style.backgroundPosition = "-120px -50px"; timer = setTimeout(function(){run_left(4, left);}, 200); break; case 4: document.getElementById('j').style.backgroundPosition = "-80px -50px"; timer = setTimeout(function(){run_left(1, left);}, 200); break; } } else { document.getElementById('j').style.backgroundPosition = "0px -50px"; }}
跳跃动画
最后,我们需要创建一个跳跃函数。我们需要向这个函数传递两个参数,一个将跟踪当前div元素是否在向上或向下移动,另一个将跟踪div元素的当前top属性。通过这两个参数,我们将决定div下一步该怎样移动,并且知道移动多少(为了模拟重力加速度,div跳跃离弧线顶部更近的时候,我们将移动div较短的距离)
function jump (up, top) {//我们改变J为跳跃精灵document.getElementById('j').style.backgroundPosition = '-160px 0px';//这里,我们需要决定精灵向下还是向上if (up && (document.getElementById('j').offsetTop > 20)) {//假如它正在向上移动,并且它距离stage顶部的距离大于20pxtop = top - (top * .1); //这给了我们的跳跃一个轻微的弧线,而不是像跑一样一直保持一个速度document.getElementById('j').style.top = top + 'px'; //改变位置timer = setTimeout(function() {jump(up, top);}, 60); //再次执行函数} else if (up) {//假如它正在向上移动,但是它已经接近stage的顶部,并且需要向下up = false; //改变up变量,因此下一次循环时它就会向下移动timer = setTimeout(function() { jump(up, top); }, 60);} else if (!up && (document.getElementById('j').offsetTop < 115)) {//假如它在向下移动,但是离地面不止5px,它将继续向下移动top = top + (top * .1);document.getElementById('j').style.top = top + 'px';timer = setTimeout(function() {jump(up, top);}, 60);} else {//假如它在向下移动,并且离地面在5px内document.getElementById('j').style.top = '120px'; //将它放置在地面上document.getElementById('j').style.backgroundPosition = '0px 0px'; //置为站立的精灵//当它在这个点静止站立时我们将不在循环执行函数}}
现在分别绑定四个函数到四个按钮上,我们就得到了跑跳动画的原型!假如你喜欢的话,你可以在这张页面查看完整的代码,并且有完整的注释,这里可以下载我所用的精灵表
总结
尽管现在我们有了可工作的动画原型,但是你可能注意到这里有一些bug。当你点击多个按钮时,脚本会尝试执行多个动画,或者,当精灵向下跳跃的时候,你再次点击跳跃按钮,J将会一直向下,永不停止。同时,正如我之前提到的,我们的脚本中有全局变量,这意味着,可能很难将这些代码添加到现有页面中而不和其他JavaScript脚本引起冲突(这也是为什么我没有尝试在本博客页面运行代码的原因)。在下一篇文章中,我们将清除所有上述bug,并且谈论封装的概念,以及在现实世界中,为了编写高质量的代码,封装为什么是如此的重要
0 0
- JavaScript动画工作原理之(二)
- JavaScript动画工作原理之(一)
- JavaScript动画工作原理之(三)
- JavaScript动画工作原理之(完结篇)
- Unity3D动画制作之二(javascript)
- JavaScript原理(二)
- Android 属性动画浅谈(二)基础运用及工作原理
- JavaScript之this的工作原理
- (二)dubbo工作原理
- EJB 工作原理之二:类一览
- RAM,SRAM,SDRAM工作原理 之二
- android动画之从源码角度分析动画原理(二)
- 动画(四)属性动画的工作原理
- 动画(六)属性动画的工作原理
- View的工作原理(二)之 View的工作流程
- Android动画之属性动画(二)
- css3 transition原理(动画系列二)
- android 动画原理二
- Hadoop2.3+Hive0.12集群部署
- iOS XML JSON SQLite CoreData 数据持久化
- iOS开发之极光推送相关问题(library not found for -lPushSDK)
- Java对下面XML文档解析要求封装为两个类
- HI3520 GPIO驱动
- JavaScript动画工作原理之(二)
- GridView数据格式化
- 《Thinking In Algorithm》10.树的三种遍历(递归与非递归实现)
- 最牛B的Linux Shell命令
- Objective C内存管理进阶(一):实践准则
- Spring使用OpenSessionInViewFilter解决Hibernate的lazy延时加载问题
- cocos2d-x admob jni
- Objective C内存管理进阶(二):理解autorelease
- Objective C内存管理进阶(三): 调试内存泄露