【前端優化】動畫幾種實現方式總結和性能分析

備註:沒整理格式,抱歉javascript

動畫實現的幾種方式:性能排序
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單線程工做機制是:當線程中沒有執行任何同步代碼的前提下才會執行異步代碼css

var t = true;
window.setTimeout(function (){
    t = false;},1000);
while (t){}
alert('end')

JavaScript引擎是單線程運行的,瀏覽器只有一個線程在運行JavaScript程序
1.3 瀏覽器工做基本原理
1、瀏覽器的內核是多線程的,內核制控下保持同步,
至少實現三個常駐線程:javascript引擎線程,GUI渲染線程,瀏覽器事件觸發線程(http請求線程等)前端

  1. javascript引擎是基於事件驅動單線程執行的,JS引擎一直等待着任務隊列中任務的到來,而後加以處理,瀏覽器不管何時都只有一個JS線程在運行JS程序。
  2. GUI渲染線程負責渲染瀏覽器界面,當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行。

但須要注意 GUI渲染線程與JS引擎是互斥的,當JS引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JS引擎空閒時當即被執行。java

  1. 事件觸發線程,當一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理。異步事件:如setTimeOut、瀏覽器內核的其餘線程如鼠標點擊、AJAX異步請求等,(當線程中沒有執行任何同步代碼的前提下才會執行異步代碼)

也就是說即便setTimeout爲0,他也是等js引擎的代碼執行完以後纔會插入到js引擎線程的最後執行。
1.4 JavaScript中任務,一種是同步任務,一種是異步任務。
同步任務:各個任務按照文檔定義的順序一一推入"執行棧"中,當前一個任務執行完畢,纔會開始執行下一個任務。
異步任務:各個任務推入"任務隊列"中,只有在當前的全部同步任務執行完畢,纔會將隊列中的任務"出隊"執行。(注:這裏的異步任務並不必定是按照文檔定義的順序推入隊列中)
//只有用戶觸發點擊事件纔會被推入隊列中(若是點擊時間小於定時器指定的時間,則先於定時器推入,不然反之)
1.5 "任務隊列是什麼?異步任務一般包括哪些?"
任務隊列(event loop):你可理解爲用於存放事件的隊列,當執行一個異步任務時,就至關於執行任務的回調函數。
一般io(ajax獲取服務器數據)、用戶/瀏覽器自執行事件(onclick、onload、onkeyup等等)以及定時器(setTimeout、setInterval)均可以算做異步操做。css3

先來看一段代碼來理解一下git

console.log("1");
setTimeout(function(){
console.log("2");
},1000);
console.log("3");
setTimeout(function(){
console.log("4");
},0);
輸出結果: 1->3->4->2.

那麼在來看你這段代碼。github

var t = true;
window.setTimeout(function (){
t = false
},1000);
while (t){}
alert('end');
1.6 setTimeOut的討論

參數
描述
code
必需。要調用的函數後要執行的 JavaScript 代碼串。
millisec
必需。在執行代碼前需等待的毫秒數。web

提示: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的區別ajax

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-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%的狀態開始執行
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 – 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...

相關文章
相關標籤/搜索