相關概念:繪製頻率、屏幕刷新頻率、硬件加速、60fpsjavascript
繪製頻率:css
頁面上每一幀變化都是系統繪製出來的(GPU或者CPU)【參考瀏覽器渲染原理】。但這種繪製又和PC遊戲的繪製不一樣,它的最高繪製頻率受限於顯示器的刷新頻率(而非顯卡),因此大多數狀況下最高的繪製頻率只能是每秒60幀(frame per second,如下用fps簡稱),對應於顯示器的60Hz。60fps是一個最理想的狀態,在平常對頁面性能的測試中,60fps也是一個重要的指標,the closer the better。html
刷新頻率:java
圖像在屏幕上更新的速度,也即屏幕上的圖像每秒鐘出現的次數,它的單位是赫茲(Hz)。刷新頻率越高,屏幕上圖像閃爍感就越小,穩定性也就越高,換言之對視力的保護也越好。通常人的眼睛、不容易察覺75Hz以上刷新頻率帶來的閃爍感,所以最好能將您顯示卡刷新頻率調到75Hz以上。要注意的是,並非全部的顯示卡都可以在最大分辨率下達到70Hz以上的刷新頻率(這個性能取決於顯示卡上RAMDAC的速度),並且顯示器也可能由於帶寬不夠而不能達到要求。影響刷新率最主要的仍是顯示器的帶寬。jquery
顯示器帶寬是顯示器視頻放大器通頻帶寬度的簡稱,指電子槍每秒鐘在屏幕上掃過的最大總像素數,以MHz(兆赫茲)爲單位。 帶寬的值越大,顯示器性能越好。git
硬件加速:github
硬件有三個處理器,CPU、GPU和APU(不是加速處理器是聲音處理器)。他們經過PCI/AGP/PCIE總線交換數據。今天,GPU已經再也不侷限於3D圖形處理了,GPU通用計算技術發展已經引發業界很多的關注,事實也證實在浮點運算、並行計算等部分計算方面,GPU能夠提供數十倍乃至於上百倍於CPU的性能。web
60Hz和60fps是什麼關係編程
沒有任何關係。fps表明GPU渲染畫面的頻率,Hz表明顯示器刷新屏幕的頻率。一幅靜態圖片,你能夠說這副圖片的fps是0幀/秒,但絕對不能說此時屏幕的刷新率是0Hz,也就是說刷新率不隨圖像內容的變化而變化。遊戲也好瀏覽器也好,咱們談到掉幀,是指GPU渲染畫面頻率下降。好比跌落到30fps甚至20fps,但由於視覺暫留原理,咱們看到的畫面仍然是運動和連貫的。canvas
_PS: 如下示例在Chrome環境中運行_
實例:
<style type="text/css"> .animate { width: 200px; height: 200px; margin: 0 auto; border-radius: 50%; background-color: #f00; line-height: 200px; border-radius: 50%; text-align: center; color: #fff; font-size: 20px; } .animate-transition { transition : transform 2s linear; -moz-transition : -moz-transform 2s linear; -webkit-transition : -webkit-transform 2s linear; -o-transition : -o-transform 2s linear; } .animate-transition:hover { cursor: pointer; transform : rotate(360deg); -moz-transform : rotate(360deg); -webkit-transform : rotate(360deg); -o-transform : rotate(360deg); } </style> <div class="animate animate-transition">Transition Animation</div>
Keyframes animation經過定義多個關鍵幀以及定義每一個關鍵幀中的元素的屬性值來實現更爲複雜的動畫效果。
實例:
<style type="text/css"> .animate-keyframes { -webkit-animation: frames 2s linear infinite; } .animate-keyframes:hover { cursor: pointer; -webkit-animation: none; } @-webkit-keyframes frames { 0% { background-color: #f00; -webkit-transform: rotate(0deg); } 100% { background-color: #f00; -webkit-transform: rotate(360deg); } } </style> <div class="animate animate-keyframes">keyframes animation</div>
優勢:
簡單、高效
聲明式的
不依賴與主線程,採用硬件加速(GPU)
簡單的控制keyframe animation 播放和暫停
缺點:
不能動態的修改或定義動畫內容
不一樣的動畫沒法實現同步
多個動畫彼此沒法堆疊
另:
1) CSS3 transition強制硬件加速會加大GPU消耗,高負荷情形下將致使運行不流暢。這種狀況在移動設備上尤其明顯。(特殊狀況下,好比當數據在瀏覽器主線程和排版線程之間傳遞產生的瓶頸也會致使不流暢)。某些CSS屬性,好比transform和opacity,則不受這些瓶頸影響。Adobe在這裏精心總結了這些問題。詳細請戳
transition的兼容性問題是個詬病,IE10+及現代瀏覽器,使用起來會形成不少不便。
因爲transition並非由JavaScript原生控制(而僅僅是由JavaScript觸發),瀏覽器沒法獲知如何與控制這些transition的JavaScript代碼同步地優化他們。
2) keyframes animation 的動畫曲線會應用到全部變化的屬性上,並且手寫比較複雜的動畫,寫起來就是噩夢。
<div class="animate-svg"> <svg id="svgAnimation" ns="http://www.w3.org/2000/svg" version="1.1" width="200" height="200"> <g transform="translate(100,100)"> <g> <rect width="200" height="200" rx="100" ry="100" fill="red" transform="translate(-100,-100)"></rect> <text x="-60" y="-0" font-size="20" fill="white" >SVG Animation</text> <!-- Add ease-in-out and infinite iterations to this animation and the code --> <animateTransform attributeName="transform" attributeType="xml" type="rotate" from="0" to="360" dur="3s" repeatCount="indefinite">SVG Animation</animateTransform> </g> </g> </svg> </div>
優勢:
1) 矢量圖形,不受像素影響——SVG的這個特性使得它在不一樣的平臺或者媒體下表現良好,不管屏幕分辨率如何
2) SVG對動畫的支持較好,其DOM結構能夠被其特定語法或者Javascript控制,從而輕鬆的實現動畫
3) Javascript能夠徹底控制SVG Dom 元素
4) SVG的結構是XML,其可訪問性(盲文、聲音朗讀等)、可操做性、可編程性、可被CSS樣式化完勝Canvas。另外,其支持 ARIA 屬性,使其如虎添翼。
缺點:
1) DOM比正常的圖形慢,並且若是其結點多而雜,就更慢。
2) SVG 畫點報表什麼的,還行;在網頁遊戲前,就一籌莫展了;固然能夠結合 Canvas + SVG實現。
3) 不能動態的修改動畫內容
4) 不能與HTML內容集成
5) 整個SVG做爲一個動畫
6) 瀏覽器兼容性問題,IE8-以及Android 2.3默認瀏覽器是不支持SVG
jQuery動畫使用setInterval實現:
// 用例 $("#div").animate({}); // 源碼 jQuery.fx.timer = function( timer ) { if ( timer() && jQuery.timers.push( timer ) && !timerId ) { timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); } }; jQuery.fx.interval = 13;
How to determine the best 「framerate」 (setInterval delay) to use in a JavaScript animation loop?
優勢:
易用,低效,兼容好;
缺點:
1) setInterval多個間隔可能會被跳過
2) setInterval多個間隔可能比預期小
3) 不一樣瀏覽器的精度量級不一樣:
4) jQuery 沒法解決頻繁觸發 Layout 致使的抽動。
實例:
<style type="text/css"> .animate-RAF { } .animate-input { margin-top: 10px; text-align: center; } </style> <div id="animate-RAF" class="animate animate-RAF">RAF Animation</div> <div class="animate-input"><input type="button" id="btn_start" value="Start" style="width:100px;height:30px"></div> <script type="text/javascript"> var animate_raf = document.getElementById("animate-RAF"), btn_start = document.getElementById("btn_start"), frameid = null; function frame(time) { animate_raf.style['-webkit-transform'] = 'rotate('+ Math.cos(time/1000)*360 +'deg)'; frameid = requestAnimationFrame(frame); } // bind Event btn_start.addEventListener("click", function(event) { var val = this.value; if(val === "Start") { frameid = requestAnimationFrame(frame); this.value = "Pause"; }else { this.value = "Start"; cancelAnimationFrame(frameid); } }, false); </script>
優勢:
1) 在每次瀏覽器更新頁面時,能獲取通知並執行應用。 簡單理解爲,RAF能在每一個16.7ms間執行一次我們的函數,很少很多。
2) 最小化的消耗資源,RAF在頁面被切換或瀏覽器最小化時,會暫停執行,等頁面再次關注時,繼續執行動畫。
3) 相比 CSS 動畫有更好的掌控,能合理下降CPU的使用。
缺點:
1) 沒法控制執行時間,執行時間由系統根據屏幕刷新時間決定
2) 瀏覽器兼容性問題,IE10+及現代瀏覽器,低版本瀏覽器建議降級處理,使用setInterval或setTimeout
優勢:
1) 畫2D圖形時,頁面渲染性能比較高
2) 頁面渲染性能受圖形複雜度影響小
3) 渲染性能只受圖形的分辨率的影響
4) 畫出來的圖形能夠直接保存爲 .png 或者 .jpg的圖形
5) 最適合於畫光柵圖像(如遊戲和不規則幾何圖形等),編輯圖片還有其餘基於像素的圖形操做。
缺點:
1) 整個就是一張圖,不管你往上畫什麼東西——沒有DOM 結點可供操做
2) 沒有實現動畫的API,你必須依靠定時器和其餘事件來更新Canvas
3) 對文本的渲染支持是比較差
4) 對要求有高可訪問性(盲文、聲音朗讀等)頁面,比較困難
5) 對交互要求高的(好比TIBCO的不少產品)的界面,不建議使用Canvas
WebGL是一種3D繪圖標準,這種繪圖技術標準容許把JavaScript和OpenGL ES 2.0結合在一塊兒,經過增長OpenGL ES 2.0的一個JavaScript綁定,WebGL能夠爲HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就能夠藉助系統顯卡來在瀏覽器裏更流暢地展現3D場景和模型了,還能建立複雜的導航和數據視覺化。顯然,WebGL技術標準免去了開發網頁專用渲染插件的麻煩,可被用於建立具備複雜3D結構的網站頁面,甚至能夠用來設計3D網頁遊戲等等。
瀏覽器支持:
Internet Explorer 11+
Google Chrome 9+
Firefox 4+
Opera 12+
Safari 5.1+
1) 不一樣的api有不一樣的模型動畫
2) 沒必要要的維護來支持多種方式實現一樣的事情
3) Web開發人員須要學習多種實現技術
4) Javascript不容易設置聲明式動畫
2) JavaScript implementation of the Web Animations API
Web Animations API爲CSS和SVG動畫提供了單一接口。旨在經過提供更好的性能、更好的控制時間線和播放、靈活、統一的Javascript編程接口,使作一些事情更容易。
當前狀態:
Specification at First Public Working Draft: www.w3.org/TR/web-animations
Chrome:
CSS Transitions & Animations rewritten on top of the Web Animations model
JavaScript API in development behind a flag
Firefox & Safari: Started implementation
IE: No public signals
實例一:A simple example
var web_animation_1 = document.getElementById("web_animation_1"); web_animation_1.addEventListener('click', function() { web_animation_1.animate([{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }],{ duration: 2 }); }, false);
實例二:More complex timing
var web_animation_2 = document.getElementById("web_animation_2"); web_animation_2.addEventListener('click', function() { web_animation_2.animate([{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }],{ direction: 'alternate', duration: 1, iterations: Infinity, easing: 'ease-in-out', playbackRate: 2 }); }, false);
實例三:Without the syntactic sugar
var web_animation_3 = document.getElementById("web_animation_3"); web_animation_3.addEventListener('click', function() { var obj = new Animation(web_animation_3,[{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }],{ duration: 2 }); document.timeline.play(obj); }, false);
實例四:Parallel animation grouping
var web_animation_4 = document.getElementById("web_animation_4"), parItem1 = document.getElementById("parItem1"), parItem2 = document.getElementById("parItem2"), parItem3 = document.getElementById("parItem3"); web_animation_4.addEventListener('click', function() { var obj = new ParGroup([ new Animation(parItem1, [{width: '0px'}, {width: '500px'}], 1), new Animation(parItem2, [{width: '0px'}, {width: '700px'}], 1), new Animation(parItem3, [{width: '0px'}, {width: '200px'}], 1), ]) document.timeline.play(obj); }, false);
實例五:Sequential animation grouping
var web_animation_5 = document.getElementById("web_animation_5"), seqItem1 = document.getElementById("seqItem1"), seqItem2 = document.getElementById("seqItem2"), seqItem3 = document.getElementById("seqItem3"); web_animation_5.addEventListener('click', function() { var obj = new SeqGroup([ new Animation(seqItem1, [{width: '0px'}, {width: '200px'}], 1), new Animation(seqItem2, [{width: '0px'}, {width: '300px'}], 1), new Animation(seqItem3, [{width: '0px'}, {width: '200px'}], 1), ]) document.timeline.play(obj); }, false);
實例六:Nested grouped animations
var web_animation_6 = document.getElementById("web_animation_6"), outerSeqItem1 = document.getElementById("outerSeqItem1"), outerSeqItem2 = document.getElementById("outerSeqItem2"), innerParItem1 = document.getElementById("innerParItem1"), innerParItem2 = document.getElementById("innerParItem2"), innerParItem3 = document.getElementById("innerParItem3"); web_animation_6.addEventListener('click', function() { var parobj = new ParGroup([ new Animation(innerParItem1, [{width: '0px'}, {width: '300px'}], 1), new Animation(innerParItem2, [{width: '0px'}, {width: '300px'}], 1), new Animation(innerParItem3, [{width: '0px'}, {width: '300px'}], 1), ]); var seqobj = new SeqGroup([ new Animation(outerSeqItem1, [{width: '0px'}, {width: '200px'}], 1), parobj, new Animation(outerSeqItem2, [{width: '0px'}, {width: '200px'}], 1), ]); document.timeline.play(seqobj); }, false);
實例七:Path animations
var web_animation_7 = document.getElementById("web_animation_7"); web_animation_7.addEventListener('click', function() { var obj = new Animation(web_animation_7, new PathAnimationEffect( 'M 100 200 ' + 'C 200 100 300 0 400 100 ' + 'C 500 200 600 300 700 200 ' + 'C 800 100 900 100 900 100', 'auto-rotate'), { duration: 2, direction: 'alternate', easing: 'ease-in-out', iterations: Infinity, }); document.timeline.play(obj); }, false);
實例八:Custom animation effects
function customAnimationEffect(timeFraction, iteration, target) { web_animation_8.innerHTML = 'timeFraction: ' + timeFraction.toFixed(2) + '\n' + 'iteration: ' + iteration; } var web_animation_8 = document.getElementById("web_animation_8"); var obj = new Animation(null, {sample: customAnimationEffect}, { duration: 2, direction: 'alternate', easing: 'ease-in-out', iterations: Infinity, }); var customPlayer = document.timeline.play(obj); window.addEventListener('slideenter', function(event) { if (event.slide == customSlide) { customPlayer.currentTime = 0; } }, false);
實例九:綜合實例
(function(document) { 'use strict'; var Animations = {}, player, controls = document.getElementById('animate-controls'); Animations.targets = { path: document.getElementById('path'), ballContainer: document.getElementById('animate-ball-container'), ball: document.getElementById('animate-ball') }; Animations.keyframeMove = new Animation(Animations.targets.ballContainer, [{ offset: 0, transform: 'translate(0,0)' }, { offset: 1, transform: 'translate(600,0)' }], { duration: 2000 }); Animations.keyframeSpinRoll = new Animation(Animations.targets.ball, [{ transform: 'rotate(950deg)' }], { duration: 2000 }); Animations.motionpathBounce = new Animation(Animations.targets.ballContainer, new MotionPathEffect("M25,25 " + "a150,100 0 0,1 300,0 " + "a75,50 0 0,1 150,0 " + "a35,20 0 0,1 70,0 " + "a2,1 0 0,1 35,0 " + "h45"), { duration: 2500 }); Animations.keyframeSpinBounce = new Animation(Animations.targets.ball, [{ transform: 'rotate(950deg)' }], { duration: 2500 }); Animations.animationGroupRoll = new AnimationGroup([Animations.keyframeMove, Animations.keyframeSpinRoll], { easing: 'ease-out' }); Animations.animationGroupBounce = new AnimationGroup([Animations.motionpathBounce, Animations.keyframeSpinBounce], { easing: 'ease-out' }); controls.addEventListener('click', function(event) { if (event.target) { var targetElement = event.target; switch (targetElement.id) { case 'keyframe-start': player = document.timeline.play(Animations.animationGroupRoll); break; case 'motionpath-start': player = document.timeline.play(Animations.animationGroupBounce); break; case 'pause': player.pause(); break; case 'cancel': player.cancel(); break; case 'play': player.play(); break; case 'reverse': player.reverse() } } }) })(document);
1) 頁面加強動畫建議使用CSS動畫
2) 複雜動畫交互建議使用RAF及setInterval 或setTimeout優雅降級處理
(function() { var lastTime = 0, vendors = ['webkit', 'moz']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(), timeToCall = Math.max(0, 16.6 - (currTime - lastTime) ), id = window.setTimeout(function() { callback(currTime + timeToCall); },timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } })();
3) 推薦動畫庫Velocity.js、GreenSock:
1) Velocity.js是一款動畫切換的jQuery插件,它從新實現了jQuery的$.animate()方法從而加快動畫切換的速度。Velocity.js只有7k的大小,它不只包含了$.animate()的全部功能,而且還包含了顏色切換、轉換(transform)、循環、緩動、CSS切換、Scroll功能,它是jQuery、 jQuery UI、CSS變換 在動畫方面的最佳組合。
Velocity.js支持IE8+、Chrome、Firefox等瀏覽器,並支持Andriod以及IOS。
Velocity.js在內部實現中使用了jQuery的$.queue()方法,所以它比 jQuery的$.animate()、$.fade()、$.delay()方法更加流暢,其性能也高於CSS的animation屬性。
2) GreenSock:GSAP v12平臺
很是快的速度:性能是很是重要的,尤爲是在移動設備上。GSAP不斷優化,以保證互動項目的快速響應、高效率及平滑,你能夠從這裏查看動畫效果測試。
異想天開的強勁:內置衆多引擎的功能,如動畫色彩、貝塞爾曲線、CSS樣式屬性、Flash濾鏡、數組等等,定義不一樣的回調,能夠經過幀或者秒定義運動。
兼容性:Flash,HTML5,jQuery,Canvas,CSS,新瀏覽器,舊瀏覽器,RequireJS,EaseIJS,移動設備等等-GSAP均可以很好的與他們兼容,你能夠選擇你熟悉的工具來使用。
Javascript,AS3/AS2:選擇適合你的語言來完成動畫。
輕量與可擴展性:模塊化與插件式的結構保持了核心引擎的輕量,TweenLite包很是小(基本上低於7kb)。
沒有依賴:GSAP沒有基於第三方工具來構建(雖然它將jQuery做爲選擇器),所以能保證最短的加載時間與最大化性能。
高等序列:不用受限於線性序列,能夠重疊動畫序列,你能夠經過精確時間控制,靈活地使用最少的代碼實現動畫。
良好的技術支持:能夠經過論壇反饋,會有專家和資深活躍用戶回答問題。
任何對象均可以實現動畫:是的,任何,不用預約義的屬性,任何對象的任意數字屬性均可以實現動畫,若是這些屬性(如顏色,濾鏡,非數值屬性等)須要處理,插件能夠實現。若是沒有,咱們能夠實現一個。
重寫管理:GSAP幫助防止動畫引擎的衝突以及高級選項的設置。
易於學習:文檔、教程、 示例、學習指南、論壇,還有不少學習資源,很是地豐富。
許可證:除商業用途意外,GSAP徹底免費。
CSS Will Change Module Level 1
CSS Will Change Module Level 1 (中文版本)
CSS vs. JS Animation: Which is Faster?
CSS animations and transitions performance: looking inside the browser
SVG or Canvas? СHoosing Between the Two
Exploring the Web Animations API
Everything You Need to Know About the CSS will-change Property
The (80-storey) Elevator Pitch