异步模型 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刷新一次,多次绘制并不会在屏幕上体现出来。
原文地址:https://www.cnblogs.com/liujunhang/p/11844830.html
- phalapi-进阶篇4(notrom进阶以及事务操作)
- 自动机器学习:利用遗传算法优化递归神经网络
- zephir-(8)类和对象1
- zephir-(4)基本语法
- zephir-(2)安装和初体验
- phalcon-入门篇8(Model层基础使用2)
- 封装、私有,一文掌握Python关键代码
- 基于TensorFlow的比较研究:神经网络优化算法
- Visual Studio 2008 每日提示(二十三)
- 应用层/安全层/传输层如何进行协议选型?
- 一分钟掌握数据库垂直拆分
- 在创建的Silverlight Control中使用图片
- 启用silverlight3的out of browser
- 连接池原来这么简单(一分钟系列)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python 字典 使用技巧
- 微信小程序生命周期学习笔记-组件
- C语言入门系列之2.数据类型、运算符和表达式
- 树莓派的cpu与gpu通信设计浅析
- Python全栈(七)Flask框架之5.视图高级--类视图和蓝图
- Python全栈(六)项目前导之5.使用GitHub进行多人协同开发
- 附002.Nginx代理相关模块解析
- ApiBoot v2.3.x分支第一个版本发布,重构源码架构设计
- Python全栈(七)Flask框架之1.Flask简介与URL和视图介绍
- 两个CSS知识点:BFC和选择器权重
- C语言入门系列之9.预处理
- Python爬虫常见异常及解决办法
- 理解nodejs中js和c++的通信原理
- 如何使用FFmpeg将互联网直播点播平台内直播视频流转化为HLS流?
- 记一次nodejs问题排查