背景

在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout或者setInterval 来实现,css3 可以使用 transition 和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。

但是传统的通过setTimeout或者setInterval实现的动画,存在两个问题,第一个就是动画的循时间环间隔不好确定,设置长了动画显得不够平滑流畅, 设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是17ms(大多数电脑的显示器刷新频率是60Hz,1000ms / 60),第二个问题是定时器第二个时间参数只是指定了多久后将动画任务添加到浏览器的UI线程队列中,如果UI线程处于忙碌状态,那么动画不会立刻执行,为了解决这个问题,H5中加入了requestAnimationFrame。

setInterval实现一个动画

<html>
<head>
<title></title>
<style type="text/css">
#box {
margin: 200px;
width: 200px;
height: 200px;
background: green;
}
</style>
</head> <body>
<div id="box"></div>
</body>
<script type="text/javascript">
var element = document.getElementById('box')
var left = 0;
var animateCallback = function() {
element.style.marginLeft = (++left)+ 'px';
if (left === 500) {
clearInterval(interval);
}
}
var interval = setInterval(animateCallback, (1000 / 60));
</script>
使用setInterval我们成功的实现了一个平滑的动画效果,但是我们会发现setInterval的执行时间并不确定,在javascript中,setInterval任务被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,所以setInterval的实际执行时机一般要比其设定的时间晚一些。使用setInterval实现动画容易失帧。

丢帧

例如我们使用setInterval进行颜色的切换

var color = ['green', 'red', 'blue', 'yellow'];
var element = document.getElementById('box');
var index = 0;
var animateCallback = function() {
index++;
element.style.backgroundColor = color[index];
if (index === 3){
clearInterval(interval);
}
}
var interval = setInterval(interval, 1000 / 100);

上面的动画切换我们设置了间隔10切换一次,但是此时的屏幕刷新频率为16.7,

  1. 第0ms时,屏幕未刷新,等待中,setInterval也未执行,等待中;
  2. 第10ms时,屏幕未刷新,等待中,setInterval执行颜色切换为green
  3. 第16.7ms时: 屏幕刷新,屏幕的box颜色改变为green,setInterval未执行。继续等待
  4. 第20ms时:屏幕未刷新,等待中,setInterval执行颜色切换为red,
  5. 第30ms时,屏幕未刷新,等待中,setInterval执行颜色切换为blue,
  6. 第34.7ms时,屏幕刷新,屏幕的box颜色改变为blue,setTimeout未执行,继续等待中。
  7. ...
从上面的执行过程中,我们可以看出,在执行到20ms和30ms直接,setInterval切换了两次颜色,但是屏幕并没有执行一次刷新,就会出现,在34.7ms时,屏幕上直接展示的就是blue颜色,而跳过了red颜色。这就是丢帧现象,这种现象也会引起页面卡顿。

requstAnimationFrame实现

<html>
<head>
<title></title>
<style type="text/css">
#box {
margin: 200px;
width: 200px;
height: 200px;
background: green;
}
</style>
</head> <body>
<div id="box"></div>
</body>
<script type="text/javascript">
var start = null;
var element = document.getElementById('box');
var left = 0;
var raf_id = null;
function animateCallback() {
element.style.marginLeft = (++left) + 'px';
if (left === 500) {
cancelAnimationFrame(raf_id);
} else {
raf_id = requestAnimationFrame( animateCallback );
}
}
raf_id = window.requestAnimationFrame(animateCallback);
</script>
requestAnimationFrame返回请求的id(整数),我们可以使用这个id来取消请求(cancelAnimationFram(id)),从而停止动画的执行。 和setInterval相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机,requestAnimationFrame的步伐跟着系统的刷新步伐走,它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧。

requestAnimationFrame优势

CPU节能

使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout仍然在后台执行动画任务,此时页面处于不可见或者不可用的状态,刷新动画是没有意义的,而且还浪费CPU资源,而rAF则完全不同,当页面处于未激活的状态下,该页面的屏幕绘制任务也会被系统暂停,因此跟着系统步伐走的rAF也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。

函数节流

在高频率事件(resize, scroll)中,为了防止在一个刷新间隔内发生多次函数执行,使用rAF可保证每次绘制间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销,一个绘制间隔内函数执行多次是没有意义的,因为显示器每16.7ms绘制一次,多次绘制并不会在屏幕上体现出来。

requestAnimationFrame小结的更多相关文章

  1. 关于 requestAnimationFrame 小结

    一.小谈 requestAnimationFrame: 说起 requestAnimationFrame,我们先看幅图: 相当一部分的浏览器的显示频率是16.7ms, 就是上图第一行的节奏,表现就是“ ...

  2. 3月web前端面试小结

    说一下box-sizing的应用场景 box-sizing的属性值分为两个,border-box和content-box,其中, border-box:width=content+padding+bo ...

  3. 神奇的requestAnimationFrame解决传统定时器bug

    可能你还没见过这个东西是个啥,其实他就是类似于setTimeout和setInterval,然而它与setTimeout和setInterval又有所不同,requestAnimationFrame不 ...

  4. 小程序刷新webview小结

    场景 在小程序其它页面做了操作,数据发生改变,回到webview页面时需要更新webview里面的数据.由于小程序没有提供与webview的实时通信能力,因此刷新页面是个可考虑的做法. 方法一 最常见 ...

  5. 动画requestAnimationFrame

    前言 在研究canvas的2D pixi.js库的时候,其动画的刷新都用requestAnimationFrame替代了setTimeout 或 setInterval 但是jQuery中还是采用了s ...

  6. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  7. Python自然语言处理工具小结

    Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...

  8. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  9. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

随机推荐

  1. myeclipse经常弹出Subversion Native Library Not Available框解决办法

    两种解决方案:(1)在myeclipse中选择 "Windows" -> Perferences. 然后通过左上方的筛选,选出svn设置菜单,点解左侧的"SVN&q ...

  2. Daphile 安装手册 -- 官方文档译文 [原创]

    Daphile 安装手册(Daphile Installation) 英文原文:https://www.daphile.com/download/DaphileInstallation.pdf 采集日 ...

  3. jstat gcutil

    QQA: jstat gcutil 的输出是什么意思 当 Java 程序有性能问题时,尤其是响应时间有突然变化时,最好第一时间查看 GC 的状态.一般用 jstat -gcutil <pid&g ...

  4. MySQL 中的WAL机制

    本文主要概括的是 WAL 机制涉及的三种日志,具体与其他组件的联系.执行可查看 一条 sql 的执行过程详解 .MySQL中的事务原理和锁机制 . 是什么 WAL,全称是Write-Ahead Log ...

  5. 音视频入门-19-使用giflib处理GIF图片

    * 音视频入门文章目录 * GIFLIB The GIFLIB project 上一篇 [手动生成一张GIF图片], 自己生成了一张 GIF 动态图 rainbow.gif. 下面,使用 GIFLIB ...

  6. 包与包管理.md

    软件包:源码包   RPM包 二进制包 RPM包依赖性   模块依赖查询   www.rpmfind.net umount 解除CDROM挂载 mount 挂载 umount [/dev/device ...

  7. 基于SVM的字母验证码识别

    基于SVM的字母验证码识别 摘要 本文研究的问题是包含数字和字母的字符验证码的识别.我们采用的是传统的字符分割识别方法,首先将图像中的字符分割出来,然后再对单字符进行识别.首先通过图像的初步去噪.滤波 ...

  8. Docker haproxy应用构建 (五)

    编写dockerfile from centos-base:v1 MAINTAINER 57674891@qq.com RUN mkdir -p /data/{soft,src,logs,script ...

  9. 编译安装 nginx -1.14.2

    编译安装 nginx -1.14.2 1 ) 下载nginx-1.14.2 源码包: wget http://nginx.org/download/nginx-1.14.2.tar.gz 2 ) 编译 ...

  10. Python找对称数——纪念第一次自主编写代码

    2021-01-17 题目: [问题描述]已知10个四位数输出所有对称数及个数 n,例如1221.2332都是对称数[输入形式]10个四位数,以空格分隔开[输出形式]输入的四位数中的所有对称数,对称数 ...