异步模型 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接口(一) ...
随机推荐
- UTC和GMT时间
来源:https://www.cnblogs.com/qiuyi21/archive/2008/03/04/1089456.html UTC和GMT时间 每个地区都有自己的本地时间,在网上以及无线电通 ...
- Java实现复制文件或者文件夹
拷贝一个文件的算法比较简单,当然,可以对它进行优化,比如使用缓冲流,提高读写数据的效率等. 话不多说直接上代码 import java.io.*; /** * 实现文件的拷贝 */ public cl ...
- 在当前目录下配置ansible
配置ansible.cfg [defaults] inventory = myhost # 指定主机清单文件 host_key_checking = False 配置主机清单文件 [node] nod ...
- list深拷贝和浅拷贝
在Python中,经常要对一个list进行复制.对于复制,自然的就有深拷贝与浅拷贝问题.深拷贝与浅拷贝的区别在于,当从原本的list复制出的list之后,修改其中的任意一个是否会对另一个造成影响,即这 ...
- import org.apache.ibatis.annotations.Param 报错
说明缺少依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus ...
- linux系统编程--线程
安装线程man page,命令:sudo apt-get install manpages-posix-dev 线程概念 什么是线程 LWP:light weight process 轻量级的进程,本 ...
- 路由器配置——路由重分布2(OSPF)
一.实验目的:使用路由重分布达到全网互通的效果 二.拓扑图: 三.具体步骤配置: 1.先给各台主机配置ip地址及网关,以PC1为例: 2.路由器配置: (1)R1路由器配置 Router>ena ...
- 【线性代数】2-5:逆(Inverse)
title: [线性代数]2-5:逆(Inverse) toc: true categories: Mathematic Linear Algebra date: 2017-09-11 20:00:1 ...
- Codeforces 1238E. Keyboard Purchase
传送门 注意到 $m$ 只有 $20$ ,考虑一下状压 $dp$ 设 $f[S]$ 表示当前确定的字符集合为 $S$ ,那么转移就考虑从最右边加入的下一个字符 $c$ 那么问题来了,代价如何计算 考虑 ...
- 21.栈的压入、弹出序列 Java
题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压 ...