JavaScript动画工作原理之(二)

原作者: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元素向右移动。


向右跑的动画

我们肯定不想通过不停地点击不同的按钮来循环精灵,因此,我们需要创建一些函数来自动地完成这些事情。

我们想要执行函数来完成这些事情:
  1. 将div元素慢慢地向右移动
  2. 移动到下一帧动画
  3. 暂停一秒钟(保留“视觉暂留”幻觉)
  4. 再次循环函数

幸运的是,这里有一种简单的方式来循环执行函数。在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" οnclick="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" οnclick="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顶部的距离大于20px
			top = 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值