【前端优化】动画几种实现方式总结和性能分析
栏目: 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请求线程等)
- javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
- GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
- 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待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()时,仅当没有该定时器的其他代码实例时才将定时器代码插入队列。这样确保了定时器代码加入到队列的最小时间间隔为指定间隔
- 某些间隔会被跳过
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:
- 统一的向下兼容策略 IE8, IE9之流
- CSS3动画不能应用所有属性 scrollTop值。如果我们希望返回顶部是个平滑滚动效果
- 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...以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 验证码前端性能分析及优化实践
- Keepfast v0.2.1 发布,前端性能分析工具
- 用 dotTrace 进行性能分析时,各种不同性能分析选项的含义和用途
- Golang 性能测试 (2) 性能分析
- 页面渲染:性能分析
- 性能分析方法论
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Uberland
Alex Rosenblat / University of California Press / 2018-11-19 / GBP 21.00
Silicon Valley technology is transforming the way we work, and Uber is leading the charge. An American startup that promised to deliver entrepreneurship for the masses through its technology, Uber ins......一起来看看 《Uberland》 这本书的介绍吧!