JavaScript 动画实现原理与缓动算法详解

作者:小圆点

时间:2026-10-29

浏览:376

本文深入解析JavaScript动画的实现机制,对比setTimeout与requestAnimationFrame的性能差异,详解如何封装兼容API。重点介绍基于完成度p的Tween算法及多种缓动函数(如ease-in/out),通过数学公式实现匀加速、正弦等复杂运动效果,并探讨CSS3与JS动画的适用场景。

文章目录

JavaScript动画概述

本文旨在基于现有知识体系,为读者提供了解JavaScript(以下简称JS)动画的入门视角。

JS如何制造出动画效果?

利用浏览器提供的 setInterval 或 setTimeout API,通过高频修改DOM元素的属性,即可生成肉眼可见的动画。一个流畅的JS动画不仅依赖于优秀的变换算法,还高度依赖执行环境。即使代码逻辑完美,若浏览器版本过旧或CPU性能不足,仍可能出现卡顿甚至死机现象。

requestAnimationFrame与性能优化

执行动画函数对浏览器而言是一项繁重任务。如何设定单帧时长以兼顾流畅度与性能?浏览器不会在每次DOM操作后立即渲染,而是将周期内的所有DOM操作合并,统一进行一次渲染。该周期约为16.7ms,不同浏览器存在几毫秒差异。因此,将SetTimeout的第二个参数设为 1000/60 较为合理。然而,SetTimeout的运行机制导致其时间并不精确,可能存在显著延迟。是否存在一种API,不关心浏览器具体渲染频率,仅在渲染时执行一帧动画?答案是 requestAnimationFrame,它使函数随浏览器渲染周期执行,且时机可靠。需注意,该方法仅在现代浏览器及IE10及以上版本中受支持。

现在可以封装一个简单的 requestAnimationFrame,后续示例将使用此封装。


window.requestAnimFrame = (function(){
 return window.requestAnimationFrame    ||
     window.webkitRequestAnimationFrame ||
     window.mozRequestAnimationFrame  ||
     function( callback ){
      window.setTimeout(callback, 1000 / 60);
     };
})();

更详细的封装实现可参考张鑫旭博客:张鑫旭:requestAnimFrame。接下来继续探讨。

动画函数编写与核心变量

动画函数的编写

借助 requestAnimationFrame,接下来需考虑如何编写动画函数。通常需提供毫秒级的 during 参数,限定动画必须在指定时间内完成。以下以小球从页面左侧100px处匀速运动至800px处为例,编写动画函数:戳我查看DEMO。


      var ele = document.getElementById("block");
      var start = Date.now();//获取动画开始的时间。
      var during = 1000; //此动画要在1秒内执行完。
      var p=0;//动画完成度 从0-1;
      requestAnimationFrame(function f(){
        if(p>=1){ ele.style.left="800px";}//如果发现动画已经执行完,将元素置到终点。
        else{
          p=(Date.now()-start)/during;
          ele.style.left=100+700*p+"px"; //从100px开始,匀速向右移动,共移动700px;
          requestAnimationFrame(f);
        }
      })

上述函数中的关键变量 p(percentage,即动画完成度)根据当前时间计算得出,从动画开始起在0到1之间匀速渐变。当 p 为1时,表示动画执行完毕。在此函数中,将 p 乘以运动长度700,得到0至700的匀速变化值,加上起始位置100,即可模拟小球从100px处匀速移动至800px处。

若将上述红色标出的运动方程改为“ele.style.left=100+700*p*p+"px"”呢?由于 p 以二次方递增,小球向右移动的速度将逐渐加快。稍作修改即可实现匀加速运动的小球效果。

接下来,我们将针对 p 进行进一步处理。

Tween算法与缓动效果实现

Tween算法及缓动效果

以下列举常用缓动算法,通过修改上述匀速运动函数的运动方程,即可实现丰富的动画效果: 1.2次方缓动: p*p 2.3次方缓动: p*p*p 3.4次方缓动: p*p*p*p 4.5次方缓动: p*p*p*p*p 5.正弦曲线缓动: Math.sin(p*Math.PI/2) 6.指数曲线缓动: Math.pow(2,10*(p-1)) 7.圆形曲线缓动: Math.sqrt(1-(p-1)*(p-1)) 8.超范围三次方: p*p*(2.70158*p-1.70158)

验证一下,例如实现小球向右运动且带有向左蓄力效果的动画,只需将第一个demo的运动方程改为“ele.style.left=100+700*p*p*(2.70158*p-1.70158)+"px"”即可,效果见:戳我查看DEMO。

实际上,每种缓动算法均可演化为三种缓动方式:ease-in(先慢后快)、ease-out(先快后慢)以及 ease-in-out(先慢后快再慢)。

以2次方缓动为例,其本身即为匀加速过程,故 ease-in 对应 p*p。其 ease-out 形式为 -(p*(p-2))。关于缓动方式的详细说明,可参考阳光的博客:JavaScript动画、运动算法详细解释与分析。

接下来展示关于缓动整合的DEMO,戳我吧。

JS动画的应用场景与总结

JS动画可以做什么?

除上述缓动效果外,利用常见数学公式还可实现周期性运动,如小球匀速圆周运动、匀速简谐振动等。感兴趣者可猛戳DEMO。

那么JS动画能做什么?这需要发挥个人想象力。上述DEMO大多操控单一属性(如left),使DOM元素发生位移。事实上,在运动方程中,元素的任何style属性均可被渐变。例如,设置DOM元素的opacity从0到1进行2次方缓动,即可实现类似jQuery fadeOut函数的效果;让DOM元素高度从无到有,则对应简单的jQuery slideDown函数。更值得注意的是,动画过程中可同时操作多项属性,这为动画带来了无限可能性,使事情变得更加有趣:DEMO:一个从小到大变化的球。

再试想一下,若使用CSS3属性(如box-shadow、transform)制作效果,必将更加绚丽。

总结

前文提及使用CSS3属性,若浏览器支持CSS3,则完全无需使用JS。CSS3自带的Animation动画属性可简单快捷地实现酷炫效果,并支持GPU加速。其不足在于仅现代浏览器支持。JS实现动画的优势在于兼容低版本浏览器,但效果通常一般(仅限普通意义上的动画,不含canvas等)。

动画仅是JS操作DOM魅力的一角,数学与计算机的碰撞往往能迸发出耀眼火花。继续学习JS吧,这是一门神奇的语言,同时了解一些数学知识,往往能为解决问题提供捷径。

以上所述就是本文的全部内容了,希望大家能够喜欢。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章