HTML动画 request animation frame
在网页中,实现动画无外乎两种方式。
1. CSS3 方式,也就是利用浏览器对CSS3 的原生支持实现动画;
2. 脚本方式,通过间隔一段时间用JavaScript 来修改页面元素样式来实现动画。
接下来我们就分别介绍这两种方式的原理,让大家先对这两种方式有一个直观认识,了解各自的优缺点。
CSS3 的方式下,开发者一般在css 中定义一些包含CSS3 transition 语法的规则。在某些特定情况下,让这些规则发生作用,于是浏览器就会将这些规则应用于指定的DOM元素上,产生动画的效果。这种方式毫无疑问运行效率要比脚本方式高,因为浏览器原生支持,省去了JavaScript 的解释执行负担,有的浏览器(比如Chrome 浏览器)甚至还可以充分利用GPU 加速的优势,进一步增强了动画渲染的性能。不过CSS3 的方式并非完美,也有不少缺点。
首先, CSS3 Transition 对一个动画规则的定义是基于时间和速度曲线( Speed Curve)的规则。换句话来说,就是CSS3 的动画过程要描述成“在什么时间范围内,以什么样的运动节奏完成动画” 。
<!DOCTYPE html>
<html>
<head>
<style>
.sample {
background: red;
position: absolute;
left: 0px;
width: 100px;
height: 100px;
transition-property: left;
transition-duration: 0.5s;
transition-timing-function: ease
}
.sample:hover {
left: 420px;
}
</style>
</head>
<body>
<div class="sample" />
</body>
</html>
在上面的例子中, sample 类的元素定义了这样的动画属性:“ left 属性会在0.2 秒内以ease 速度曲线完成动画” 。transition 只定义了动画涉及的属性、时间和速度曲线,并不定义需要修改的具体值。sample 类的left 属性默认值为0 ,当鼠标移到sample 类元素上时, left 属性就拥有新的值420px 。这时候transition 定义的规则发生作用,让left 属性以ease 速度曲线在0.2 秒
的时间完成从0 变成420px 的转化过程,这个过程中,用户看到的就是sample 类元素向右移动420 个像素的动画过程。
因为CSS3 定义动画的方式是基于时间和速度曲线,可能不利于动画的流畅,因为动画是可能会被中途打断的,在上面的例子中,鼠标移到sample 类元素上的时候开始动画,但是在0.2 秒的动画时间内,用户的鼠标可能会移出这个sample 类元素,这时候CSS3 还会以ease 速度曲线的节奏让sample 类元素回到原位。从用户体验角度来说,中途sample 类元素回到原位的动作,语义上是“取消操作”的含义,但却依然以同样的时间和ease 节奏来完成“取消操作”的动画,这并不合理。
时间和速度曲线的不合理是CSS3 先天的属性,更让开发者头疼的就是开发CSS3 规则的过程,尤其是对transition-duration 时间很短的动画调试,因为CSS3 的transition 过程总是一闪而过,捕捉不到中间状态,只能一遍一遍用肉眼去检验动画效果,用CSS3做过复杂动画的开发者肯定都深有体会。虽然CSS3 有这样一些缺点,但是因为其无与伦比的性能,用来处理一些简单的动画还是不错的选择。
相对于CSS3 方式,脚本方式最大的好处就是更强的灵活度,开发者可以任意控制动画的时间长度,也可以控制每个时间点上元素渲染出来的样式,可以更容易做出丰富的动画效果。脚本方式的缺点也很明显,动画过程通过JavaScript 实现,不是浏览器原生支持,消耗的计算资源更多。如果处理不当,动画可能会出现卡顿滞后现象,本来使用动画是为了创造更好的用户体验,如果出现卡顿,反而对用户体验带来不好的影响。最原始的脚本方式就是利用setlnterval 或者setTimeout 来实现,每隔一段时间一个指定的函数被执行来修改界面的内容或者样式,从而达到动画的效果。
<!DOCTYPE html>
<html>
<head>
<style>
#sample {
position: absolute;
background: red;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="sample" />
<script type="text/javascript">
var animatedElement = document.getElementById("sample");
var left = 0;
var timer;
var ANIMATION_INTERVAL = 16; timer = setInterval(function() {
left += 10;
animatedElement.style.left = left + "px";
if ( left >= 400 ) {
clearInterval(timer);
}
}, ANIMATION_INTERVAL); </script>
</body>
</html>
在上面的例子中,有一个常量ANIMATION INTERVAL 定义为16 , setlnterval 以这个常量为间隔,每16 毫秒计算一次sample 元素的left 值,每次都根据时间推移按比例增加left 的值,直到left 大于400 。为什么要选择16 毫秒呢?因为每秒渲染60 帧(也叫60fps, 60 Frame Per Second)会给用户带来足够流畅的视觉体验,一秒钟有1000 毫秒, 1000 /60 =16 ,也就是说,如果我们做到每16 毫秒去渲染一次画面,就能够达到比较流畅的动画效果。对于简单的动画, setlnterval 方式勉强能够及格,但是对于稍微复杂一些的动画,脚本方式就顶不住了,比如渲染一帧要花去超过32 毫秒的时间,那么还用16 毫秒一个间隔的方式肯定不行。实际上,因为一帧渲染要占用网页线程32 毫秒,会导致setlnterval根本无法以16 毫秒间隔调用渲染函数,这就产生了明显的动画滞后感,原本一秒钟完成的动画现在要花两秒钟完成,所以这种原始的setlnterval 方式是肯定不适合复杂的动画的。
出现上面问题的本质原因是setlnterval 和setTimeout 并不能保证在指定时间间隔或者延迟的情况下准时调用指定函数。所以可以换一个思路,当指定函数调用的时候,根据逝去的时间计算当前这一帧应该显示成什么样子,这样即使因为浏览器渲染主线程忙碌导致一帧渲染时间超过16 毫秒,在后续帧谊染时至少内容不会因此滞后,即使达不倒60fps 的效果,也能保证动画在指定时间内完成。
<!DOCTYPE html>
<html>
<head>
<style>
#sample {
position: absolute;
background: red;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="sample" />
<script type="text/javascript"> var lastTimeStamp = new Date().getTime();
function raf(fn) {
var currTimeStamp = new Date().getTime();
var delay = Math.max(0, 16 - (currTimeStamp - lastTimeStamp));
var handle = setTimeout(function(){
fn(currTimeStamp);
}, delay);
lastTimeStamp = currTimeStamp;
return handle;
} var left = 0;
var animatedElement = document.getElementById("sample");
var startTimestamp = new Date().getTime();
function render(timestamp) {
left += (timestamp - startTimestamp) / 16;
animatedElement.style.left = left + 'px';
if (left < 400) {
raf(render);
}
} raf(render);
</script>
</body>
</html>
在上面定义的raf 中,接受的fn 函数参数是真正的渲染过程, raf 只是协调渲染的节奏。raf 尽量以每隔16 毫秒的速度去调用传递的fn 参数,如果发现上一次被调用时间和这一次被调用时间相差不足16 毫秒,就会保持16 毫秒一次的渲染间隔继续,如果发现
两次调用时间间隔已经超出了16 毫秒,就会在下一次时钟周期立刻调用fn 。上面的render 函数中根据当前时间和开始动画的时间差来计算sample 元素的left 属性,这样无论render 函数何时被调用,总能够渲染出正确的结果。
最后,我们将render 作为参数传递给raf ,启动了动画过程
HTML动画 request animation frame的更多相关文章
- Android动画效果之Frame Animation(逐帧动画)
前言: 上一篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画),今天来总结下Android的另外一种动画Frame ...
- Android动画主要包含补间动画(Tween)View Animation、帧动画(Frame)Drawable Animation、以及属性动画Property Animation
程序运行效果图: Android动画主要包含补间动画(Tween)View Animation.帧动画(Frame)Drawable Animation.以及属性动画Property Animatio ...
- Android动画总结#补间动画(Tween Animation/View Animation) #帧动画(Frame Animation/Drawable Animation)#属性动画(PropertyAnimation)
1.共有三种动画,英文名字多种叫法如下 第一种动画:补间动画(Tween Animation/View Animation) 四个:RotateAnimation旋转. AlphaAnimation透 ...
- 动画(Animation) 、 高级动画(Core Animation)
1 演示UIImage制作的动画 1.1 问题 UIImage动画是IOS提供的最基本的动画,通常用于制作一些小型的动画,本案例使用UIImage制作一个小狗跑动的动画,如图-1所示: 图-1 1.2 ...
- 【Android UI设计和开发】动画(Animation)详细说明(一)
Android开发之动画效果浅析 请尊重他人的劳动成果.转载请注明出处:Android开发之动画效果浅析 程序执行效果图: Android动画主要包括补间动画(Tween)View Animation ...
- Android 动画——属性动画Property Animation
Android在3.0之前只提供了两种动画:View Animation .Drawable Animation .也就是我们在<Android 动画——Frame Animation与Twee ...
- .net mvc 站点自带简易SSL加密传输 Word报告自动生成(例如 导出数据库结构) 微信小程序:动画(Animation) SignalR 设计理念(一) ASP.NET -- WebForm -- ViewState ASP.NET -- 一般处理程序ashx 常用到的一些js方法,记录一下 CryptoJS与C#AES加解密互转
.net mvc 站点自带简易SSL加密传输 因项目需要,传输数据需要加密,因此有了一些经验,现简易抽出来分享! 请求:前端cryptojs用rsa/aes 或 rsa/des加密,后端.net ...
- iOS 核心动画 Core Animation浅谈
代码地址如下:http://www.demodashi.com/demo/11603.html 前记 关于实现一个iOS动画,如果简单的,我们可以直接调用UIView的代码块来实现,虽然使用UIVie ...
- android 补间动画和Animation
介绍: 补间动画是一种设定动画开始状态.结束状态,其中间的变化由系统计算补充.这也是他叫做补间动画的原因. 补间动画由Animation类来实现具体效果,包括平移(TranslateAnimation ...
随机推荐
- vsftp 基于虚拟用户的ftp服务器 如何做配额
做配额的方法: 1,是用磁盘配额,但是虚拟用户好像没有好办法.只能应用于本地用户.与Vsftpd设置无关 2,文件夹限制大小,是占用的.这和Vsftpd没有关系 所以可以先把用户禁锢在自己工作目录里面 ...
- sql对于字符串的处理
- Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第十三集之Redis的单机版搭建】
(转载其他博客的安装步骤,截图是自己的) 1, 第一步:安装gcc编译环境 yum install gcc-c++ 第二步:把redis的源码上传到linux服务器. 第三步:解压缩. tar -zx ...
- scrapy选择器归纳
python 爬虫: srcrapy框架xpath和css选择器语法 Xpath基本语法 一.常用的路径表达式: 表达式 描述 实例 nodename 选取nodename节点的所有子节点 //div ...
- Get与Post方法的区别
Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET,POST,PUT,DELETE. 一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELE ...
- CSRF自动化检测
CSRF自动化检测: 这里主要是对POST型form表单的检测 1. 根据URL获取form表单组成的数组 2. 遍历表单数组,对比不设置cookie与设置了cookie两种情况下的表单是否还存在,如 ...
- asp.net core 依赖注入实现全过程粗略剖析(2)
接着 上篇 目前也算是交代清楚了相关的类.那么框架具体是如何来实例化的呢?整个的流程是怎么样的. 我们参考源码中的Test文件夹来看看: var collection = new ServiceCol ...
- (转)java创建对象的步骤
关于对象的创建过程一般是从new指令(我说的是JVM的层面)开始的(具体请看图1),JVM首先对符号引用进行解析,如果找不到对应的符号引用,那么这个类还没有被加载,因此JVM便会进行类加载过程.符号引 ...
- 用shell脚本守护后台进程
假如现在在 crond 中添加了一个每分钟执行的定时任务如下: */ * * * * root cd /data/sbin; sh test.sh >/dev/>& 为了防止上一个 ...
- java文件课后动手动脑
package 第九周作业1; import java.io.File; import java.io.FileInputStream; import java.io.IOException; imp ...