异步模型 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接口(一) ...
随机推荐
- Vue入门(一)——环境搭建
1.参考该教程装vue脚手架和创建工程 https://segmentfault.com/a/1190000008922234 附:在每个工程下cmd,执行npm install,此时工程下会多一个n ...
- Linux的正则练习
grep和 egrep的正则表达式 1.显示三个用户root.wang的UID和默认shell cat /etc/passwd | grep “^\(root\|wang\)” | tr ‘:’ ‘ ...
- mysql 表字段与关键字相同的话
desc is a reserved keyword (short for DESCENDING in ORDER BY). Enlose it into backticks: INSERT INTO ...
- osm2pgsql
osm2pgsql -d gis --create --slim --drop --flat-nodes '/data/nodes.bin' -G --hstore --tag-transform-s ...
- RunTime.getRunTime().addShutdownHook 添加钩子
Runtime.getRuntime().addShutdownHook(shutdownHook); google了一下它的含义:在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设 ...
- windows——celery
celery 4.0版本以后放弃对Windows的支持 GITHUB_issues(https://github.com/celery/celery/issues/4178) 替代解决方案: 安装:p ...
- C# params object[] args 可以传多个参数,可以不限制类型
http://blog.csdn.net/wslyy99/article/details/1836486 C#参数数组(params)的使用方法 http://www.vcsos.com/articl ...
- @ComponentScan什么时候可以不加
SpringBoot在没配置@ComponentScan的情况下,默认只扫描和主类处于同包下的Class. 主类Application.java: import org.springframework ...
- 并发编程入门(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...
- 第四届西安邮电大学acm-icpc校赛 热狗树
题目描述 “我是番茄酱!”“我是黄芥末酱!”“合在一起就是——美式热狗上加的,那个!“热狗树上的每个节点都涂有番茄酱或者黄芥末酱中的一种,这样热狗树就变得美味了~LiMn2O4构造了一颗热狗树,他想 ...