在网页中,实现动画无外乎两种方式。
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的更多相关文章

  1. Android动画效果之Frame Animation(逐帧动画)

    前言: 上一篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画),今天来总结下Android的另外一种动画Frame ...

  2. Android动画主要包含补间动画(Tween)View Animation、帧动画(Frame)Drawable Animation、以及属性动画Property Animation

    程序运行效果图: Android动画主要包含补间动画(Tween)View Animation.帧动画(Frame)Drawable Animation.以及属性动画Property Animatio ...

  3. Android动画总结#补间动画(Tween Animation/View Animation) #帧动画(Frame Animation/Drawable Animation)#属性动画(PropertyAnimation)

    1.共有三种动画,英文名字多种叫法如下 第一种动画:补间动画(Tween Animation/View Animation) 四个:RotateAnimation旋转. AlphaAnimation透 ...

  4. 动画(Animation) 、 高级动画(Core Animation)

    1 演示UIImage制作的动画 1.1 问题 UIImage动画是IOS提供的最基本的动画,通常用于制作一些小型的动画,本案例使用UIImage制作一个小狗跑动的动画,如图-1所示: 图-1 1.2 ...

  5. 【Android UI设计和开发】动画(Animation)详细说明(一)

    Android开发之动画效果浅析 请尊重他人的劳动成果.转载请注明出处:Android开发之动画效果浅析 程序执行效果图: Android动画主要包括补间动画(Tween)View Animation ...

  6. Android 动画——属性动画Property Animation

    Android在3.0之前只提供了两种动画:View Animation .Drawable Animation .也就是我们在<Android 动画——Frame Animation与Twee ...

  7. .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 ...

  8. iOS 核心动画 Core Animation浅谈

    代码地址如下:http://www.demodashi.com/demo/11603.html 前记 关于实现一个iOS动画,如果简单的,我们可以直接调用UIView的代码块来实现,虽然使用UIVie ...

  9. android 补间动画和Animation

    介绍: 补间动画是一种设定动画开始状态.结束状态,其中间的变化由系统计算补充.这也是他叫做补间动画的原因. 补间动画由Animation类来实现具体效果,包括平移(TranslateAnimation ...

随机推荐

  1. echarts Y轴的刻度 跟数据对应---tooltip-formatter

    var xAxisData = ['2018-01', '2018-02', '2018-03', '2018-04', '2018-05', '2018-06', '2018-07', '2018- ...

  2. windows 设置开机启动,启动项

    第一步ctrl+R输入以下任意一个 方法一: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 方法二: shell:start ...

  3. sqlldr的用法 (这个最完整)

    转自:http://blog.chinaunix.net/uid-23622436-id-2394093.html 一:在 Oracle 数据库中,我们通常在不同数据库的表间记录进行复制或迁移时会用以 ...

  4. JavaSE| String常用方法

    字符串 * java.lang.String类型:字符串类型 * 1.String类型是final修饰,不能被继承的 * 2.Java 程序中的所有字符串字面值(如 "abc" ) ...

  5. 最长不下降子序列nlogn

    b[i]表示长度为i的最长不下降子序列的最小末尾元素的值显然它是单调递增的,满足二分性质,然后就可以愉快地二分啦. #include<iostream> #include<cstdi ...

  6. visual studio 加入zen-codding

    大家都知道zen codding的强大之处大家都知道了,那如何让visual studio也支持呢,直接下载插件安装即可: 插件下载地址:zen-codding for visual studio下载 ...

  7. css3组件之几何图形

    一.平行四边形 1.实心无边框 #parallelogram { width: 150px; height: 100px; -webkit-transform: skew(20deg); -moz-t ...

  8. Java性能调优zz

    写Java也有n年了,现在还是有不少的坏的代码习惯,也通过学习别人的代码学到了不少好的习惯.这篇文章主要是整理的资料.留给自己做个警戒,提示以后写代码的时候注意!在文章的后面,会提供整理的原材料下载. ...

  9. 关于sql server的一种简单用法——在上面写查询语句,即可在下面修改数据

    选择数据库中的表,右键单击-->编辑前200行,然后在显示的页面中选择带SQL标志的图标

  10. 2159 ACM 杭电 杀怪 二维费用的背包+完全背包问题

    题意:已知经验值,保留的忍耐度,怪的种数和最多的杀怪数.求进入下一级的最优方案. 思路:用二维费用的背包+完全背包问题 (顺序循环)方法求解 什么是二维费用的背包问题? 问题: 二维费用的背包问题是指 ...