HTML5之动画优化(requestAnimationFrame)
- 定时器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)的更多相关文章
- HTML5探秘:用requestAnimationFrame优化Web动画
本文转载自: HTML5探秘:用requestAnimationFrame优化Web动画
- [js高手之路] html5新增的定时器requestAnimationFrame实战进度条
在requestAnimationFrame出现之前,我们一般都用setTimeout和setInterval,那么html5为什么新增一个requestAnimationFrame,他的出现是为了解 ...
- html5新增的定时器requestAnimationFrame
在requestAnimationFrame出现之前,我们一般都用setTimeout和setInterval,那么html5为什么新增一个requestAnimationFrame,他的出现是为了解 ...
- React-Native 动画优化
前言 动画对于客户端来说是非常重要的一部分,直接影响到应用的用户体验.前端对于动画优化通常使用CSS3样式来实现动画,以利用GPU加速特性.而React-Native由于渲染模式的不同,无法使用CSS ...
- 移动HTML5前端性能优化总结
概述 1. PC优化手段在Mobile侧同样适用 2. 在Mobile侧我们提出三秒种渲染完成首屏指标 3. 基于第二点,首屏加载3秒完成或使用Loading 4. 基于联通3G网络平均338KB/s ...
- [js高手之路] html5 canvas动画教程 - 实时获取鼠标的当前坐标
有了前面的canvas基础之后,现在开始就精彩了,后面写的canvas教程都是属于综合应用,前面已经写了常用的canvas基础知识,参考链接如下: [js高手之路] html5 canvas系列教程 ...
- [js高手之路]html5 canvas动画教程 - 边界判断与小球粒子模拟喷泉,散弹效果
备注:本文后面的代码,如果加载了ball.js,那么请使用这篇文章[js高手之路] html5 canvas动画教程 - 匀速运动的ball.js代码. 本文,我们要做点有意思的效果,首先,来一个简单 ...
- [js高手之路]html5 canvas动画教程 - 边界判断与反弹
备注:本文后面的代码,如果加载了ball.js,那么请使用这篇文章[js高手之路] html5 canvas动画教程 - 匀速运动的ball.js代码. 边界反弹: 当小球碰到canvas的四个方向的 ...
- 移动HTML5前端性能优化指南
概述 1. PC优化手段在Mobile侧同样适用 2. 在Mobile侧我们提出三秒种渲染完成首屏指标 3. 基于第二点,首屏加载3秒完成或使用Loading 4. 基于联通3G网络平均338KB/s ...
随机推荐
- RCAN——Image Super-Resolution Using Very Deep Residual Channel Attention Networks
1. 摘要 在图像超分辨领域,卷积神经网络的深度非常重要,但过深的网络却难以训练.低分辨率的输入以及特征包含丰富的低频信息,但却在通道间被平等对待,因此阻碍了网络的表示能力. 为了解决上述问题,作者提 ...
- js中bind解析
一.arguments的含义 // arguments 是一个对应于传递给函数的参数的类数组对象 function a(){ console.log(arguments); } a(); // Arg ...
- PhoneUtils
import java.util.regex.Matcher; import java.util.regex.Pattern; public class PhoneUtils { /** * @par ...
- consul ocelot
consul配置完成后 新建.netcoreapi项目, nuget安装ocelot 添加多个配置文件,.netcore中会自动合并为一个文件,global配置总的配置,其他为各个项目的配置 Serv ...
- python之scrapy模块pipelines
1.知识点 """" pipelines使用: 1.在spiders里面使用yield生成器 list_li = response.xpath("// ...
- hutool-all 包把实体Bean转化成字符串,以及把字符串转化成Bean对象
GxyJobEntity gxyJobEntity1 = new GxyJobEntity(); gxyJobEntity1.setUserId("user001"); gxyJo ...
- 扩展:Zero Copy
- k8s-高可用多主master配置
准备主机 centos7镜像 node1: 192.168.0.101 node2: 192.168.0.102 node3: 192.168.0.103 vip: 192.168.0.104 配置s ...
- Java数据结构之TreeMap
一.源码注释 /** * TreeMap基于NavigableMap 的一个红黑树的实现.TreeMap会根据比较器comparator对键值对的key进行比较进行排序,如果没有比较器就是用key的自 ...
- 安装最新docker-ce失败解决
报错 下载 检查本地是否已经安装 rpm -qa |grep containerd.io 如果有低版本的,卸载即可. 安装新版的containerd.io软件包 wget https://downlo ...