异步模型 requestAnimationFrame

前言

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

上面这段介绍来自于MDN当中,是关于requestAnimationFrame的一个介绍。

在网页开发当中,想要设置动画,一般会通过下面的几种途径:

  • js的定时器
  • css3的transition和animation
  • h5当中的canvas

除了上述的几种方式以外,H5还提供了一种新的api, 专门用来请求动画并且是一个异步的api,requestAnimationFrame,可以理解为请求动画帧

为了便于理解这个api,我们需要来了解几个相关的概念:

屏幕刷新频率

所谓的屏幕刷新频率,指的是图像在屏幕上的更新速度,也就是屏幕上的图像每秒钟出现的次数,单位是赫兹(Hz)。

一般电脑而言,刷新频率一般在60Hz。屏幕的刷新频率会受到屏幕分辨率、屏幕尺寸、显卡的影响。

目前市场上的屏幕主要有两种,一种是CRT,另外一种是LCD,CRT是传统显示屏,LCD是使用比较广泛的液晶显示屏幕。

CRT是一种使用阴极射线管的显示器,屏幕上的图形图像是由一个个因电子束击打而发光的荧光点组成,由于显像管内荧光粉受到电子束击打后发光的时间很短,所以电子束必须不断击打荧光粉使其持续发光。电子束每秒击打荧光粉的次数就是屏幕刷新频率。

而对于LCD来说,则不存在刷新频率的问题,它根本就不需要刷新。因为LCD中每个像素都在持续不断地发光,直到不发光的电压改变并被送到控制器中,所以LCD不会有电子束击打荧光粉而引起的闪烁现象。

当我们正常的去看电脑屏幕的时候,显示器同样会按照指定的赫兹数例如每秒60次的频率不断的更新屏幕上的图像。

而我们之所以感觉不到屏幕上的变化,是因为人的眼睛存在视觉停留效应,简单的说就是前一副画面还没有完全失去印象,后面一副就已经刷新出来了,这中间的间隔时间大约为16.7ms(1000/60≈16.7),这也就导致了我们认为屏幕上的图像是静止的。

动画原理

根据刷新频率,我们在屏幕上看到的内容以每秒钟60次的频率进行着刷新,因为刷新的频率较高,所以我们基本感觉不到屏幕在进行刷新。而动画本质就是要让人眼看到图像被刷新而引起变化的视觉效果,这个变化要以连贯的、平滑的方式进行过渡。 那怎么样才能做到这种效果呢?

刷新的频率为60Hz的屏幕每16.7ms刷新一次,我们可以进行一种假设,在屏幕每次刷新前都将元素向右移动1像素。这样做的结果就是每次在刷新后我们看到的元素的位置都是不同的,会因为刷新频率较快,我们看到的图像就是移动的元素。

setTimeout 和 setInterval 掉帧

我们在使用setTimeoutsetInterval的时候,就可能出现掉帧的现象。

为什么呢?

首先这两个定时器其实就是通过设置一个时间间隔然后不断调整元素位置,从而实现动画效果。

但是在某些机器上或者某些特殊情况下使用定时器实现的动画会出现掉帧和卡顿现象,出现的原因可能是以下的两点因素:

  • 这两个定时器执行时间不确定,因为在js中这两个定时器是属于异步操作,会被放到队列当中去,只有等到主线程的任务执行完毕之后才会执行队列里面的内容。因此定时器执行的时间可能存在比实际的时间长一点的情况。
  • 刷新频率受到屏幕分辨率和屏幕尺寸的影响,因此不同的设备可能会有不同的刷新频率,而定时器的时间间隔是相同并且固定的,这个时间可能和定时器的时间不相符。

以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致,从而引起丢帧现象。 那为什么步调不一致就会引起丢帧呢?

首先要明白,setTimeout的执行只是在内存中对图像属性进行改变,这个变化必须要等到屏幕下次刷新时才会被更新到屏幕上。如果两者的步调不一致,就可能会导致中间某一帧的操作被跨越过去,而直接更新下一帧的图像。

requestAnimationFrame

和传统的定时器相比较而言,requestAnimationFrame最大的优势是由系统决定到底什么时候执行回调函数。

例如,刷新频率是60Hz,那么回调函数就是每16.7秒执行一次,如果刷新频率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms。

简单的说,这个api可以保证回调函数可以在屏幕的每一次刷新间隔只执行一次,这样做的好处就是不会引起掉帧的发生。

下面是简单的demo,通过requestAnimationFrame来让元素不断的位移。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#d1 {
width:100px;
height:100px;
background-color:red;
position: absolute;
left:0;
top:0;
}
</style>
</head>
<body>
<div id="d1"></div>
</body>
<script type="text/javascript">
var d1 = document.getElementById('d1') var num = 0; function render() {
num +=1;
if(num <=500) {
d1.style.left = d1.offsetLeft + 1 + 'px';
// 动画尚未结束前,进行递归渲染
window.requestAnimationFrame(render)
}
} // 第一帧需要手动的进行渲染
window.requestAnimationFrame(render)
</script>
</html>

requestAnimationFrame 还可以和DocumentFragment配合,从而提高向网页中插入大量数据时的性能。

使用requestAnimationFrame 的好处

  • 避免掉帧卡顿
  • cpu节能 使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
  • 函数节流 在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。

异步模型 requestAnimationFrame的更多相关文章

  1. .NET - 基于事件的异步模型

    注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...

  2. Task C# 多线程和异步模型 TPL模型

    Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task TaskCompletionSource 更通用, ...

  3. libgo协程库:网络性能完爆ASIO异步模型(-O3测试)

    在purecpp社区的github组织中有一个协程库:https://github.com/yyzybb537/libgo 近日有用户找到我,想要了解一下libgo库在网络方面的性能,于是选取已入选标 ...

  4. JavaScript 学习笔记之线程异步模型

    核心的javascript程序语言并没有包含任何的线程机制,客户端javascript程序也没有任何关于线程的定义,事件驱动模式下的javascript语言并不能实现同时执行,即不能同时执行两个及以上 ...

  5. Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别

    Task C# 多线程和异步模型 TPL模型   Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...

  6. JQuery日记6.7 Javascript异步模型(二)

    异步模型看起来非常美,但事实上它也是有天生缺陷的.看以下代码 try { setTimeout( function(){ throw new Error( '你抓不到我的!' ); }, 100); ...

  7. Netty 异步模型

    简介 Netty中的 I/O 操作是异步的, 包括 Bind.Write.Connect 等操作会简单的返回一个ChannelFuture. 调用者不能立刻获得结果, 而是通过Future-Liste ...

  8. ​结合异步模型,再次总结Netty多线程编码最佳实践

    更多技术分享可关注我 前言 本文重点总结Netty多线程的一些编码最佳实践和注意事项,并且顺便对Netty的线程调度模型,和异步模型做了一个汇总.原文:​​结合异步模型,再次总结Netty多线程编码最 ...

  9. 以两种异步模型应用案例,深度解析Future接口

    摘要:本文以实际案例的形式分析了两种异步模型,并从源码角度深度解析Future接口和FutureTask类. 本文分享自华为云社区<[精通高并发系列]两种异步模型与深度解析Future接口(一) ...

随机推荐

  1. 红米k30评测+全面解读

    暂时还没有图片等页面美化..其实网上有很多美图,这里不再粘贴了 红米k30是小米公司子品牌红米最近推出的一款性价比非常高的手机,以下进行多方面解读: 本人会尽量用不懂手机都能理解的语言来各方面讲解k3 ...

  2. 软件测试常用的linux命令

    不同Linux发行版的命令数量不一样,但Linux发行版本最少的命令也有200多个.这里我把比较重要和使用频率最多的命令,按照它们在系统中的作用分成下面六个部分一一介绍. ◆ 安装和登录命令:logi ...

  3. CF487E Tourists[圆方树+树剖(线段树套set)]

    做这题的时候有点怂..基本已经想到正解了..结果感觉做法有点假,还是看了正解题解.. 首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点 ...

  4. 题解 [CF525D] Arthur and Walls

    题面 解析 首先考虑将一个\('*'\)变成\('.'\)后会形成什么, 显然至少是一个\(2\times 2\)的矩形. 因为\(1\times 1\)和\(1\times 2\)的改了没用啊, 而 ...

  5. BZOJ 2982 combination 脑子+组合数学

    可以发现,整个数列构成一个树形结构,并且是个完全二叉堆(小根堆). 并且这个堆的形态在给定$n$后是固定的,第1个位置上显然只能放1. 对子树的根来说,他自己是所分得的数集中最小的那个,所以从剩下$s ...

  6. HGOI 20191108 题解

    Problem A 新婚快乐 一条路,被$n$个红绿灯划分成$n+1$段,从前到后一次给出每一段的长度$l_i$,每走$1$的长度需要$1$分钟. 一开始所有红绿灯都是绿色的,$g$分钟后所有红绿灯变 ...

  7. CF1207A

    CF1207A-There Are Two Types Of Burgers 题意: 出售普通汉堡和鸡肉汉堡,并且两种汉堡所需的原材料价格不同,问最多能卖多少钱. 解法: 对于这道题,我们优先考虑先卖 ...

  8. elasticsearch-head后台运行

    运行插件 # npm run start > elasticsearch-head@0.0.0 start /usr/local/elasticsearch-head-master > g ...

  9. 预处理、const、static与sizeof-为什么不把所有的函数都定义成内联函数

    1:内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率.如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很小.另一方面,每一处内联函数的调用都要复制 ...

  10. 手把手教你在Linux系统下安装MySQL

    在CentOS中默认安装有MariaDB,这个是MySQL的分支,但为了需要,还是要在系统中安装MySQL,而且安装完成之后可以直接覆盖掉MariaDB. 1. 下载并安装MySQL官方的 Yum R ...