Javascript动画效果的实现(一)

大家在写Javascript脚本代码交互效果的时候,很多时候可能会用到动画效果,比如幻灯片,下拉菜单,层的展开关闭,元素的显示与隐藏以及有时候让人非常讨厌的浮动广告(它会在你的屏幕上飘来飘去让人着实讨厌)。当然在进行这些处理的时候你也可以不用加入动画效果,但是如果是适当的加入动画效果无疑会让你的界面变得更加吸引人。本篇文章简单的说明一下动画的原理,重点说的是Javascript中动画的实现,本篇文章需要你对Javascript有一定的理解,当然要是对Flash有所了解更好。

先简单的说一下动画的原理,本部分内容与最终实现没有紧密联系,有相关了解或不感兴趣请略过。动画是利用人眼的视觉残留特性而达成的一种视觉效果,即人眼看到的影像会有短暂时间的残留,这个时间约为1/24秒,当一段连续变化的影像在较短时间内变化时就会给人以流畅的感觉。根据1/24秒这个数据我们可以推断出,当连续变化的影像为每秒24次的速度就能给人流畅的感觉。所以电影的帧频为24帧,而电视一般采用的是25帧和30帧两种制式。

有过Flash开发经验的开发人员可能对“帧”这个概念比较熟悉,我们在讲多少帧的时候指的就是每秒钟画面切换的次数。在Javascript中没有“帧”这个概念,但是Javascript也提供了相应的方法让我们能够实现类似的效果,setTimeout()和setInterval()这两个方法。这两个方法简单的说就是延迟一段时间后执行相应的代码,区别在于setTimeout()是只执行一次而setInterval()是不断的重复执行直到你显式的取消它。利用这两个方法我们就能模拟出类似于Flash中的onEnterFrame()效果,如果你对Flash不熟悉没关系,这个地方本来就与Flash的关系不大,提及Flash只不过是为了有一个更好的理解罢了。简单的举个例子,我们新建一个页面并添加一个div(例1):

  1. <div id="mydiv" style="position:absolute;width:50px;height:50px;background:#f00;left:0px;">
  2. </div>

然后我们给它加一段Javascript代码:

  1. var obj=document.getElementById('mydiv');
  2. function changeStyle(){
  3. oobj.style.left=parseInt(obj.style.left)+20+'px'
  4. }
  5. setInterval(changeStyle,1000);

将代码拷到body当中打开页面或者点击查看Demo,你会发现有个红色的正方形每隔1秒往右移动一下。你可能会说:切,我咋一点都没看出来有动画效果?

确实现在我们实现的是每隔一秒种让方框往右移动一下,看起来一点动画流畅的感觉都没有。这主要是因为我们的帧频太低,或者说我们的间隔时间太长,如果我们将间隔时间改为40毫秒,也就是帧频为25的时候,再来看一下效果。怎么样,是不是看起来流畅多了?这个时间间隔越小理论上来说动画就会显得越流畅,当然,我们不能只看理论,如果你把间隔时间设置的太小的话浏览器有可能会卡掉,反而不好了,所以,保持帧频大于24,时间间隔小于40毫秒应该就能满足我们平常的使用要求了。

这个例子中我们实现了一个最简单的一维线性运动的动画,当然这个变化还可以简单的扩展到二维,从位置的变化扩展到大小的变化,颜色的变化等等。不知道你对用Javascript实现动画效果是否有了一个初步的了解?

我们通过最粗糙的手段实现了一个最简单的动画例子,下面我们在具体的分析整理一下我们的这个简单了示例。

现在我们让这个红色的方框动起来了,可是它不能一直不停的在动啊,你会发现一会儿你就找不到它了,它可能已经跑到很远的地方去了…我们在做动画效果的时候,有可能会碰到要一直不停的走啊走啊走到很远也不停下的情况,但是除了失恋的时候我们很少这么做不是吗?(即使你真的这么做,千万别忘了在过马路的时候看红绿灯,不然危险)

在大多数时候我们碰到的情况都需要有个临界值,当我们的变化超过这个临界值的时候就要停下。这个临界值一般有两种判断情况:一种是结束位置,即我们变化的值达到了临界值,这时候就要终止动画;二就是持续时间,即我们的变化时间达到了我们设定的阈值,这时候也要终止动画。

第一种情况我们先举个例子,还拿刚才我们那个例子来说,我们要让div向右移动100个像素,当我们的div的left大于100px的时候我们就要终止动画。将代码简单的修改如下(例2):

  1. var obj=document.getElementById('mydiv');
  2. function changeStyle(){
  3. oobj.style.left=parseInt(obj.style.left)+20+'px'
  4. oif(parseInt(obj.style.left)>=100)
  5. oclearInterval(timer);
  6. }
  7. var timer=setInterval(changeStyle,40);

如果你测试了你就会发现,当红色的方块往右移动了100个像素的时候它就停在那了。这种情况一般适用于渐进变化,只控制结束位置,而持续时间不容易判断的情况下。例如我们常用到的“标准指数渐进”,即每次向终点靠近的位移大小总是距离的1/2。还是开始的那个例子,我们要求让红色的方块以“标准指数渐进”的方式向右移动100个像素。简单的写一下代码(例3):

  1. var obj=document.getElementById('mydiv');
  2. function changeStyle(){
  3. oobj.style.left=parseInt(obj.style.left)+100-parseInt(obj.style.left))/2+'px';
  4. oif(100-parseInt(obj.style.left)<1)
  5. oclearInterval(timer);
  6. }
  7. var timer=setInterval(changeStyle,40);

将改好的代码运行,你会看到现在红色方块运动已经不是开始时候的匀速运动了,而是由开始的非常快变得越来越慢,最终停在我们设定好的位置处。

第二种情况,动画持续的时间,就是当我们的动画运行时间等于我们设定的时间的时候就终止动画。在前面几个例子中我们使用了简单的累加来实现对象位置的改变,现在我们就要把这个获取对象位置的方法抽象一下,让它的适用范围更广。

首先还是拿最简单的匀速直线运动来举例。

我们都知道路程,速度与时间之间的方程式:s=vt,我们想获取的不是路程而是当前的位置,那么根据公式我们修改一下得到:p=t*c/d+b(t为当前时间,c为总改变值,d为持续时间,b为初始值)。注意,我们这里讲的当前时间t和持续时间d并不是通常意义上的时间,而是指的变化是的帧数,因为我们在处理的时候时间并不是无限细分的,而是以某一个时间片段为基础的,比如我们上面设置的是40ms,这里的时间t和d代表的就是帧数,是以整数来表示的,它们的粒度是整数1,当我们的时间t变化了1的时候相当于时间过去了40ms,也就是1帧的时间,如果我们的持续时间d为40,也就是说明我们的动画要持续40帧,时间是40×40ms。这里的40ms并不是固定不变的你可以根据自己的需求改变。现在我们时间有了,随着时间改变,位置的变化也有了。下面就要利用我们刚才分析得到的公式进行编码了。同样还是开始时候的那个例子(例4):

  1. var obj=document.getElementById('mydiv');
  2. //下面几个参数分别表示动画从第一帧开始,持续30帧,也就是1.2s,移动的距离为100px,开始的位置为0px
  3. var t=0,c=100,d=30,b=0;
  4. //var date1 = new Date(); //时间标识1
  5. function changeStyle(){
  6. obj.style.left=t*c/d+b+'px';
  7. if(t < d){
  8. setTimeout(changeStyle,10);
  9. t++;
  10. }else{
  11. //var date2 = new Date(); //时间标识2
  12. //alert(date2 - date1); //时间差
  13. }
  14. }
  15. changeStyle();

这里的代码实现的效果与例2基本相同,但是采用的实现手段却是有所不同。首先明显的是这里用的是setTimeout()方法结合递归的手法实现了与setInterval()方法相似的效果,我个人感觉这种实现方法更利于控制,结合时间t我们还可以实现快速向前或快速向后的效果;其次,例2中我们规定了每次移动的距离为20px,其移动速度为20px/40ms,而这里的移动的速度是根据我们设定的变化距离以及动画持续的时间自动计算出来的,所以在例4中我们能够准确的控制时间的长度。这也是利用这种实现手法的优点。而且你可以利用不同的公式实现不同的动画效果,比如有加速度的运动,正弦运动,圆周运动等等。关于缓动的知识我会在下一篇文章中介绍。

这里我们通过四个简单的例子简单的分析了Javascript中动画的实现,不知道能不能给你一点小小的启示呢?有什么错误或者不当的地方欢迎批评指正,欢迎交流!

转载请注明原文出处《Javascript动画效果的实现(一)》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。

18条回复 发表于 “Javascript动画效果的实现(一)”上

  1. […] 留言 Javascript动画效果的实现(一) […]

  2. kingback says:

    我是个新手,看了这篇文章之后茅塞顿开,不过你能不能讲一下在规定时间内完成规定变化的值的动画怎么实现呢?现在的库,框架都有,但不知怎么实现的。谢谢!

    [回复]

    DK 说:

    @kingback, 首先是看你的变化值多少,然后根据你动画执行的规定时间和每次动画的间隔时间算出要执行多少次,然后按照已有的公式进行动画就好.

    [回复]

    kingback 说:

    @DK,谢谢,还有个问题想问下你,就是按照你上面的公式,我给设10ms重复一次,总时间是500ms,但是实际执行的时间却大概有5000ms,这个问题是怎么解决的?

    [回复]

    DK 说:

    @kingback, 你是参考了例4吧,开始的时候写的不是很严谨,我又修改了一下。执行时间肯定会比你设计的时间长一点,因为要考虑程序的执行时间,所以实际的执行时间肯定比动画时间稍长。500ms大约要600-800毫秒但是不会5000毫秒那么多。

  3. says:

    请问这个在firefox里运行怎么看不见移动呢?

    [回复]

    DK 说:

    @花, 不会啊,我这边都动呢,你看一下你的Fx是否报错

    [回复]

    ben 说:

    @DK, 楼主那个多了一个括号, 所有没动,删掉一个就好了。

    [回复]

  4. 不知 says:

    把楼主的办法用jquery写了一下,也很好用呢:
    一:

    o$(function()
    o{
    ofunction rel(){
    ovar obj = $(“#red”);
    ovar tmp = obj.css(“left”);
    ovar lef = parseInt(tmp)+500-(parseInt(tmp)+500)/2+’px’;
    o//tmp = left;
    o$(“#red”).css(“left”,lef);
    oif(500-parseInt(tmp)<=1)
    oclearInterval(timer);
    o}
    osetInterval(rel,100);
    o})

    二:

    o$(function()
    o{
    ovar t=0,b=0,c=500,d=30;
    o
    ofunction rel()
    o{
    ovar obj = t*c/d+b+’px’;
    o//var obj = c*(t/=d)*t+b+’px’;
    o$(“#red”).css(“left”,obj);
    o
    oif(t<=d){setTimeout(rel,40); t++;}
    o}
    orel();
    o})

    [回复]

    DK 说:

    @不知, jQuery自己带了动画模块啊亲~

    [回复]

    不知 说:

    @DK, 那时候想做缓动效果呀><////////(捂脸)

    [回复]

  5. uling says:

    第二,三段代码不起效果怎么回事

    [回复]

  6. 乌拉拉 says:

    楼主 你的例子3的判断语句if(100-parseInt(obj.style.left) 150) 为什么到了150时候还是不停啊?

    [回复]

    DK 说:

    @乌拉拉, 你写的我没看明白,另外例子我都测试过,应该没什么问题

    [回复]

  7. […] 文章来源:http://www.dklogs.net/?p=267 […]

  8. […] Javascript动画效果的实现(一) […]

  9. […] 这里我们通过四个简单的例子简单的分析了Javascript中动画的实现,不知道能不能给你一点小小的启示呢?有什么错误或者不当的地方欢迎批评指正,欢迎交流!查看原文请点击 […]

我要评论