乐趣区

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

备注:没整理格式,抱歉
动画实现的几种方式:性能排序 js < requestAnimationFrame <css3< Canvasjs 实现方式:1.setTimeout 自身调用 eg12.setInterval 调用 eg2setTimeout 的定时器值推荐最小使用 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 线程空闲的时候才会真正执行 ep3millisec 参数有什么用?那么问题来了。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.png5 处,创建一个定时器 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-animatable-properties/
http://leaverou.github.io/animatable/
(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% 的状态开始执行 reversealternate-reverseanimation-fill-modeforwards 让动画保持在结束状态 none:默认值,回到动画没开始时的状态。backwards:让动画回到第一帧的状态。both: 根据 animation-direction(见后)轮流应用 forwards 和 backwards 规则。animation-play-statepausedrunning 动画播放过程中,会突然停止。这时,默认行为是跳回到动画的开始状态,想让动画保持突然终止时的状态,就要使用 animation-play-state 属性大前端团队 > 前端动画实现 > image2017-11-28 14:29:8.pnganimation 也是一个简写形式
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 的 stepeg: http://dabblet.com/gist/1745856animation-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 – st^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…

退出移动版