RequestAnimationFrame更好的实现Javascript动画

一直以来,JavaScript的动画都是通过定时器和间隔来实现的。虽然使用CSS transitions 和 animations使Web开发实现动画更加方便,但多年来以JavaScript为基础来实现动画却很少有所改变。直到Firefox 4的发布,才带来了第一种对JavaScript动画的改善的方法。但要充分认识改善,这有利于帮助我们了解web动画是如何演变改进的。

定时器Timer

用于创建动画的第一个模式是使用链式setTimeout()调用。在Netscape 3′s hayday的很长一段时期,开发者都记得一种在网络上随处可见的固定式最新行情状态栏,通常它类似于这样:

  1. (function(){
  2. var msg = "新的广告",
  3. len = 25,
  4. pos = 0,
  5. padding = msg.replace(/./g, " ").substr(0,len),
  6. finalMsg = padding + msg;
  7. function updateText(){
  8. var curMsg = finalMsg.substr(pos++, len);
  9. window.status = curMsg;
  10. if (pos == finalMsg.length){ pos = 0; }
  11. setTimeout(updateText, 100);
  12. }
  13. setTimeout(updateText, 100);
  14. })();

如果你想在浏览器中测试这段代码,你可以新建一个

标签用来模拟window.status,例如:newsticker example

这种让人烦恼的web模式,后来遭到对window.status禁用的抵抗,但随着Explorer 4和Netscape 4的发布,浏览器第一次给开发者更多对页面元素的控制权限,这种技术再次出现。这样就出现了使用javascript动态改变元素大小、位置、颜色等的一种全新动画模式。例如,下面就是一个将div宽度变化成100%的动画(类似于进度条):



  1. (function(){
  2. function updateProgress(){
  3. var div = document.getElementByIdx_x("status");
  4. div.style.width = (parseInt(div.style.width, 10) + 5) + "%";
  5. if (div.style.width != "100%"){ setTimeout(updateProgress, 100); }
  6. }
  7. setTimeout(updateProgress, 100);
  8. })();

尽管动画在页面上的地方不同,但基本原理却是一样的:做出改变,用setTimeout()间隔使页面更新,然后setTimeout又执行下一次变化,这个过程反复执行,直到动画完成(见进度条动画),早期的状态栏动画是相同的技术,只是动画不一样而已。


间隔动画Intervals


随着成功将动画引入web,新的探索开始了。一个动画已经无法满足了,现在需要多个动画。首次尝试为每个动画创建多个动画循环,在早期的浏览器中使用setTimeout()来创建多个动画是有点复杂的,所以开发商开始使用setInterval()一创建单一的动画循环,来管理页面上所有的动画,一个使用wetInterval()的基本动画像这样:



  1. (function(){
  2. function updateAnimations(){
  3. updateText();
  4. updateProgress();
  5. }
  6. setInterval(updateAnimations, 100);
  7. })();

创建一个小动画库,updateAnimations()方法将每一个动画(同时看到一个新闻股票和进度条在一起运行)循环执行并进行适当的改变。如果没有动画需要更新,该方法可以退出而不做任何事情,甚至停止动画循环,直到有更多的动画更新做好准备。


动画问题比较棘手的问题是延迟应该为多少。间隔一方面必须足够短,从而使不同的动画都能流畅的进行,别一方面还要足够长,使得浏览器可以完成渲染。大多数浏览器的刷新频率为60HZ,即每秒60次刷新,大多数浏览器的刷新频率都不会比这个更频繁,因为他们知道,最终用户是得不到更好的体验的。


鉴于此,为流畅动画的最佳时间间隔为1000毫秒/ 60,约17ms。在这个频率你会看到流畅的动画,那是因为你最大的接近了浏览器能达到的频率。跟以前的动画相比,你会发现17ms间隔的动画更加平滑,也更快(因为动画更新更频繁,没有做其他任何修改的情况下),多个动画可能需要节流,以免17ms的动画完成得太快。


问题


即使使用setInterval()为基础的动画循环比多套使用setTimeout()的动画循环高效,这里还是存在问题。无论是setInterval()还是setTimeout()都无法达到精确,这个延迟即你指定的第二个参数仅仅表示何时代码会添加到浏览器的可能被执行的UI线程队列中。如果队列中有其他工作在此之前,那代码将会等到他完成才会执行。简而言之,毫秒级的延迟不是表示何时代码会执行,而是表示何时代码会添加进队列。如果UI线程处于繁忙状态或在处理用户动作,那么代码将不会被马上执行。


平滑动画的关键是理解下一帧何时被执行,直到现在都没有一个方法来保证下一帧将会在浏览器中被绘制。随着的日益流行和新的基于浏览器的游戏的出现,开发商对setInterval()和setTimeout()的不精准越来越感到失望。


浏览器的计时器分辨率加剧了这个问题,计时器对毫秒不精准,这里有一些常见的计时器分辨率:

Internet Explorer 8
and earlier 15.625ms

Internet Explorer 9
and later 4ms.

Firefox and Safari
~10ms.

Chrome has a timer
4ms.

IE在版本9之前的的分辨率为15.625,所以0~15之间的任意值可能是0或15,但没有分别。IE9的计时器分辨率改进为4ms,但涉及到动画时也是不具体的,chrome的计时器分辨率为4ms,firefox 和 safari的为10ms。因此即使你把间隔设定为最佳的显示效果,你也仅仅是得到这个近似值。

mozRequestAnimationFrame

Mozilla 的 Robert O’Callahan 在思考这个问题,并想出了一个独特的方案。他指出CSS transitions 和 animations的优势在于浏览器知道哪些动画将会发生,所以得到正确的间隔来刷新UI。而javascript动画,浏览器不知道动画正在发生。他的解决方案是创建一个mozRequestAnimationFrame()方法来告诉浏览器哪些javascript代码正在执行,这使得浏览在执行一些代码后得到优化。


mozRequestAnimationFrame()方法接受一个参数,是一个屏幕重绘前被调用的函数。这个函数用来对生成下合适的dom样式的改变,这些改变用在下一次重绘中。你可以像调用setTimeout()一样的方式链式调用mozRequestAnimationFrame(),例如:



  1. function updateProgress(){
  2. var div = document.getElementByIdx_x("status");
  3. div.style.width = (parseInt(div.style.width, 10) + 5) + "%";
  4. if (div.style.left != "100%"){
  5. mozRequestAnimationFrame(updateProgress);
  6. }
  7. }
  8. mozRequestAnimationFrame(updateProgress);

由于mozRequestAnimationFrame()只运行给定的函数一次,你需要在下一次UI动画的时候再次调用它。你也需要相同的方法来管理何时停止调用。很酷,是非常流畅的动画增强的实例。


因此,mozRequestAnimationFrame()解决了浏览器不知道Javascript动画正在执行和不知道多少才是合适的间隔的问题,但对于不知道何时你的代码才被真正执行,也是由这个方案来解决的。


传递给mozRequestAnimationFrame()的函数实际是一个下一次重绘何时发生的的时间码(以毫秒为单位自1970年1月1日计算)。这是很重要的一点:mozRequestAnimationFrame()实际上列表出将要重绘的点并可以告诉你他们所处的时间。这样你就能够决定怎样更好的来调整你的动画。


为了得到上次重绘过去的时间,你可以查询mozAnimationStartTime,其中包含了过去重绘的时间代码。减去传递回调时的这个值可以计算出下一次重绘到屏幕时所用的时间。使用这些值的典型模式如下:



  1. function draw(timestamp){
  2. //calculate difference since last repaint
  3. var diff = timestamp - startTime;
  4. //use diff to determine correct next step
  5. //reset startTime to this repaint
  6. startTime = timestamp;
  7. //draw again
  8. mozRequestAnimationFrame(draw);
  9. }
  10. var startTime = mozAnimationStartTime;
  11. mozRequestAnimationFrame(draw);

关键是第一次不是通过callback调用时,mozAnimationStartTime是到mozRequestAnimationFrame()经过的时间。如果是在回调函数中,mozAnimationStartTime是通过参数传递进来的时间代码平均值。

webkitRequestAnimationFrame

在很多人热忠于chrome时,随即创建了webkitRequestAnimationFrame()方法。这个版本与firefox的版本在两方面有着细微的差别。一方面,它不通过回调函数传递时间代码,你将无法知道下次重绘何时发生,另一方面,它添加了第二个可选参数来确定哪一个DOM元素发生改变。因此,如果你知道重绘发生在页面哪个部分的元素内,你可以限制重绘发生的区域。


应该不会感到惊讶,有没有相应的mozAnimationStartTime,因为如果没有下一个重绘的时间信息不是很有益。有,只是webkitCancelAnimationFrame()取消了之前计划的重绘。


如果你不需要精确的时间差异,你可以用下面的方式来创建一个用于Firefox4和chrome10+的动画:



  1. function draw(timestamp){
  2. //calculate difference since last repaint
  3. var drawStart = (timestamp || Date.now()),
  4. diff = drawStart - startTime;
  5. //use diff to determine correct next step
  6. //reset startTime to this repaint
  7. startTime = drawStart;
  8. //draw again
  9. requestAnimationFrame(draw);
  10. }
  11. var requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame,
  12. startTime = window.mozAnimationStartTime || Date.now();
  13. requestAnimationFrame(draw);

这种模式使用可用的方法来创建以花费多少时间为理念的循环动画。Firefox使用时间代码信息是有用的,而Chrome默认为欠精准的时间对象。当用这种模式的时候,时间的差异给你一种多少时间过去了的想法,但不会告诉你Chrome的下一次重绘出现在何时。不过这比只有多少时间过去了的模糊概念要好些。


总结


mozRequestAnimationFrame()方法的介绍为推动Javascript 动画及web的历史发展有着非常重要的作用。如前所述,JavaScript动画的态几乎和JavaScript的初期一样。随着浏览器逐渐推出CSS transitions 和 animations,很高兴看到基于JavaScript的动画的关注,因为这些在基于的游戏领域将变得更重要和更与CUP联系紧密。知道Javascript何时尝试动画,允许浏览器做更多的优化处理,包括在tab处于后台或移动设备电量过低时停止进程。


该requestAnimationFrame()API现在正由W3C起草一个新议案,并正由Mozilla和Google努力使之成为Web大舞台的一部分。很高兴能看到这两大集团这么迅速的兼容(可能不完全)实现。


RequestAnimFrame使用

对于一个侦中对DOM的所有操作,只进行一次Layout和Paint。

如果发生动画的元素被隐藏了,那么就不再去Paint。
  1. window.requestAnimFrame = (function(){
  2. return  window.requestAnimationFrame       ||
  3. window.webkitRequestAnimationFrame ||
  4. window.mozRequestAnimationFrame    ||
  5. window.oRequestAnimationFrame      ||
  6. window.msRequestAnimationFrame     ||
  7. function( callback ){
  8. window.setTimeout(callback, 1000/60);
  9. };
  10. })();
  11. //调用
  12. function animationLoop(elem){
  13. requestAnimFrame(animationLoop);
  14. //logic
  15. }
Or

  1. window.requestAnimFrame = (function(w, r) {
  2. w[‘r‘+r] = w[‘r‘+r] || w[‘webkitR‘+r] || w[‘mozR‘+r] || w[‘msR‘+r] || w[‘oR‘+r] || function(c){ w.setTimeout(c, 1000 / 60); };
  3. return w[‘r‘+r];
  4. })(window, ‘equestAnimationFrame‘);

RequestAnimationFrame更好的实现Javascript动画

时间: 2024-10-11 11:05:23

RequestAnimationFrame更好的实现Javascript动画的相关文章

关于使用 requestAnimationFrame 制作 JavaScript 动画

昨天在 MDN 复习关于对象的知识,最后有个做吃球小游戏的练习,翻了翻以前做的这个练习的文件内容,复习了一波.在代码的最后,我发现了一个完全不知道的方法:requestAnimationFrame. 在 MDN 找到相关页面,是这么介绍的:使用该方法需要传入一个回调函数作为擦书,在使用该方法后,浏览器会在下次重绘前调用回调函数.如果需要在下次重绘前继续更新下一个动画,则需要在回调函数内部调用 window.requestAnimationFrame(). 该方法会返回一个 long 整数,该整数

JavaScript动画知多少?

今天,小学生以自己浅薄的见地,在前辈大能的基础上写这篇文章,希望给大家打开一扇窥探JavaScript(以下简称JS)动画的窗户. JS如何制造出动画效果? 结合浏览器提供的 setInterval 或 setTimeout API,高频改变DOM元素的一些属性,即可创造一个肉眼可见的动画效果.一个看起来非常流畅的JS动画除了需要良好的变换算法外,与其执行宿主也是非不开的.程序写得再好,如果浏览器过于老旧,电脑CPU性能低下,也会出现卡顿,甚至卡死. 执行一个动画函数对于浏览器来说是个苦差,设置

Javascript动画效果(四)

Javascript动画效果(四) 前面我们自己写了一个小小的关于js动画的插件,下面我们来使用之前的框架来完成我们想要的动画效果.我们经常在淘宝网中看到,鼠标经过某一图片时,该图片有从上滚出而又从下滚入的效果,那么那种效果是如何实现的呢? 首先我们我们完成该效果的html和css代码,代码如下: html部分代码: <div id="move"> <a href="#"><i><img src="images/1

javascript动画效果之缓冲动画(修改版)

在编写多块同时触发运动的时候,发现一个BUG, timer = setInterval(show, 30);本来show是一个自定义函数,当设为timer = setInterval(show(one,two), 30);时,发现show里面的参数one和two无法被导入,所以需要做以下代码改进和优化 原版的html和css代码在这里javascript动画效果之缓冲动画 js代码如下 1 <script> 2 function $(id) { 3 return typeof id === &

JavaScript网站设计实践(三)设计有特色的主页,给主页链接添加JavaScript动画脚本

原文:JavaScript网站设计实践(三)设计有特色的主页,给主页链接添加JavaScript动画脚本 一.主页一般都会比较有特色,现在在网站设计(二)实现的基础上,来给主页添加一点动画效果. 1.这里实现的动画效果是:当鼠标悬停在其中某个超链接时,会显示出属于该页面的背景缩略图,让用户知道这个链接的页面大概内容是什么. 效果图: 2.实现这个效果的思路 (1)把主页的几个链接的背景图片缩放到150px*150px,拼成一张750*150的图片,并保存为slideshow.png,存放到ima

javascript动画系列第一篇——模拟拖拽

× 目录 [1]原理介绍 [2]代码实现 [3]代码优化[4]拖拽冲突[5]IE兼容 前面的话 从本文开始,介绍javascript动画系列.javascript本身是具有原生拖放功能的,但是由于兼容性问题,以及功能实现的方式,用的不是很广泛.javascript动画广泛使用的还是模拟拖拽.本文将详细介绍该内容 原理介绍 模拟拖拽最终效果和在桌面上移动文件夹的效果类似 鼠标按下时,拖拽开始.鼠标移动时,被拖拽元素跟着鼠标一起移动.鼠标抬起时,拖拽结束 所以,拖拽的重点是确定被拖拽元素是如何移动的

慕课网javascript动画特效 多透明度 源代码

慕课网javascript动画特效 多透明度 源代码<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>多物体运动</title> <style> div{ width:200px; height:100px; background:yellow; margin-bottom: 20px; filte

JavaScript动画工作原理之(完结篇)

原作者:Steven Riche 发布时间:2014年2月18日 原文链接:http://code.tutsplus.com/tutorials/javascript-animation-that-works-part-4-of-4--net-35263 翻译:子毅 --------- 将JavaScript进行到底 碎碎两句 折腾了一个多月,杂七杂八的事情加在一起,简直糟透了.博客停了大概有一个月了,从今天起一切都是新的,做好自己就OK了 ---------------------------

Win10系列:JavaScript 动画1

在应用程序中使用动画会使应用显得更加生动,进而给用户带来良好的视觉效果.例如,当用户将某个项添加到列表时,新项不会立即出现在列表中,而是采用动画形式到达相应位置,并且列表中的其他项也采用动画形式移动到新位置,以便为添加的项腾出空间.这样的动画效果能够让用户更清晰地看到变化过程,而不是只能看到变化之后的结果. 应用程序的动画效果可以使用Windows动画库来创建,开发人员无需自己编写动画效果代码即可获得Metro风格的动画外观和效果.另外还可以通过设置页面元素的style对象的相关属性来创建.下面