今天介绍一个功能强大的api—window.requestAnimationFrame,它既可以实现如丝般顺滑的动画,又能充当性能优化的利器,还能代替setTimeout,setInterval等定时器。自从学会了requestAnimationFrame,我已经不会拼写setInterval啦…

背景

动画的实现与浏览器显示

在讲具体功能和api使用方法之前,我们先来大体聊一下api的背景和原理。

在各类影视节目横行的今天,大家应该都对电影或是动画的实现有一定了解,最开始的动画是工作人员,一张图一张图画出来的,然后通过快速的切换图片,使静态的画面“运动起来”,人们认为,人类的肉眼所能分辨的频率的极值约为50ms/次,也就是说,只要画面在50ms内快速切换,人类就几乎发现不了画面切换带来的顿挫感,观测到的画面是一种顺滑的流畅的图像。这也就是原始动画片的制作原理。

浏览器的画面,和动画片类似,也是浏览器按照一定频率一帧一帧绘制出来的,一般情况下浏览器的刷新率为16ms绘制一帧。也就是说,这个频率的绘制,会让人完全感受不到画面的闪烁,让图像真正的动起来。

浏览器绘制每一帧,都会按照以下过程进行:(省略一些不太相关的过程)
1、开始新的一帧率
2、处理输入事件
3、执行requestAnimationFrame
4、解析html
5、计算样式
6、更新图层树
7、发送帧

通过这个过程不难发现,每当浏览器开始绘制新的一帧画面的时候,都会去执行一下requestAnimationFrame,那如果我们讲动画相关的代码,通过requestAnimationFrame来执行,是不是就可以做到和浏览器本身画面一样顺滑了呢?

window.requestAnimationFrame

告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

举个例子
如果你希望得到一个向右侧移动的方块,那么你可以这样

function animationTest(){const div = document.createElement("div");div.style.width = '100px'div.style.height = '100px'div.style.position = 'absolute'div.style.top = '0px';div.style.left = '0px';div.style.backgroundColor = '#f00'div.style.zIndex = '999999'document.body.appendChild(div);let distance = 0;function move(){distance++console.log(distance) // 打印当前帧,方块移动的距离div.style.left = distance + 'px'requestAnimationFrame(move); // 通知浏览器开始绘制下一帧的时候,继续执行move函数}move();
}animationTest()

这段代码你可以打开浏览器,F12打开控制台,复制进去测试。 你应该可以看到一个红色的方块,在浏览器缓慢的向右侧移动。
同时相信你用不了多久,就会发现,这个动画怎么停下来呢?

window.cancelAnimationFrame

取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求.

想要通知浏览器,下一帧的时候,不要继续执行requestAnimationFrame的回调啦,那么你需要像使用setTimeout一样,存一下requestAnimationFrame返回的id,并传入到cancelAnimationFrame

比如这样

function animationTest(){let animationId;const button = document.createElement("button");button.innerHTML = "停止动画"button.style.position = 'absolute'button.style.top = '150px';button.style.left = '0px';button.style.zIndex = '999999'button.onclick = () => {if(!!animationId){window.cancelAnimationFrame(animationId);button.innerHTML = "开始动画"animationId = void 0;}else{move()button.innerHTML = "停止动画"}}const div = document.createElement("div");div.style.width = '100px'div.style.height = '100px'div.style.position = 'absolute'div.style.top = '0px';div.style.left = '0px';div.style.backgroundColor = '#f00'div.style.zIndex = '999999'document.body.appendChild(div);document.body.appendChild(button);let distance = 0;function move(){distance++console.log(distance) // 打印当前帧,方块移动的距离div.style.left = distance + 'px'animationId = requestAnimationFrame(move); // 通知浏览器开始绘制下一帧的时候,继续执行move函数}move();
}animationTest()

好啦,如果你有在控制台测试,你就能看到,多了一个控制动画的按钮,现在你可以自由的控制动画的开始和停止了

其他场景

刚才我们举了一个简单动画的的例子,那么我们在其他场景会不会用到这个api呢,当然是用的到的, 尤其是做音视频,或者对应用性能有一定要求的项目中,requestAnimationFrame方法基本上是绕不开的。
比如在移动端等端上设备模拟亮度,一般会有一个由亮到暗,或者由暗到亮的过渡,或者页面平滑滚动,再或者需要一个基于画面刷新率的观测器都可以用的到。这些和动画大同小异。但是这里再介绍一个非常常见的场景,鼠标事件

mousemove

我们知道,mousemove的触发频率很高,很多时候,我们不需要这么高的触发频率,常常为了优化性能,我们会写一个截流函数,来降低它触发的频率,如果你仅仅是为了让画面看起来更流畅,对频率没有特殊的需求。那么你可以直接使用requestAnimationFrame,这样mousemove的触发频率就会与画面刷新率保持一致,既保证了画面的流畅度,又降低了mousemove的触发次数,纯天然绿色截流,你值得拥有

function mousemoveTest(){document.addEventListener("mousemove", move);function move(){requestAnimationFrame(() => {// todo 高性能消耗的代码console.log("move 函数执行了")})}
}
mousemoveTest()

这一步很难看到效果,如果你在move函数中执行了一段,性能消费很高的代码,比如拖拽某些dom的时候,需要大量的计算,造成画面卡顿。这个时候,用这种方式,就能看到明显的效果了

总结

这里就不在举更多的例子了,简单总结一下,requestAnimationFrame(callback), 其中callback就是你想要执行的函数,将callback作为参数传递给requestAnimationFrame的时候,就表示你希望在浏览器绘制下一帧的时候,去调用这个函数。当你需要多次触发被requestAnimationFrame包裹callback的时候,在浏览器绘制一帧的的过程中,不论你触发多少次,它都仅会执行一次。正因为如此,所以可以当作一个16ms一次的截流函数来使用。也是因为如此,我们才可以在一个看似死循环的递归中使用它。

当然,requestAnimationFrame也是它存在的问题,比如他只会在当前页面激活时被触发,也就是说,不论你切了浏览器tab标签,还是切换到了其他的程序,只要当前的浏览器页面没有被激活,requestAnimationFrame是不会触发的。在一些有严格要求的动画中,会导致丢帧的情况。解决这种丢帧问题的方法,一般会采用监听visibilitychange事件,当用户离开当前页面时,我们记录离开的时间戳,当用户再次回到页面时,我们利用记录的时间戳计算用户离开页面的时间,并计算出在这段时间内丢失的动画状态,同时将动画状态进行补偿,这样就可以解决丢帧的问题了。

window.requestAnimationFrame强大的前端动画神器相关推荐

  1. 浏览器动画window.requestAnimationFrame

    新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,欢迎批评指正 再看别人现实粒子效果的时候会有以下码代: 1 window.requestAnimationFrame || (window.req ...

  2. 由奥迪车灯想到的前端动画

    最近对汽车比较感兴趣,平时也多留意看了一些身边的车,发现奥迪部分车型的转向灯很有特色,有一个从左到右的动画效果,视觉效果很赞,这撩起了我的好奇心,怎么用代码在网页上模拟实现呢? 先来看看我们需要实现的 ...

  3. 前端动画优化及性能检测

    前端动画优化及性能检测工具使用 前端使用动画可以分为两类: css 动画 js 动画 我们提倡能够使用 css 完成的动画尽量使用 css ( 即使用 animation 和 keyframes ). ...

  4. html中canvas动画游戏显示,【Fes】基于canvas的前端动画/游戏入门(一)

    前言 本系列虽说是基础教程,但这是相对动画/游戏领域来说,在前端领域算是中级教程了,不适合前端小白或萌新.阅读前请确保自己对前端三大件(JavaScript+CSS+HTML)的基础已经十分熟悉,而且 ...

  5. html5中动画总结,前端动画总结

    导言 之前一直没有总结一下关于前端学习到的动画该怎么实现,这篇文章帮我自己总结一下. 基本概念 在学习动画之前, 我们得先了解一下关于动画的基本概念. 帧: 帧是动画的最小单位,一帧也就是一张图片,连 ...

  6. 【第二届青训营-寒假前端场】- 「前端动画实现」笔记

    动画的基本原理 动画是什么 动画发展史 计算机动画 前端动画分类 css动画 animation-name animation-duration animation-timing-function a ...

  7. 【前端动画】实现动画的6种方式

    引言 动画基本上分类两类:补间动画和帧动画. 补间动画:补齐中间的动画.由浏览器帮助补齐中间的状态,开发者只需要定义开始和结束的状态. 帧动画:除了开始与结束状态,开发者还可以定义中间关键帧的状态,可 ...

  8. window.requestAnimationFrame

    今天小猪在看一个html5的demo时一直在找他的动画是怎么实现的,按照我的理解就应该是调用setInterval来循环调用动画函数来实现.但是在Demo中就是找不到这个函数.干着急的小猪只好一步一步 ...

  9. 网页性能管理详解:浅谈chrome-Timeline及window.requestAnimationFrame()方法

    你遇到过性能很差的网页吗? 这种网页响应非常缓慢,占用大量的CPU和内存,浏览起来常常有卡顿,页面的动画效果也不流畅. 你会有什么反应?我猜想,大多数用户会关闭这个页面,改为访问其他网站.作为一个开发 ...

最新文章

  1. 问题.beego路由设置及请求参数传递
  2. MySQL事物的概念
  3. mysql没加引号导致全表扫描_mysql隐蔽的索引规则导致数据全表扫描
  4. Kali Linux渗透基础知识整理(四):维持访问
  5. python测试udp端口_zabbix上使用外部检查的方式监测公网tcp/udp端口开放情况
  6. Mac 终端所有命令失效
  7. 雷锋实验室:Evernote的中国门徒
  8. 互联网靠什么赚钱和发展趋势
  9. 下了一个游戏说计算机丢失,冰封64位win10系统下启动游戏提示计算机丢失XINPUT1-3.dll怎么办...
  10. mysql二级软件_全国计算机等级考试二级MySQL练习软件
  11. 未来已来:数字化时代的商业模式创新-读书笔记
  12. Excel数据可视化竟可以如此惊艳!数据可视化大屏制作
  13. 数字孪生与元宇宙:数字化科技的双向融合之路
  14. SAP 后台表查询方法及消息报错定位方法
  15. 网络系统管理Debian模块||初始化环境、网络地址规划
  16. 04 cefsharp谷歌浏览器多开页面的实现
  17. 服务器上的文件夹设置ftp,设置ftp服务器上传文件夹
  18. 刷题、OJ 1337: 运动员分组
  19. 信息系统建设服务和能力评估和计算机信息系统集成CS资质的区别
  20. SOC与SIP小芯片两种IP互联技术

热门文章

  1. 100道mysql的面试题问答
  2. 【Python语言概述】语言简介、语言规范、安装扩展库、库的导入与使用
  3. figma有哪些插件比较好用,分享5款必备figma插件
  4. 亚马逊鲲鹏系统评论批量点赞或举报的一款神器
  5. 抖音快速涨粉之抖音用户数据分析
  6. DELPHI2010的IDHTTP控件与DELPHI7的IDHTTP控件有什么不一样呀?GET事件DELPHI7正常,DELPHI2010就有问题!
  7. 关于“考勤助手”体系架构风格的选取
  8. 通过 $ git commit --amend 修改 commit 的 message
  9. xctf ics-05 wp
  10. box2d升级至 Box2DFlash 2.1a