【前端优化】动画几种实现方式总结和性能分析

栏目: JavaScript · 发布时间: 5年前

内容简介:动画实现的几种方式:性能排序js < requestAnimationFrame <css3< Canvasjs实现方式:

动画实现的几种方式:性能排序

js < requestAnimationFrame <css3< Canvas

js实现方式:

1.setTimeout 自身调用 eg1

2.setInterval 调用 eg2

setTimeout的定时器值推荐最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60帧)

为什么倒计时动画一定要用setTimeout而避免使用setInterval-------两者区别及setTimeout引发的js线程讨论

1.js线程讨论

1.1 为什么:单线程是JavaScript的一大特性。

JavaScript是浏览器用来与用户进行交互、进行DOM操作的,这也使得了它必须是单线程这一特性。比如你去修改一个元素的DOM,同时又去删除这个元素,那么浏览器应该听谁的?

1.2 js单线程工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码

var t = true;

window.setTimeout(function (){

t = false;},1000);

while (t){}

alert('end')

JavaScript引擎是单线程运行的,浏览器只有一个线程在运行JavaScript程序

1.3 浏览器工作基本原理

一、浏览器的内核是多线程的,内核制控下保持同步,

至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程(http请求线程等)

  1. javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
  2. GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。

但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

  1. 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。异步事件:如setTimeOut、浏览器内核的其他线程如鼠标点击、AJAX异步请求等,(当线程中没有执行任何同步代码的前提下才会执行异步代码)

也就是说即使setTimeout为0,他也是等js引擎的代码执行完之后才会插入到js引擎线程的最后执行。

1.4 JavaScript中任务,一种是同步任务,一种是异步任务。

同步任务:各个任务按照文档定义的顺序一一推入"执行栈"中,当前一个任务执行完毕,才会开始执行下一个任务。

异步任务:各个任务推入"任务队列"中,只有在当前的所有同步任务执行完毕,才会将队列中的任务"出队"执行。(注:这里的异步任务并不一定是按照文档定义的顺序推入队列中)

//只有用户触发点击事件才会被推入队列中(如果点击时间小于定时器指定的时间,则先于定时器推入,否则反之)

1.5 "任务队列是什么?异步任务通常包括哪些?"

任务队列(event loop):你可理解为用于存放事件的队列,当执行一个异步任务时,就相当于执行任务的回调函数。

通常io(ajax获取服务器数据)、用户/浏览器自执行事件(onclick、onload、onkeyup等等)以及定时器(setTimeout、setInterval)都可以算作异步操作。

先来看一段代码来理解一下

console.log("1");

setTimeout(function(){

console.log("2");

},1000);

console.log("3");

setTimeout(function(){

console.log("4");

},0);

输出结果: 1->3->4->2.

那么在来看你这段代码。

var t = true;

window.setTimeout(function (){

t = false

},1000);

while (t){}

alert('end');

1.6 setTimeOut的讨论

参数

描述

code

必需。要调用的函数后要执行的 JavaScript 代码串。

millisec

必需。在执行代码前需等待的毫秒数。

提示:setTimeout() 只执行 code 一次。如果要多次调用,请使用 setInterval() 或者让 code 自身再次

原理:setTimeout调用的时候,JavaScript引擎会启动定时器timer,当定时器时间到,就把该事件放到主事件队列等待处理。

注意:浏览器JavaScript线程空闲的时候才会真正执行 ep3

millisec参数有什么用?

那么问题来了。setTimeout(handler,0)和setTimeout(handler,100)在单独使用时,好像并没有区别。(中间执行的代码处理时间超过100ms时)

millisec一般在多个setTimeout一起使用的时,需要区分哪个先加入到队列的时候才有用,否则都可以设置成setTimeout(handler,0)

1.7 SetTimeout 与 setInterval的区别

setTimeout(function(){

/ 代码块... /

setTimeout(arguments.callee, 10);

}, 10);

setInterval(function(){

/ 代码块... /

}, 10);

setTimeout递归执行的代码必须是上一次执行完了并间格一定时间才再次执行

比仿说: setTimeout延迟时间为1秒执行, 要执行的代码需要2秒来执行,那这段代码上一次与下一次的执行时间为3秒. 而不是我们想象的每1秒执行一次.

setInterval是排队执行的

比仿说: setInterval每次执行时间为1秒,而执行的代码需要2秒执行, 那它还是每次去执行这段代码, 上次还没执行完的代码会排队, 上一次执行完下一次的就立即执行, 这样实际执行的间隔时间为2秒

这样的话在我看来, 如果setInterval执行的代码时间长度比每次执行的间隔段的话,就没有意义,并且队伍越来越长,内存就被吃光了.如果某一次执行被卡住了,那程序就会被堵死

巨坑无比的setInterval

定时器的代码可能在代码还没有执行完成再次被添加到队列,结果导致循环内的判断条件不准确,代码多执行几次,之间没有停顿。

JavaScript已经解决这个问题,当使用setInterval()时,仅当没有该定时器的其他代码实例时才将定时器代码插入队列。这样确保了定时器代码加入到队列的最小时间间隔为指定间隔

  1. 某些间隔会被跳过

2.多个定时器的代码执行之间的间隔可能比预期要小

大前端团队 > 前端动画实现 > image2017-11-28 14:24:25.png

5处,创建一个定时器

205处,添加一个定时器,但是onclick代码没执行完成,等待

300处,onclick代码执行完毕,执行第一个定时器

405处,添加第二个定时器,但前一个定时器没有执行完成,等待

605处,本来是要添加第三个定时器,但是此时发现,队列中有了一个定时器,被跳过

等到第一个定时器代码执行完毕,马上执行第二个定时器,所以间隔会比预期的小。

二 CSS3动画

1.tansition

transition-property 要运动的样式 (all || [attr] || none)

transition-duration 运动时间

transition-delay 延迟时间

transition-timing-function 运动形式

ease:(逐渐变慢)默认值

linear:(匀速)

ease-in:(加速)

ease-out:(减速)

ease-in-out:(先加速后减速)

cubic-bezier 贝塞尔曲线( x1, y1, x2, y2 ) http://matthewlein.com/ceaser/

transition的完整写法如下 img{ transition: 1s 1s height ease; }

单独定义成各个属性。 img{ transition-property: height; transition-duration: 1s; transition-delay: 1s; transition-timing-function: ease; }

/ 可以多个动画同时运动 /用逗号隔开

transition:1s width,2s height,3s background;

/ 可以在动画完成时间之后添加动画延迟执行的时间 /

transition:1s width,2s 1s height,3s 3s background;

过渡完成事件

Webkit内核: obj.addEventListener('webkitTransitionEnd',function(){},false);

firefox: obj.addEventListener('transitionend',function(){},false);

/ tansition动画发生在样式改变的时候 /

function addEnd(obj,fn) ---封装适应与各个浏览器的动画结束

{

//动画执行完执行该函数
obj.addEventListener('WebkitTransitionEnd',fn,false);
obj.addEventListener('transitionend',fn,false); //标准

}

addEnd(oBox,function(){

alert("end");

});

// 面临两个bug:1.tansition中有多个动画时,每个执行完,都会有一个结束弹出

2.发生重复调用的情况--需要移除

//移除动画执行完的操作

function removeEnd(obj,fn)

}

obj.removeEventListener('transitionend',fn,false);
obj.removeEventListener('WebkitTransitionEnd',fn,false);

{

使用注意

(1)不是所有的CSS属性都支持transition

http://oli.jp/2010/css-animat...

http://leaverou.github.io/ani...

(2)transition需要明确知道,开始状态和结束状态的具体数值,才能计算出中间状态

transition的局限

transition的优点在于简单易用,但是它有几个很大的局限。

(1)transition需要事件触发,所以没法在网页加载时自动发生。

(2)transition是一次性的,不能重复发生,除非一再触发。

(3)transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态。

(4)一条transition规则,只能定义一个属性的变化,不能涉及多个属性。

CSS Animation就是为了解决这些问题而提出的。

2.transform

rotate() 旋转函数 取值度数 deg 度数 -origin 旋转的基点

skew() 倾斜函数 取值度数

skewX()

skewY()

scale() 缩放函数 取值 正数、负数和小数

scaleX()

scaleY()

translate() 位移函数

translateX()

translateY()

Transform 执行顺序问题 — 后写先执行

-webkit-transform:rotate(360deg);

旋转原点可以是关键字+像素位置:相对于左上角作为零点:正为下,右

-webkit-transform-origin:right bottom;

-webkit-transform-origin:200px 200px;

一个transform可以有多个值:

-webkit-transform:rotate(360deg) scale(0.2);

-webkit-transform:skewX(45deg);

-webkit-transform:skewY(45deg);

-webkit-transform:skew(15deg,30deg);

3.Animation 关键帧——keyFrames

只需指明两个状态,之间的过程由计算机自动计算

关键帧的时间单位

数字:0%、25%、100%等

字符:from(0%)、to(100%)

格式

@keyframes 动画名称

{

动画状态

}

@keyframes miaov_test

{

from { background:red; }

to { background:green; }

}

可以只有to

必要属性

animation-name 动画名称(关键帧名称)

animation-duration 动画持续时间

属性:

animation-play-state 播放状态( running 播放 和paused 暂停 )

animation-timing-function 动画运动形式

linear 匀速。

ease 缓冲。

ease-in 由慢到快。

ease-out 由快到慢。

ease-in-out 由慢到快再到慢。

cubic-bezier(number, number, number, number): 特定的贝塞尔曲线类型,4个数值需在[0, 1]区间内

animation-delay 动画延迟只是第一次

animation-iteration-count 重复次数/infinite为无限次

animation-direction 播放前重置/动画是否重置后再开始播放

alternate 动画直接从上一次停止的位置开始执行

normal 动画第二次直接跳到0%的状态开始执行

reverse

alternate-reverse

animation-fill-mode

forwards 让动画保持在结束状态

none:默认值,回到动画没开始时的状态。

backwards:让动画回到第一帧的状态。

both: 根据animation-direction(见后)轮流应用forwards和backwards规则。

animation-play-state

paused

running

动画播放过程中,会突然停止。这时,默认行为是跳回到动画的开始状态,想让动画保持突然终止时的状态,就要使用animation-play-state属性

大前端团队 > 前端动画实现 > image2017-11-28 14:29:8.png

animation也是一个简写形式

div:hover { animation: 1s 1s rainbow linear 3 forwards normal; }

分解成各个单独的属性

div:hover { animation-name: rainbow; animation-duration: 1s; animation-timing-function: linear; animation-delay: 1s; animation-fill-mode:forwards; animation-direction: normal; animation-iteration-count: 3; }

Animation与Js的结合

通过class,在class里加入animation的各种属性

直接给元素加-webkit-animation-xxx样式

animation的问题

写起来麻烦

没法动态改变目标点位置

animation的函数:

obj.addEventListener('webkitAnimationEnd', function (){}, false);

实例1:无缝滚动

animation的step

eg: http://dabblet.com/gist/1745856

animation-timing-function: steps(30, end)

1.什么时候使用:

animation默认以ease方式过渡,它会在每个关键帧之间插入补间动画,所以动画效果是连贯性的,除了ease,linear、cubic-bezier之类的过渡函数都会为其插入补间。但有些效果不需要补间,只需要关键帧之间的跳跃,这时应该使用steps过渡方式

大前端团队 > 前端动画实现 > image2017-11-28 14:29:45.png

线性动画: http://sandbox.runjs.cn/show/...

帧动画: http://sandbox.runjs.cn/show/...

2.step使用:

语法:

steps(number[, end | start])

参数说明:

number参数指定了时间函数中的间隔数量(必须是正整数)

第二个参数是可选的,可设值:start和end,表示在每个间隔的起点或是终点发生阶跃变化,如果忽略,默认是end。

大前端团队 > 前端动画实现 > image2017-11-28 14:30:37.png

横轴表示时间,纵轴表示动画完成度(也就是0%~100%)。

第一个图,steps(1, start)将动画分为1段,跳跃点为start,也就是说动画在每个周期的起点发生阶跃(即图中的空心圆 → 实心圆)。由于只有一段,后续就不再发生动画了。

第二个图,steps(1, end)同样是将动画分为1段,但跳跃点是end,也就是动画在每个周期的终点发生阶跃,也是图中的空心圆 → 实心圆,但注意时间,是在终点才发生动画。

第三个图,steps(3, start)将动画分为三段,跳跃点为start,动画在每个周期的起点发生阶跃(即图中的空心圆 → 实心圆)。在这里,由于动画的第一次阶跃是在第一阶段的起点处(0s),所以我们看到的动画的初始状态其实已经是 1/3 的状态,因此我们看到的动画的过程为 1/3 → 2/3 → 1 。

第四个图,steps(3, end)也是将动画分为三段,但跳跃点为end,动画在每个周期的终点发生阶跃(即图中的空心圆 → 实心圆)。虽然动画的状态最终会到达100%,但是动画已经结束,所以100%的状态是看不到的,因此我们最终看到的动画的过程是0 → 1/3 → 2/3。

https://idiotwu.me/study/timi...

steps第一个参数的错误的理解:

第一个参数 number 为指定的间隔数,即把动画分为 n 步阶段性展示,估计大多数人理解就是keyframes写的变化次数

@-webkit-keyframes circle { 0% {background-position-x: 0;} 100%{background-position-x: -400px;} }

@-webkit-keyframes circle { 0% {} 25%{} 50%{} 75%{} 100%{} }

如果有多个帧动画

@-webkit-keyframes circle { 0% {background-position-x: 0;} 50% {background-position-x: -200px;} 100%{background-position-x: -400px;} }

0-25 之间变化5次, 25-50之间 变化5次 ,50-75 之间变化5次,以此类推

应用:

Sprite 精灵动画 2D游戏

https://idiotwu.me/css3-runni...

4.3D转换

父容器:

transform-style(preserve-3d) 建立3D空间

Perspective 景深

Perspective- origin 景深基点

子元素:

Transform 新增函数

rotateX()

rotateY()

rotateZ()

translateZ()

scaleZ()

实例1:3D盒子

http://beiyuu.com/css3-animation

使用实例:

requestAnimationFrame

是什么

js的一个API

该方法通过在系统准备好绘制动画帧时调用该帧,从而为创建动画网页提供了一种更平滑更高效的方法

使用

var handle = setTimeout(renderLoop, PERIOD);

var handle = window.requestAnimationFrame(renderLoop);

window.cancelAnimationFrame(handle);

为什么出现

css:

  1. 统一的向下兼容策略 IE8, IE9之流
  2. CSS3动画不能应用所有属性 scrollTop值。如果我们希望返回顶部是个平滑滚动效果
  3. CSS3支持的动画效果有限 CSS3动画的贝塞尔曲线是一个标准3次方曲线

缓动(Tween)知识:

Linear:无缓动效果

Quadratic:二次方的缓动(t^2)

Cubic:三次方的缓动(t^3)

Quartic:四次方的缓动(t^4)

Quintic:五次方的缓动(t^5)

Sinusoidal:正弦曲线的缓动(sin(t))

Exponential:指数曲线的缓动(2^t)

Circular:圆形曲线的缓动(sqrt(1-t^2))

Elastic:指数衰减的正弦曲线缓动

超过范围的三次方缓动((s+1) t^3 – s t^2)

指数衰减的反弹缓动

js:

1.延迟时间固定导致了动画过度绘制,浪费 CPU 周期以及消耗额外的电能等问题

2.即使看不到网站,特别是当网站使用背景选项卡中的页面或浏览器已最小化时,动画都会频繁出现

大前端团队 > 前端动画实现 > image2017-11-28 14:31:6.png

相当一部分的浏览器的显示频率是16.7ms

搞个10ms setTimeout,就会是下面一行的模样——每第三个图形都无法绘制

显示器16.7ms刷新间隔之前发生了其他绘制请求(setTimeout),导致所有第三帧丢失,继而导致动画断续显示(堵车的感觉),这就是过度绘制带来的问题

requestAnimationFrame 与setTimeout相似,都是延迟执行,不过更智能,跟着浏览器的绘制走,如果浏览设备绘制间隔是16.7ms,那我就这个间隔绘制;如果浏览设备绘制间隔是10ms, 我就10ms绘制,浏览器(如页面)每次要重绘,就会通知(requestAnimationFrame)

页面最小化了,或者被Tab切换当前页面不可见。页面不会发生重绘

兼容性

Android设备不支持,其他设备基本上跟CSS3动画的支持一模一样

https://developer.mozilla.org...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

CSS商业网站布局之道

CSS商业网站布局之道

朱印宏 / 清华大学出版社 / 2007-1 / 75.00元

本书是一本CSS技术专著。 主要从布局角度全面、系统和深入地讲解CSS在标准网站布局之中的应用。很多读者经过初步的学习之后就能够使用CSS设计出一些漂亮的网页样式,于是便乐在其中,踌躇满志,这是好事,但千万不要自我陶醉,因为你还未领略CSS的博大精深。用CSS容易,难的是全部都用CSS。CSS的精髓是布局,而不是样式,布局是需要缜密的逻辑思维和系统设计的,而样式只需要简单地编写代码或复制即可。本书......一起来看看 《CSS商业网站布局之道》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换