2020已经过去五分之四了,你确定还不来了解一下JS的rAF?
不会吧,不会吧,现在都2020年了不会还真人有人不知道JS的rAF吧???
rAF
简介
rAF是requestAnimationFrame的简称;
我们先从字面意思上理解requestAnimationFrame,「request - 请求」,「Animation - 动画」, 「Frame - 帧率;框架」,rAF难道是JS的动画框架???,结果显而易见并不是。但确实rAF和动画有关系
我们先来看一下MDN官网对的requestAnimationFrame解释:
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
浏览器兼容性
requestAnimationFrame兼容IE10及以上,这时候有人会有疑问,怎么才到IE10啊,但其实我们最常使用的CSS3 animation属性也是IE10之后才有的,在IE9之前想要实现动画基本使用的是setTimeout/setInterval实现
作用及用法
requestAnimationFrame简称rAF,它是浏览器全局对象window的一个方法。
相比于setTimeout的在固定时间后执行对应的动画函数,rAF用于指示浏览器在下一次重新绘制屏幕图像时, 执行其提供的回调函数。
这也是rAF的最大优势–它能够保证我们的动画函数的每一次调用都对应着一次屏幕重绘,从而避免setTimeout通过时间定义动画频率,与屏幕刷新频率不一致导致的丢帧。
详细用法
requestAnimationFrame语法如下:
window.requestAnimationFrame(callback)
「参数;callback」
下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame()开始去执行回调函数的时刻。
「返回值」一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
DOMHighResTimeStamp 指的是一个double类型,用于存储毫秒级的时间值。这种类型可以用来描述离散的时间点或者一段时间(两个离散时间点之间的时间差)。
performance.now()方法返回一个精确到毫秒的DOMHighResTimeStamp 。
它的实际常见用法类似于setTimeout,只是不需要设置时间间隔而已。如下:
const element = document.getElementById('some-element-you-want-to-animate');
let start;
function step(timestamp) {
// timestamp回调函数传入的`DOMHighResTimeStamp`参数,也就是存储毫秒级的时间值
if (start === undefined)
start = timestamp;
const elapsed = timestamp - start;
//这里使用`Math.min()`确保元素刚好停在200px的位置。
element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';
if (elapsed < 2000) { // 在两秒后停止动画
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
上述代码的作用在每一次屏幕显示图像的更新中,都将元素向左移动1px,停在200px位置上。
实际使用示例
「上才艺,E G M,E G M E G M E G M」
我们以在3000毫秒内移动1500px距离的动画为例
setTimeout的实现方式
以下代码通过setTimeout每10毫秒为间隔时间改变一次元素的位置以实现元素的动画效果, 当然,可以通过改变这个间隔时间来微调动画效果,可是你永远没有办法确定最优方案,因为它总会和刷新频率存在交叉。
<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">
</div>
<script type="text/javascript">
let divEle = document.getElementById("div");
const distance = 1500; // 需要移动的距离
const timeCount = 3000; // 需要使用的时间
const intervalTime = 10; // 设置间隔时间为10ms
let runCount = timeCount / intervalTime; // 相除得到运行次数
let moveValue = distance / runCount; // 每次运行移动的距离
function handler() {
let left = parseInt(divEle.style.left);
if(left >= distance) {
// 当距离左侧的距离超出需要移动的距离停止
return;
}
divEle.style.left = left + moveValue;
window.setTimeout(handler, intervalTime);
}
window.setTimeout(handler, intervalTime);
</script>
requestAnimationFrame的实现方式
「从setTimeout切换到 requestAnimationFrame很容易,因为它们都安排了一个回调。对于连续动画,在调用动画函数之后再次调用requestAnimationFrame。」
request 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成(这点很像虚拟DOM不是~),并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,这样就不会出现过度渲染的问题,保证了流畅的需求以及浏览器的完美渲染。
<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">
</div>
<script type="text/javascript">
let divEle = document.getElementById("div");
const distance = 1500; // 需要移动的距离
const timeCount = 3000; // 需要使用的时间
function handler( time ) {
// time为rAF返回的毫秒级时间单位,当time的大于timeCount的值则停止
// time理论上是从 1 开始到timeCount定义的3000,
if(time > timeCount) {
time = timeCount;
}
// 这句代码的作用是 time理论上是从 1 至 3000
// 当到达3000的时候,time * distance / timeCount得到的一定是distance的值1500
divEle.style.left = time * distance / timeCount;
window.requestAnimationFrame( handler ); // 循环调用,渲染完成会停止
}
window.requestAnimationFrame( handler );
</script>
requestAnimationFrame的优点
为什么不使用settimeout?
setTimeout通过设定一个时间间隔来不断的更新屏幕图像,从而完成动图。
它的优点是可控性高,可以进行编码式的动画效果实现。
setTimeout缺点:
「造成无用的函数运行开销:」
也就是过度绘制,同时因为更新图像的频率和屏幕的刷新重绘制步调不一致,会产生丢帧,在低性能的显示器动画看起来就会卡顿。
「当网页标签或浏览器置于后台不可见时,仍然会执行,造成资源浪费」
「API本身达不到毫秒级的精确:」
如果使用 setTimeout或者setInterval 那么需要我们制定时间 假设给予 (1000/60)理论上就可以完成60帧速率的动画。所以事实是浏览器可以“强制规定时间间隔的下限(clamping th timeout interval)”,一般浏览器所允许的时间再5-10毫秒,也就是说即使你给了某个小于10的数,可能也要等待10毫秒。
「浏览器不能完美执行:」
当动画使用10ms的settimeout绘制动画时,您将看到一个时序不匹配,如下所示。
我们的显示屏一般是「16.7ms(即60FPS)的显示频率」,上图的第一行代表大多数监视器上显示的「16.7ms显示频率」,上图的第二行代表「10ms的典型setTimeout」。由于在显示刷新间隔之前发生了另一个绘制请求,因此无法绘制每次的第三个绘制(红色箭头指示)。这种透支会导致动画断断续续,「因为每三帧都会丢失」。计时器分辨率的降低也会对电池寿命产生负面影响,并降低其他应用程序的性能。
如果使用requestAnimationFrame可以解决setTimeout的丢帧问题,因为它使应用程序时通知(且仅当)的浏览器需要更新页面显示,渲染时间由系统处理。因此,应用程序与浏览器绘画间隔完全一致,并且仅使用适当数量的资源。
requestAnimationFrame的好处
相比于setTimeout的在固定时间后执行对应的动画函数,requestAnimationFrame用于指示浏览器在下一次重新绘制屏幕图像时, 执行其提供的回调函数。
「使浏览器画面的重绘和回流与显示器的刷新频率同步」它能够保证我们的动画函数的每一次调用都对应着一次屏幕重绘,从而避免
setTimeout通过时间定义动画频率,与屏幕刷新频率不一致导致的丢帧。「节省系统资源,提高性能和视觉效果」在页面被置于后台或隐藏时,会自动的停止,不进行函数的执行,当页面激活时,会重新从上次停止的状态开始执行,因此在性能开销上也会相比
setTimeout小很多。
兼容问题
目前的时间点上,几乎所有的浏览器现行版本都支持了requestAnimationFrame函数。但在一部分浏览器上还需要加上兼容性前缀。
下面这是比较全面的方法用来使requestAnimation兼容各浏览器:
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz']; // 浏览器前缀
// 当window.requestAnimationFrame不存在时执行for循环,添加前缀
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
//当添加前缀后依旧不存在,则使用setTimeout替代
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());
然后,我们就可以以使用setTimeout的感觉使用requestAnimationFrame方法制作动画啦!
相关链接
个人网站:zhaohongcheng.com
GitHub:https://github.com/Tzlibai
结尾
如有疑问,可在下方留言,会第一时间进行回复
谢谢你愿意花时间阅读这篇文章,希望可以对你有所帮助!
我曾踏足山巅,也曾跌落谷底,两者都让我受益良多。
2020已经过去五分之四了,你确定还不来了解一下JS的rAF?的更多相关文章
- [TPYBoard - Micropython] 五分种学会用TPYBoard - GPS 制作短信群发机
转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 欢迎加入讨论群 64770604 一.什么是TPYBoard-GPS TPYBoard-GPS又称TPYBoa ...
- [TPYBoard - Micropython 之会python就能做硬件 9] 五分种学会用TPYBoard V102 制作避障小车(升级版)
转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 欢迎加入讨论群 64770604 感谢山东萝卜电子科技公司授权 一.实验器材 1.TPYboard V102板 ...
- LocalDateTime查找最近的五分钟点
/** * 最近的五分钟 * @param dateTime * @return */ public static LocalDateTime getNear5(LocalDateTime dateT ...
- 《find技巧》-“linux命令五分系列”之一
一天一个命令,做个记录, 我要成大神,哈哈哈 本原创文章属于<Linux大棚>博客. 博客地址为http://roclinux.cn. 文章作者为roc 希望您能通过捐款的方式支持Linu ...
- laravel报错:SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0' for key 'PRIMARY' (SQL: insert into `cart` (`uid`, `gid`, `gname`, `price`) values (3, 21, 夏季日系复古工装短袖衬衫男士印花潮流宽松五分
原因:要操作的数据表id没有设置自增,导致出现id为0的情况 解决方法:给该数据表的id字段设置自增
- C#语句2——循环语句(for循环与for循环嵌套)
循环:反复执行某段代码. 循环四要素:初始条件,循环条件,循环体,状态改变. for(初始条件;循环条件;状态改变) { 循环体 } break ——中断循环,跳出整个循环 continue——停止本 ...
- Java:过去、未来的互联网编程之王
Java对你而言是什么?一门你大学里学过的语言?一个IT行业的通用语言?你相信Java已经为下一次互联网爆炸做好了准备么?Java 一方面为嵌入式计算做了增强,而另一方面为实时应用做了精简,本文将介绍 ...
- 2014年全球SEO行业调查报告
前言: 1.该调查报告是MOZ每两年一度针对SEO行业的数据分析报告. 2.随着SEO的进化,该报告已不仅仅是SEO行业,今年的调查数据更多分析网络营销行业,可以称作"网络营销行业调查报告& ...
- HttpWebRequest提高效率之连接数,代理,自动跳转,gzip请求等设置问题
先设置4个: [csharp] webrequest.ServicePoint.Expect100Continue = false; //是否使用 Nagle 不使用 提高效率 webrequest. ...
随机推荐
- Oracle12C安装配置文档
Oracle12C安装配置文档 Oracle12C安装配置文档 准备软件: 开始安装: 打开从官网下载下来的两个压缩包,进行解压 打开解压好的后缀为2of2的文件夹找到路径为database下的“ ...
- 在MyBatis中采用模糊查询变量的引用标志应当是$而不是#
具体如下例: @Select("select count(*) from hy_stock where name like '%${keyword}%' or code like '%${k ...
- AMQP 概论
AMQP 是应用层协议的一个开放标准,为面向消息的中间件设计.基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制.目标是实现一种在全行业广泛使用的标准消 ...
- Python测试框架pytest命令行参数用法
在Shell执行pytest -h可以看到pytest的命令行参数有这10大类,共132个 序号 类别 中文名 包含命令行参数数量 1 positional arguments 形参 1 2 gene ...
- 【新阁教育】基于Log4Net实现日志信息双向存储(含源码)
1.引言 在上位机开发中,日志记录是必不可少的,我们可以通过日志记录做日志分析及错误追踪.初学者会采用txt文本写入来实现日志保存,但是文本写入不是线程安全,当存在多个线程同时写入日志时,就会出现一些 ...
- HTML页面的基本信息
1.python中生成的html页面,每一段的基本解释,以及header中的应用 2.body中的应用 2.1.a href链接点击baidu直接跳转百度网址,如果需要重新打开一个页面,详情看2.16 ...
- Mysql 多表连查 xml写法 非注解形式
1.xml写法 <!-- 联查用户users表 --> <resultMap type="nanh.entity.Tasks" id="selectTa ...
- C#开发PACS医学影像处理系统(十五):Dicom影像交叉定位线算法
1.定位线概念:某个方位的影像在另一个方向的影像上的投影相交线,例如横断面(从头到脚的方向)在矢状面(从左手到右手)上的影像投影面交线. 举个例子:右边的是MR(核磁共振)的某一帧切片,这是从头开始扫 ...
- 福利来了~Linux一键部署包,环境安装不用愁!!!
前言 昨天一哥们的弟弟突然问我有没有部署过的Linux,公司连个运维都没有,服务器都要后端部署.... 你有没有相似的遭遇呢?公司规模小,后端即是运维,一份工资干两份活,哈哈~ 为了解决这老弟的困惑, ...
- Redis主从复制、多实例、高可用
Redis主从复制 在开始实现redis的高可用之前,首先来学习一下如何实现redis的主从复制,毕竟高可用也会依赖主从复制的技术. Redis的主从复制,可以实现一个主节点master可以有多个从节 ...