文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript动画抖动的原因是什么与怎么解决

2023-07-02 09:26

关注

这篇文章主要讲解了“JavaScript动画抖动的原因是什么与怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript动画抖动的原因是什么与怎么解决”吧!

使用定时器实现动画出现卡顿的原因

这个问题跟浏览器的事件循环机制有关,JavaScript 引擎在解析执行我们的代码的时候,遇到定时器,会调用浏览器 API,让定时器去进行倒计时,此时并不阻塞同步代码的执行,当定时器倒计时完毕,定时器回调会被放入宏任务队列等待执行。

在这个过程中问题就来了,如果说同步代码的执行需要50ms,而定时器设置的定时只有20ms,那么由于事件循环的机制,还是要等待同步任务执行完整之后再来执行微任务队列中的定时器回调,而这中间,又相隔了30ms,在这30ms的过程中,定时器的回调一直处于 pendding 的状态。如果定时器中是动画相关的操作,那也需要在预期的时间上多等待50ms。

画了张图,希望能帮助大家理解(如果不能帮助大家理解,那么请忽略这张图……)

JavaScript动画抖动的原因是什么与怎么解决

这里有两个点,一个是显示器的刷新频率,另一个是定时器的时间间隔。

一般显示器刷新频率都是60Hz,这基本上意味着每秒需要重绘60次。大多数浏览器都会限制重绘的频率,使其不超过显示器的刷新频率。因为超过刷新频率,用户也感知不到,白白浪费性能。

因此,实现平滑动画最佳的重绘间隔为1000ms/60,大约17毫秒。以这个速度重绘,可以实现最平滑的动画效果。因为这已经是浏览器的重绘频率的极限了。

知道何时绘制下一帧,是创造平滑动画的关键。直到几年前,都没有确切保证让浏览器在何时把下一帧绘制出来的方法。随着 <canvas>HTML5 游戏的兴起,开发者发现 setIntervalsetTimeout 的不精确是个大问题,而浏览器自身的计时器也存在着精度不足毫秒的问题。

以下是几个浏览器计时器的精度情况:

以 Chrome 为例,它的计时器精度为 4ms,这意味着 0~4 之间的任何值最终要么是 0,要么是4;不可能是别的值。因此,即使将浏览器的时间间隔设置为最优,也免不了只能得到相近似的结果。

对于 JavaScript 来说,它不知道浏览器会在何时发生重绘。因此,我们通过定时器做动画的时候,在定时器中控制动画的代码已经执行完成的情况下,动画效果并不一定会立马生效,因为此时浏览器可能还处在等待下一次重绘的过程中,当下一次重绘完成,动画效果才能在浏览器窗口中显示出来。

而由于浏览器计时器时间差的问题,会导致定时器的计时并不一定是我们设置的 17 ms,而是在多个时间点内反复横跳,也因此才出现使用定时器做动画的时候动画抖动的问题,在复杂的动画中,这种问题尤为明显。

在这样的环境下,今天的主角 requestAnimationFrame 应运而生!

requestAnimationFrame 的前世今生

Mozilla 的 Robert O'Callahan 一直在思考这个问题,并且提出了一个独特的解决方案。他指出,浏览器知道 CSS 过渡和动画应该什么时候开始,并且能够计算出正确的时间间隔,到时间就去刷新用户界面。

但是对于 JavaScript 而言,浏览器并不知道动画什么时候开始。他给出的方案是创造一个名为 MozRequestAnimationFrame 的新方法,以此来通知浏览器某些 JavaScript 代码要执行动画了。这样浏览器就可以在运行某些代码后进行适当的优化。

目前,所有的浏览器都支持这个方法不带前缀的版本,也就是现在用到的 requestAnimationFrame

requestAnimationFrame VS setInterval

这里就不再过多的介绍 requestAnimationFrame 的详细用法了,它的用法并不复杂。

与定时器不同的是,requestAnimationFrame 只会在被调用的时候执行一次动画,而不会连续执行。如果想做连续的动画,则可以通过递归来实现对 requestAnimationFrame 的连续调用。

接下来通过一个 demo 来对比一下使用 requestAnimationFramesetInterval 两者做出来的动画效果之间的差异。

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta http-equiv="X-UA-Compatible" content="IE=edge" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>Document</title>  </head>  <style>    .square1,    .square2 {      position: absolute;      width: 100px;      height: 100px;    }    .square1 {      top: 40px;      background: red;    }    .square2 {      top: 150px;      background: blue;    }  </style>  <body>    <div class="container">      <div class="square1"></div>      <div class="square2"></div>      <button class="btn">开始!</button>    </div>    <script>      const square1El = document.querySelector('.square1')      const square2El = document.querySelector('.square2')      // 定时器版      function squareMove() {        let timer = null        square1El.style.left = '0px'        timer = setInterval(() => {          const squareLeft = parseInt(square1El.style.left)          if (squareLeft >= 500) return clearInterval(timer)          square1El.style.left = squareLeft + 1 + 'px'        }, 17)      }      // requestAnimationFrame 版      function squareMove2() {        let timer = null        square2El.style.left = '0px'        function updateAnimation() {          const squareLeft = parseInt(square2El.style.left)          if (squareLeft >= 500) return cancelAnimationFrame(timer)          square2El.style.left = squareLeft + 1 + 'px'          window.requestAnimationFrame(updateAnimation)        }        window.requestAnimationFrame(updateAnimation)      }      document.querySelector('.btn').addEventListener('click', () => {        squareMove()        squareMove2()      })    </script>  </body></html>

在页面中画了两个正方形,当点击按钮的时候方块开始运动,红色方块是使用 setInterval 实现的动画,蓝色方块使用的是 requestAnimationFrame

在生成gif的时候视频被压缩了,但是还是能看到红色的方块在开始运动的时候有明显的抖动,而蓝色的方块则比较丝滑。

实际上,requestAnimationFrame 的回调函数可以接收一个参数,这个参数是一个 DOMHightResTimeStamp 的实例(比如:performance.now()的返回值),用来表示下一次重绘的时间。这一点非常重要,requestAnimationFrame 实际上是把重绘任务安排在了未来的一个已知的时间点上,而且通过这个参数来告诉开发者。

类似于 setInterval 的清除方法 clearIntervalrequestAnimationFrame 也有对应的取消重绘的方法 cancelAnimationFrame,用法也跟 clearInterval 非常类似,在每次调用 requestAnimationFrame 的时候,都会返回一个id,cancelAnimationFrame 就是通过这个id去取消对应的 requestAnimationFrame

感谢各位的阅读,以上就是“JavaScript动画抖动的原因是什么与怎么解决”的内容了,经过本文的学习后,相信大家对JavaScript动画抖动的原因是什么与怎么解决这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯