异步模型 requestAnimationFrame
异步模型 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 掉帧
我们在使用setTimeout
和setInterval
的时候,就可能出现掉帧的现象。
为什么呢?
首先这两个定时器其实就是通过设置一个时间间隔然后不断调整元素位置,从而实现动画效果。
但是在某些机器上或者某些特殊情况下使用定时器实现的动画会出现掉帧和卡顿现象,出现的原因可能是以下的两点因素:
- 这两个定时器执行时间不确定,因为在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的更多相关文章
- .NET - 基于事件的异步模型
注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...
- Task C# 多线程和异步模型 TPL模型
Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task TaskCompletionSource 更通用, ...
- libgo协程库:网络性能完爆ASIO异步模型(-O3测试)
在purecpp社区的github组织中有一个协程库:https://github.com/yyzybb537/libgo 近日有用户找到我,想要了解一下libgo库在网络方面的性能,于是选取已入选标 ...
- JavaScript 学习笔记之线程异步模型
核心的javascript程序语言并没有包含任何的线程机制,客户端javascript程序也没有任何关于线程的定义,事件驱动模式下的javascript语言并不能实现同时执行,即不能同时执行两个及以上 ...
- 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 ...
- JQuery日记6.7 Javascript异步模型(二)
异步模型看起来非常美,但事实上它也是有天生缺陷的.看以下代码 try { setTimeout( function(){ throw new Error( '你抓不到我的!' ); }, 100); ...
- Netty 异步模型
简介 Netty中的 I/O 操作是异步的, 包括 Bind.Write.Connect 等操作会简单的返回一个ChannelFuture. 调用者不能立刻获得结果, 而是通过Future-Liste ...
- 结合异步模型,再次总结Netty多线程编码最佳实践
更多技术分享可关注我 前言 本文重点总结Netty多线程的一些编码最佳实践和注意事项,并且顺便对Netty的线程调度模型,和异步模型做了一个汇总.原文:结合异步模型,再次总结Netty多线程编码最 ...
- 以两种异步模型应用案例,深度解析Future接口
摘要:本文以实际案例的形式分析了两种异步模型,并从源码角度深度解析Future接口和FutureTask类. 本文分享自华为云社区<[精通高并发系列]两种异步模型与深度解析Future接口(一) ...
随机推荐
- BZOJ3037 创世纪[基环树DP]
实际上基环树DP的名字是假的.. 这个限制关系可以看成每个点有一条出边,所以就是一个内向基环树森林. 找出每个基环树的环,然后对于树的部分,做DP,设状态选或不选为$f_{x,0/1}$,则 $f_{ ...
- sheet.getRow(rowIndex);为null_POI导出excel
第一次使用POI,出现这个问题,看到有其他猿也遇到过,不知道怎么处理,所以记录一下~ sheet.getRow(rowIndex);通过sheet获取指定行,rowIndex代表第几行 用rowInd ...
- UVALive 6859——凸包&&周长
题目 链接 题意:在一个网格图上,给出$n$个点的坐标,用一个多边形包围这些点(不能接触,且多边形的边只能是对角线或直线),求多边形的最小周长. 分析 对于每个点,我们考虑与之相邻的4个点.一共由 $ ...
- TypeError: Object of type 'ListSerializer' is not JSON serializable
问题: 解决:ser.data是json数据,你想要的
- Linux 文件查看
链接:https://www.nowcoder.com/questionTerminal/fb39fbeec71f43a3a16edeb0bc98f4ac 来源:牛客网 /var/log/messag ...
- 期望与概率dp
概率与期望dp 定义: 概率:事件A发生的可能性,计作P(A) 期望:事件A结果的平均大小,记住E(x) E(x)=每种结果的大小与其概率的乘积的和 注意计算概率时需要考虑是否要用容斥原理 期望d ...
- 【线性代数】2-7:转置与变换(Transposes and Permutation)
title: [线性代数]2-7:转置与变换(Transposes and Permutation) toc: true categories: Mathematic Linear Algebra d ...
- 前端导出pdf
html2canvas文档地址 http://html2canvas.hertzen.com/configuration 方式一:使用html2canvas和jspdf插件实现 该方式是通过html2 ...
- Python实用黑科技——解包元素(1)
需求: 很多时候手上已经有了一个具有n个元素的列表或者元组,你打算把这些元素单独取出来(解包)放入n个变量组成的集合(这里的集合和Python自己的set不同)中. 方法: 显然,最好的办法就是直接用 ...
- linux虚拟机安装centos6.x
安装系统,作为每一个it技术控们的基本功,对于各位大神和技术大牛们应该是易如反掌或者是家常便饭啦,都是从无数次安装,重装,刷机中一步步走来的.那么今天,我也分享一套装机教程,共各位和我一样的小白参考或 ...