requestAnimationFrame 解析

文章目录

    • 什么是 requestAnimationFrame
    • 为什么 setInterval 实现动画会有一些抖动感
    • 使用 requestAnimationFrame
    • requestAnimationFrame 对比 setInterval

本文将给大家介绍一个使用 js 实现动画的利器,requestAnimationFrame,我们一般情况下,在 js 实现一个动画,一般是使用 setInterval 实现,不过通常使用这个方法实现动画的时候,细看能够看见一些抖动感

什么是 requestAnimationFrame

  1. requestAnimationFrame 是浏览器提供的一个方法,我们可以通过 requestAnimationFrame 来告诉浏览器我们需要执行一个动画,并且这个动画触发的时机是浏览器在下次重绘之前调用指定的回调函数更新动画
  2. 该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
  3. 当然也有一些需要注意的地方:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 requestAnimationFrame()。因为 requestAnimationFrame() 是一次性的
  4. 在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配
  5. 而如果想要取消 requestAnimationFrame 执行,可以通过 cancelAnimationFrame 方法

为什么 setInterval 实现动画会有一些抖动感

  1. 这个原因可能有不少,但是主要的就是 浏览器渲染帧的不同步

  2. 为什么会不同步呢?主要原因是浏览器无法确定定时器的回调函数的执行时机

  3. 我们知道 setInterval 是一个异步的任务,只有当同步任务执行完成之后才会执行异步任务,假设我们设置的 setInterval 是 20ms 执行一次,那么如果同步代码执行的时间是 10ms,也就表示实际上 setInterval 执行的时候已经隔了 30ms 的时间了

  4. 这是一点,那我们在猜想一下,按照 60 帧率来计算的话,应该是 16.7ms 执行一次,就可以达到一个比较细腻的动画,那么如果我们将 setInterval 的时间设置为 16.7ms 可以解决这个问题吗,也是不行的,还是这个异步的问题,加上等待同步代码执行时间,这个时间一定是不精确的,何况本身浏览器的计时存在细小的误差

  5. 为了方便理解,我们还是回到这个开始设置 20ms 的时间,按照预期 60 帧渲染的话,那么不算其他干扰的情况,那么执行的时间线如下:

    在这里插入图片描述

  6. 可以看到,第一帧没有渲染,20ms的时候执行了 setInterval 代码了,但是就需要等待下一次的执行时机,也就第二帧,而如果假设是 10ms 的话,那么第一帧可以找到,但是在执行第二帧的时候,其实 setInterval 执行了三次,也就是说在第二帧本该执行第二次 setInterval 所设置的动画数值,变了第三次 setInterval 设置的动画数值,出现了一次跳跃,那自然看起来也会存在抖动感

  7. 正是因为由于这种不同步,就导致了一次执行渲染一针或者多帧,或者当前帧没有渲染,下一次执行的时候数值跳跃等等,从而导致动画存在抖动感

  8. 这个更加具体一点大家可以参考手绘动画翻页这种效果,如果翻到某一页突然停一下,或者某两页或者几页黏在一起,被当做一页翻过去的时候,就可以名显的感觉到这个不连贯的感觉

  9. 或者有人说,我可以强制触发reflow来触发啊,那还是这个问题,屏幕的刷新率是固定的,你重绘时机过早也不会被展示出来,也不会被用户感知到,等于无效的操作,那么还是无法解决这个问题

使用 requestAnimationFrame

  1. 相信在经过上面 setInterval 实现动画为什么存在抖动感的解析,就知道应该要怎么解决,解决方案就是在每一帧渲染之前,就处理好每一帧需要表现的动画样式和一些数据,然后就可以在每一帧时机渲染的时候处理正确的动画效果,这样就可以保证我们的动画拥有相对细腻的效果了

  2. 而 requestAnimationFrame 就可以帮助我们完成这个效果

  3. requestAnimationFrame 是只会执行一次的,所以我们如果需要利用 requestAnimationFrame 实现动画效果的话,往往需要递归,也就是如下的形式,如下:

    function run(){requestAnimationFrame(run)
    }
    requestAnimationFrame(run)
    

requestAnimationFrame 对比 setInterval

  1. 实现代码:

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>requestAnimationFrame</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}.sign {width: 500px;height: 220px;border-right: 2px solid #000;position: relative;margin-left: 20px;}.box {width: 100px;height: 100px;background-color: salmon;position: absolute;left: 0;top: 0;}.box1 {top: 120px;background-color: skyblue;word-wrap: break-word;}.btns {margin-left: 20px;margin-top: 30px;}</style>
    </head><body><div class="sign"><div class="box">setInterval</div><div class="box box1">requestAnimationFrame</div></div><div class="btns"><button class="s-btn">setInterval</button><button class="r-btn">requestAnimationFrame</button><button class="btn">一起执行</button></div><script>const box = document.querySelector('.box');const box1 = document.querySelector('.box.box1');const sBtn = document.querySelector('.s-btn')const rBtn = document.querySelector('.r-btn')const btn = document.querySelector('.btn')let boxLeft = 0let boxId = nulllet box1Left = 0let box1Id = nullsBtn.addEventListener('click', () => {boxId = setInterval(() => {boxLeft++box.style.left = boxLeft + 'px'if (boxLeft >= 400) {clearInterval(boxId)boxId = null}}, 1000 / 60)})function run() {box1Left++box1.style.left = box1Left + 'px'if (box1Left >= 400) {cancelAnimationFrame(box1Id)box1Id = nullreturn}requestAnimationFrame(run)}rBtn.addEventListener('click', () => {box1Id = requestAnimationFrame(run)})btn.addEventListener('click', () => {sBtn.click()rBtn.click()})</script></body></html>
    
  2. 屏幕刷新率为 60hz 时效果

    在这里插入图片描述

  3. 屏幕刷新率为 165hz 时

    在这里插入图片描述

  4. 由于这种 gif 的原因,可能实际感受不如在屏幕上看的明显,但是可以大致感知出来 setInterval 的那种抖动感,而且 setInterval 只能设置固定的时间,是无法契合当前的屏幕的刷新率的

  5. 这里在补充一点性能上的区别,这个就先了解一下他们在后台的运行机制:

    1. requestAnimationFrame() 运行在后台标签页或者隐藏的 <iframe> 里时,requestAnimationFrame()` 会被暂停调用以提升性能和电池寿命

    2. 而 setInterval 在后台也是不会停止调用的会继续在浏览器的内存中调用,消耗性能,比如最开始编写轮播图的时候,如果页面隔一段时间切换回来之后,会导致轮播图切换的非常快,就是因为没有停止执行,而切换回来之后,把失去的值都补上,这个也是可以验证的,比如我们把 setInterval 执行改为 2000ms,每次增加 50px,来看一下效果,如图

      在这里插入图片描述

    3. 通过这个是可以看到切换回来的时候,一下子就跳跃了一大段距离,就表示实际是没有停止执行的,一直改变着移动的数值,切换胡来的时候才会进行了跳跃式的移动

  6. 当然动画最优选还是 css3,不过有些动画总会需要 js 的介入嘛

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/312425.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2013年AMC8数学竞赛中英文真题典型考题、考点分析和答案解析

“一元复始&#xff0c;万象更新。行而不辍&#xff0c;未来可期。” 努力学习和奋斗的时光总是过得飞快&#xff0c;不知不觉&#xff0c;2024年已经悄然而至&#xff0c;今天是2024年1月1日&#xff0c;六分成长祝所有的读者朋友和孩子们新年快乐&#xff01;学习进步&#…

【模拟电路】基础理论与实际应用

一、毫安时和毫瓦时 二、开关电路 三、继电器 四、半导体 五、二极管 六、三极管 七、三极管应用案例 一、毫安时和毫瓦时 毫安时&#xff08;mAh&#xff09;和毫瓦时&#xff08;mWh&#xff09;是两个不同的物理量&#xff0c;它们分别表示电量和能量的度量单位。下面的图…

LVS那点事

LVS 原理 IPVS LVS 的 IP 负载均衡技术是通过 IPVS 模块来实现的&#xff0c;IPVS 是 LVS 集群系统的核心软件&#xff0c;它的主要作用是&#xff1a;安装在 Director Server 上&#xff0c;同时在 Director Server 上虚拟出一个 IP 地址&#xff0c;用户必须通过这个虚拟的…

大华主动注册协议介绍

一、大华主动注册协议介绍 前面写了一篇文章&#xff0c;介绍一些设备通过大华主动注册协议接入到AS-V1000的文章&#xff0c;很多问我关于大华主动注册协议的相关知识。 由于大华主动注册协议是一种私有协议&#xff0c;通常不对外公开详细的协议规范和技术细节。因此…

[Angular] 笔记 25:指令

组件指令 (chatgpt 回答) 在 Angular 中&#xff0c;组件本身可以被视为指令&#xff0c;这种指令被称为组件指令。组件是 Angular 应用的构建块之一&#xff0c;它封装了一段具有特定功能和特性的用户界面&#xff0c;并且可以在应用中重复使用。 组件指令具有以下特征&…

学习SpringCloud微服务

SpringCloud 微服务单体框架微服务框架SpringCloud微服务拆分微服务差分原则拆分商品服务拆分购物车服务拆分用户服务拆分交易服务拆分支付服务服务调用RestTemplate远程调用 微服务拆分总结 服务治理注册中心Nacos注册中心服务注册服务发现 OpenFeign实现远程调用快速入门引入…

TiDB SQL调优案例TiFlash

背景 早上收到某系统的告警tidb节点挂掉无法访问&#xff0c;情况十万火急。登录中控机查了一下display信息&#xff0c;4个TiDB、Prometheus、Grafana全挂了&#xff0c;某台机器hang死无法连接&#xff0c;经过快速重启后集群恢复&#xff0c;经排查后是昨天上线的某个SQL导…

python安装MongoDB与运算符优先级

python安装MongoDB MongoDB 是目前最流行的 NoSQL 数据库之一&#xff0c;使用的数据类型 BSON&#xff08;类似 JSON&#xff09;。 PyMongo Python 要连接 MongoDB 需要 MongoDB 驱动&#xff0c;这里我们使用 PyMongo 驱动来连接。 pip 安装 pip 是一个通用的 Python 包…

HTTP小记2

目录 HTTP/1.1优化 QUIC协议 路由器 RTT&#xff08;Round-Trip Time&#xff09; 计算机网络体系结构 体系结构各层在整个过程中的作用 HTTP/1.1优化 1.通过缓存技术来避免/减少发送HTTP请求 2.减少HTTP请求的次数 将原本由客户端处理的重定向请求&#xff0c;交给代理…

面向对象基础-类与对象-封装

1、类与对象 1.1 概念 类&#xff1a;类是一个抽象的概念&#xff0c;用于描述一类对象的特点。 对象&#xff1a;根据类的概念所创造的实体。 【思考】一个对象可以没有对应的类嘛&#xff1f; 不可以&#xff0c;因为必须现有类才能创建对象。 1.2 类的内容 类中最基础的内容…

git(安装,常用命令,分支操作,gitee,IDEA集成git,IDEA集成gitee,IDEA集成github,远程仓库操作)

文章目录 1. Git概述1.1 何为版本控制1.2 为什么需要版本控制1.3 版本控制工具1.4 Git简史1.5 Git工作机制1.6 Git和代码托管中心 2. Git安装3. Git常用命令3.1 设置用户签名3.1.1 说明3.1.2 语法3.1.3 案例实操 3.2 初始化本地库3.2.1 基本语法3.2.2 案例实操3.2.3 结果查看 3…

LDO线性稳压器与开关电源的原理

线性稳压器LDO典型代表&#xff1a;LM7805 ,AMS1117&#xff0c;还有一下性能比较好的LDO&#xff1a; 开关稳压器典型代表&#xff1a;LM2596&#xff0c;MP1584,TPS5430&#xff0c;MP2315S LDO靠发热分散能量&#xff0c;纹波较小一般在30mv以下&#xff1b;DCDC通过开关开断…