• 定时器setInterval实现的匀速动画为什么不是匀速?
  • window.requestAnimationFrame()

一、定时器setInterval实现的匀速动画为什么不是匀速?

以上提问并非通过计算时间戳来计算每帧运动,而是直接使用定时器按照既定的间隔时间叠加每次运动距离的方式,这里主要意在解析浏览器中定时器与页面重绘导致的不准确性,先来看一个示例:

 <style>
#a{
width: 100px;
height: 100px;
background-color:#faf;
position: absolute;
}
</style>
<div id="a"></div>
<script>
var aDom = document.getElementById('a');
function move(){
  aDom.style.left = aDom.offsetLeft + 100 + 'px';
    if(aDom.offsetLeft > 800){
    clearInterval(timer);
    }
}
var timer = setInterval(move,10);
</script>

示例中定时器每隔10毫秒元素移动100px,从定时器的意义上来说这就是匀速运动了,但是定时器只能每隔10毫秒将每一次运动的执行函数添加执行队列中,执行并修改元素的属性值,但是实际呈现到页面的却是1秒钟刷新60次的重绘来完成的,这种时间差会产生什么问题呢?

如果不了解浏览器js线程的话,建议有必要先了解:浏览器UI多线程及JavaScript单线程运行机制的理解

继续来看下面的分析:

第一次运动点:100px —— 实际运动(渲染到页面)的时间:16.667毫秒 —— 实际修改元素属性值的时间10毫秒;

第二次运动点:300px —— 实际运动(渲染到页面)的时间:33.334毫秒 —— 实际修改元素属性值的时间20毫秒(200px),实际修改元素属性值的时间30毫秒(300px);

第三次运动点:500px —— 实际运动(渲染到页面)的时间:50.001毫秒 —— 实际修改元素属性值的时间40毫秒(400px),实际修改元素属性值的时间50毫秒(500px);

第四次运动点:600px —— 实际运动(渲染到页面)的时间:66.668毫秒 —— 实际修改元素属性值的时间60毫秒(600px);

第五次运动点:800px —— 实际运动(渲染到页面)的时间:83.335毫秒 —— 实际修改元素属性值的时间70毫秒(700px),实际修改元素属性值的时间80毫秒(800px);

通过上面的分析,原本以定时器的执行逻辑,应该运动8次匀速到达终点的动画,实际上使用了5次非匀速的方式到达终点。

基于以上的原因我们在实现动画的时候采用的是执行时间比值的方式来实现元素运动:

 var h = (new Date()).getTime(); //记录初始时间戳
var ratio = null;
var speed = null;
var t = setInterval(function(){
var performH = (new Date()).getTime(); //获取当前时间戳
ratio = (performH -h) / 80; //当前所用时间 / 动画执行实行
speed = ratio * 800; //使用时间比值计算得出当前元素运动位置
if(ratio < 1){
aDom.style.left = speed + "px";
}else{
aDom.style.left = 800 + "px";
clearInterval(t);
}
},1000/60);

这种处理方式理论上最接近匀速运动,但是还是会受js执行栈与浏览器重绘渲染速率的影响,在HTML5中提供window.requestAnimationFrame()这个API来解决这个问题,既然是HTML5就必然会有兼容性问题,下一节来具体分析。

二、window.requestAnimationFrame()

MDN手册:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame#Notes

window.requestAnimationFrame()告诉浏览器希望执行一次动画,并要求浏览器在下次重绘之前调用指定的回调函数更新动画。

//语法
window.requestAnimationFrame(callback);

callback(回调函数):浏览器下次重绘前执行,callback执行时window.requestAnimationFrame()内部会给这个回调函数传入DOMHighResTimeStamp参数,DOMHighResTimeStamp参数指示当前被requesAnimationFrame排序的回调函数被触发时间。可以理解为时间戳,以ms(毫秒)为单位。

 //上面的示例基于requestAnimationFrame实现
var startA = null;
function a(timestamp){
if(!startA) startA = timestamp;
var progress = timestamp - startA;
console.log(progress);
aDom.style.left = Math.min(progress / 80 * 800, 800) + 'px';
if(progress < 80){
window.requestAnimationFrame(a);
}
}
window.requestAnimationFrame(a);

window.requestAnimationFrame()是HTML5的API就必然会有兼容性问题:

封装兼容性的window.requestAnimationFrame():

 window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000/60);
}
})();

即使使用setInterval()来实现兼容,但并不代表其具备requestAnimationFrame()精准性。window.requestAnimationFrame()执行后会返回一个long整数,请求ID,是回调列表中唯一的标识。非零值,可以使用这个值来给window.cancelAnimationFrame()取消回调函数。

兼容window.cancelAnimationFrame():

 window.cancelAnimFrame = (function(){
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function(id){
window.clearTimeout(id);
}
})();

HTML5之动画优化(requestAnimationFrame)的更多相关文章

  1. HTML5探秘:用requestAnimationFrame优化Web动画

    本文转载自: HTML5探秘:用requestAnimationFrame优化Web动画

  2. [js高手之路] html5新增的定时器requestAnimationFrame实战进度条

    在requestAnimationFrame出现之前,我们一般都用setTimeout和setInterval,那么html5为什么新增一个requestAnimationFrame,他的出现是为了解 ...

  3. html5新增的定时器requestAnimationFrame

    在requestAnimationFrame出现之前,我们一般都用setTimeout和setInterval,那么html5为什么新增一个requestAnimationFrame,他的出现是为了解 ...

  4. React-Native 动画优化

    前言 动画对于客户端来说是非常重要的一部分,直接影响到应用的用户体验.前端对于动画优化通常使用CSS3样式来实现动画,以利用GPU加速特性.而React-Native由于渲染模式的不同,无法使用CSS ...

  5. 移动HTML5前端性能优化总结

    概述 1. PC优化手段在Mobile侧同样适用 2. 在Mobile侧我们提出三秒种渲染完成首屏指标 3. 基于第二点,首屏加载3秒完成或使用Loading 4. 基于联通3G网络平均338KB/s ...

  6. [js高手之路] html5 canvas动画教程 - 实时获取鼠标的当前坐标

    有了前面的canvas基础之后,现在开始就精彩了,后面写的canvas教程都是属于综合应用,前面已经写了常用的canvas基础知识,参考链接如下: [js高手之路] html5 canvas系列教程 ...

  7. [js高手之路]html5 canvas动画教程 - 边界判断与小球粒子模拟喷泉,散弹效果

    备注:本文后面的代码,如果加载了ball.js,那么请使用这篇文章[js高手之路] html5 canvas动画教程 - 匀速运动的ball.js代码. 本文,我们要做点有意思的效果,首先,来一个简单 ...

  8. [js高手之路]html5 canvas动画教程 - 边界判断与反弹

    备注:本文后面的代码,如果加载了ball.js,那么请使用这篇文章[js高手之路] html5 canvas动画教程 - 匀速运动的ball.js代码. 边界反弹: 当小球碰到canvas的四个方向的 ...

  9. 移动HTML5前端性能优化指南

    概述 1. PC优化手段在Mobile侧同样适用 2. 在Mobile侧我们提出三秒种渲染完成首屏指标 3. 基于第二点,首屏加载3秒完成或使用Loading 4. 基于联通3G网络平均338KB/s ...

随机推荐

  1. 订阅发布模式eventEmiter

    // 订阅发布模式 class EventEmitter { constructor() { this._events = {}; } on(name, callback) { if (this._e ...

  2. springboot 静态资源访问,和文件上传 ,以及路径问题

    springboot 静态资源访问: 这是springboot 默认的静态资源访问路径  访问顺序依次从前到后(http://localhost:8080/bb.jpg) spring.resourc ...

  3. shell 脚本基础与条件判断

    #!shell脚本格式决定专业性 #!/bin/bash #filename:脚本名 #author:作者 #date:时间 #脚本作用 脚本的执行方式  #脚本名为wk.sh 绝对路径 /root/ ...

  4. WPF学习笔记 - .Net Framework的分离存储技术

    写入: protected override void OnClosed(EventArgs e) { base.OnClosed(e); IsolatedStorageFile f = Isolat ...

  5. Selenium 2自动化测试实战37(自动发邮件功能)

    自动发邮件功能 例如,如果想在自动化脚本运行完成之后,邮箱就可以收到最新的测试报告结果.SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,它是一组用于由源地址到 ...

  6. [Feature] Compare the effect of different scalers

    Ref: Compare the effect of different scalers on data with outliers 主要是对该代码的学习研究. from sklearn.prepro ...

  7. Java NIO学习笔记九 NIO与IO对比

    Java NIO与IO Java nio 和io 到底有什么区别,以及什么时候使用nio和io,本文做一个比较. Java NIO和IO之间的主要区别 下表总结了Java NIO和IO之间的主要区别, ...

  8. TypeError: '<' not supported between instances of 'str' and 'int'

    <不支持str实例和int实例之间的对比 birth是str类型 2000是int类型 所以无法对比,报错 birth = input('birth: ') if birth < 2000 ...

  9. Scrapy框架——使用CrawlSpider爬取数据

    引言 本篇介绍Crawlspider,相比于Spider,Crawlspider更适用于批量爬取网页 Crawlspider Crawlspider适用于对网站爬取批量网页,相对比Spider类,Cr ...

  10. mySQL的简单安装和配置

    MySQL的安装和配置 1.去官网下载mysql-5.6.29-winx64.zip包.地址: http://dev.mysql.com/downloads/mysql/5.6.html 2,把安装包 ...