核心要点
requestAnimationFrame
是一个辅助函数,用于编写与浏览器绘制周期同步的动画,从而实现更流畅、更省CPU的动画效果。它受所有现代浏览器支持,并且可以与旧版浏览器兼容。requestAnimationFrame
创建的 animate
函数可以设计为接受一系列函数作为参数,这些函数依次调用,从而实现动画序列。该函数可以跟踪动画进度并计算动画结束时间。requestAnimationFrame
比 setTimeout
或 setInterval
更高效地创建动画,因为它与浏览器的刷新率同步工作。它可以与任何 JavaScript 框架或库一起使用,并可以处理各种动画,包括 CSS 属性、canvas 动画、SVG 动画以及基于滚动或用户交互的动画。DOM 元素动画涉及每隔几毫秒修改一次 CSS 样式以产生运动的错觉。这意味着将回调函数传递给 setTimeout
,并在该回调函数中修改节点的样式对象。然后再次调用 setTimeout
来排队下一个动画帧。 requestAnimationFrame
这个新的辅助函数应运而生,用于编写动画。它最初出现在 Firefox 4 中,并逐渐被所有浏览器(包括 IE 10)采用。幸运的是,使其与旧版浏览器兼容很容易。
<code class="language-javascript">window.requestAnimationFrame(callbackFunction);</code>
与 setTimeout
(在指定时间延迟后运行)不同,requestAnimationFrame
在浏览器下次绘制屏幕时运行回调函数。这使您可以与浏览器的绘制周期同步,这样就不会绘制得太频繁或不够频繁,这意味着您的动画将非常流畅,而且不会对 CPU 造成过大的负担。
浏览器兼容性处理
目前每个浏览器都有 requestAnimationFrame
的前缀版本,因此让我们检测支持哪个版本并对其进行引用:
<code class="language-javascript">var _requestAnimationFrame = function(win, t) { return win["webkitR" + t] || win["r" + t] || win["mozR" + t] || win["msR" + t] || function(fn) { setTimeout(fn, 60) } }(window, "equestAnimationFrame");</code>
请注意,我们如何使用方括号表示法访问窗口对象上的属性。我们使用方括号表示法是因为我们正在使用字符串连接动态构建属性名称。如果浏览器不支持它,我们将回退到一个常规函数,该函数在 60 毫秒后调用 setTimeout
以实现类似的效果。
动画函数构建
现在让我们构建一个简单的函数,该函数将重复调用我们的 _requestAnimationFrame
来模拟动画。为了实现动画,我们需要一个作为入口点的外部函数和一个将被重复调用的内部函数(称为步进函数)。
<code class="language-javascript">window.requestAnimationFrame(callbackFunction);</code>
在每次调用步进函数时,我们需要跟踪动画的进度以知道何时结束。我们将计算动画应该结束的时间,并根据每个周期剩余的时间来计算进度。
<code class="language-javascript">var _requestAnimationFrame = function(win, t) { return win["webkitR" + t] || win["r" + t] || win["mozR" + t] || win["msR" + t] || function(fn) { setTimeout(fn, 60) } }(window, "equestAnimationFrame");</code>
请注意,我们正在使用 new Date()
来获取以毫秒为单位的当前时间。加号将日期对象强制转换为数值数据类型。rate
变量是一个介于 0 和 1 之间的数字,表示动画的进度率。
动画函数完善
现在我们需要考虑函数的输入和输出。让我们允许函数接受函数和持续时间作为参数。
<code class="language-javascript">function animate() { var step = function() { _requestAnimationFrame(step); } step(); }</code>
我们可以像这样调用此函数:
<code class="language-javascript">function animate() { var duration = 1000 * 3, // 3 秒 end = +new Date() + duration; var step = function() { var current = +new Date(), remaining = end - current; if (remaining < 60) { // 如果剩余时间少于 60 毫秒,则在此结束动画 return; } else { var rate = 1 - remaining / duration; // 执行一些动画操作 } _requestAnimationFrame(step); } step(); }</code>
在 run
函数中,我将放置一些代码,这些代码将节点的宽度从“100px”动画到“300px”。
<code class="language-javascript">function animate(item) { var duration = 1000 * item.time, end = +new Date() + duration; var step = function() { var current = +new Date(), remaining = end - current; if (remaining < 60) { item.run(1); // 1 = 进度为 100% return; } else { var rate = 1 - remaining / duration; item.run(rate); } _requestAnimationFrame(step); } step(); }</code>
改进动画函数
它工作正常,但我真正想要的是能够输入一个函数数组,这些函数依次调用。这样,在第一个动画结束后,第二个动画就会开始。我们将把数组视为一个堆栈,一次弹出一个个项目。让我们更改输入:
<code class="language-javascript">animate({ time: 3, // 以秒为单位的时间 run: function(rate) { /* 使用 rate 执行某些操作 */ } });</code>
当第一次运行动画时,item
为 null,remaining
小于 60 毫秒,因此我们从数组中弹出第一个项目并开始执行它。在动画的最后一帧中,remaining
也小于 60,因此我们完成当前动画,从数组中弹出下一个项目并开始动画下一个项目。还要注意,我已经将 rate
值通过缓动公式。现在从 0 到 1 的值以三次方的比例增长,使其看起来不那么僵硬。要调用动画函数,我们执行以下操作:
<code class="language-javascript">animate({ time: 3, run: function(rate) { document.getElementById("box").style .width = (rate * (300 - 100) + 100) + "px"; } });</code>
请注意,盒子的宽度首先扩展,占用 2 秒,然后高度扩展,再占用 2 秒。
代码优化
让我们稍微清理一下我们的代码。请注意,我们多次调用 getElementById
,这不太好。让我们缓存它,并在缓存开始和结束值的同时缓存它。
<code class="language-javascript">function animate(list) { var item, duration, end = 0; var step = function() { var current = +new Date(), remaining = end - current; if (remaining < 60) { if (item) item.run(1); // 1 = 进度为 100% item = list.shift(); // 获取下一个项目 if (item) { duration = item.time * 1000; end = current + duration; item.run(0); // 0 = 进度为 0% } else { return; } } else { var rate = remaining / duration; rate = 1 - Math.pow(rate, 3); // 缓动公式 item.run(rate); } _requestAnimationFrame(step); }; step(); }</code>
请注意,我们不需要修改主函数,因为 run
函数一直是自包含对象的一部分,并且可以通过 this
变量访问对象的所有属性。现在,无论何时运行步进函数,我们都缓存了所有变量。就是这样。一个简单的动画辅助函数,它利用 requestAnimationFrame
并为旧版浏览器提供回退。
(脚本演示部分省略,因为无法在此环境中运行JavaScript代码。)
(关于使用requestAnimationFrame进行简单动画的常见问题解答部分也省略,因为内容与原文高度重复,只需保留核心要点即可完成伪原创。)
以上是使用requestAnimationFrame的简单动画的详细内容。更多信息请关注PHP中文网其他相关文章!