window.requestAnimationFrame强大的前端动画神器
今天介绍一个功能强大的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强大的前端动画神器相关推荐
- 浏览器动画window.requestAnimationFrame
新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,欢迎批评指正 再看别人现实粒子效果的时候会有以下码代: 1 window.requestAnimationFrame || (window.req ...
- 由奥迪车灯想到的前端动画
最近对汽车比较感兴趣,平时也多留意看了一些身边的车,发现奥迪部分车型的转向灯很有特色,有一个从左到右的动画效果,视觉效果很赞,这撩起了我的好奇心,怎么用代码在网页上模拟实现呢? 先来看看我们需要实现的 ...
- 前端动画优化及性能检测
前端动画优化及性能检测工具使用 前端使用动画可以分为两类: css 动画 js 动画 我们提倡能够使用 css 完成的动画尽量使用 css ( 即使用 animation 和 keyframes ). ...
- html中canvas动画游戏显示,【Fes】基于canvas的前端动画/游戏入门(一)
前言 本系列虽说是基础教程,但这是相对动画/游戏领域来说,在前端领域算是中级教程了,不适合前端小白或萌新.阅读前请确保自己对前端三大件(JavaScript+CSS+HTML)的基础已经十分熟悉,而且 ...
- html5中动画总结,前端动画总结
导言 之前一直没有总结一下关于前端学习到的动画该怎么实现,这篇文章帮我自己总结一下. 基本概念 在学习动画之前, 我们得先了解一下关于动画的基本概念. 帧: 帧是动画的最小单位,一帧也就是一张图片,连 ...
- 【第二届青训营-寒假前端场】- 「前端动画实现」笔记
动画的基本原理 动画是什么 动画发展史 计算机动画 前端动画分类 css动画 animation-name animation-duration animation-timing-function a ...
- 【前端动画】实现动画的6种方式
引言 动画基本上分类两类:补间动画和帧动画. 补间动画:补齐中间的动画.由浏览器帮助补齐中间的状态,开发者只需要定义开始和结束的状态. 帧动画:除了开始与结束状态,开发者还可以定义中间关键帧的状态,可 ...
- window.requestAnimationFrame
今天小猪在看一个html5的demo时一直在找他的动画是怎么实现的,按照我的理解就应该是调用setInterval来循环调用动画函数来实现.但是在Demo中就是找不到这个函数.干着急的小猪只好一步一步 ...
- 网页性能管理详解:浅谈chrome-Timeline及window.requestAnimationFrame()方法
你遇到过性能很差的网页吗? 这种网页响应非常缓慢,占用大量的CPU和内存,浏览起来常常有卡顿,页面的动画效果也不流畅. 你会有什么反应?我猜想,大多数用户会关闭这个页面,改为访问其他网站.作为一个开发 ...
最新文章
- 问题.beego路由设置及请求参数传递
- MySQL事物的概念
- mysql没加引号导致全表扫描_mysql隐蔽的索引规则导致数据全表扫描
- Kali Linux渗透基础知识整理(四):维持访问
- python测试udp端口_zabbix上使用外部检查的方式监测公网tcp/udp端口开放情况
- Mac 终端所有命令失效
- 雷锋实验室:Evernote的中国门徒
- 互联网靠什么赚钱和发展趋势
- 下了一个游戏说计算机丢失,冰封64位win10系统下启动游戏提示计算机丢失XINPUT1-3.dll怎么办...
- mysql二级软件_全国计算机等级考试二级MySQL练习软件
- 未来已来:数字化时代的商业模式创新-读书笔记
- Excel数据可视化竟可以如此惊艳!数据可视化大屏制作
- 数字孪生与元宇宙:数字化科技的双向融合之路
- SAP 后台表查询方法及消息报错定位方法
- 网络系统管理Debian模块||初始化环境、网络地址规划
- 04 cefsharp谷歌浏览器多开页面的实现
- 服务器上的文件夹设置ftp,设置ftp服务器上传文件夹
- 刷题、OJ 1337: 运动员分组
- 信息系统建设服务和能力评估和计算机信息系统集成CS资质的区别
- SOC与SIP小芯片两种IP互联技术
热门文章
- 100道mysql的面试题问答
- 【Python语言概述】语言简介、语言规范、安装扩展库、库的导入与使用
- figma有哪些插件比较好用,分享5款必备figma插件
- 亚马逊鲲鹏系统评论批量点赞或举报的一款神器
- 抖音快速涨粉之抖音用户数据分析
- DELPHI2010的IDHTTP控件与DELPHI7的IDHTTP控件有什么不一样呀?GET事件DELPHI7正常,DELPHI2010就有问题!
- 关于“考勤助手”体系架构风格的选取
- 通过 $ git commit --amend 修改 commit 的 message
- xctf ics-05 wp
- box2d升级至 Box2DFlash 2.1a