其實這篇文章相似版本早在12年就在網上各處出現了,也隨着HTML5的興起,HTML的新特性也是倍受開發者們追捧,天然相關HTML5的高性能動畫與遊戲的相關文章也是層出不窮的,筆者也是在12年接觸的相關技術,不過俗話說「隔行如隔山」,隨着大前端時代的到來,前端的工做範圍和知識疆界也在不斷的擴展,須要的知識結構和知識體系也在不斷的豐富,最近也基於所在團隊不斷須要有新人有這方面的知識儲備,因而就有了此文。固然本文並不會提供任何一段完整可用的代碼,伸手黨也請耐下心來看吧,理解了原理實現實際上是一件很簡單的事情。css
什麼是動畫?什麼是遊戲?html
如上所述,既然是面對新人的,因此有必要從根源開始講起。首先須要回到「是什麼」的問題?前端
這裏援引某度的定義:html5
「動畫的概念不一樣於通常意義上的動畫片,動畫是一種綜合藝術,它是集合了繪畫、漫畫、電影、數字媒體、攝影、音樂、文學等衆多藝術門類於一身的藝術表現形式。最先發源於19世紀上半葉的英國,興盛於美國,中國動畫起源於20世紀20年代。動畫是一門年青的藝術,它是惟一有肯定誕生日期的一門藝術,1892年10月28日埃米爾·雷諾首次在巴黎著名的葛萊凡蠟像館向觀衆放映光學影戲,標誌着動畫的正式誕生,同時埃米爾·雷諾也被譽爲「動畫之父」。動畫藝術通過了100多年的發展,已經有了較爲完善的理論體系和產業體系,並以其獨特的藝術魅力深受人們的喜好。node
動畫的英文有不少表述,如animation、cartoon、animated cartoon、cameracature。其中較正式的 "Animation" 一詞源自於拉丁文字根anima,意思爲「靈魂」,動詞animate是「賦予生命」的意思,引伸爲使某物活起來的意思。因此動畫能夠定義爲使用繪畫的手法,創造生命運動的藝術。
動畫技術較規範的定義是採用逐幀拍攝對象並連續播放而造成運動的影像技術。不論拍攝對象是什麼,只要它的拍攝方式是採用的逐格方式,觀看時連續播放造成了活動影像,它就是動畫。」
固然筆者還據說過另外種說法動畫起源於我國的連環畫,固然這都是後話了,回到正題,撇開歷史因素和文化因素不談,單就技術實現來講動畫其實就是「採用逐幀拍攝對象並連續播放而造成的影像技術」。以下圖:
在用戶的視界內,從左到右連續定寬展現這種圖片就能給人看上去一種這個「球球」動起來了同樣的感受。
說白了,實際上是利用人類大腦的「腦洞」和眼睛的「視覺暫留」,將本來「連續」的世界抽象成一個「離散」的世界,再使用了一種相似「積分」的方式把每個畫面累積起來,造成了動畫。
這裏還有值得一提的就是「幀(frame)」,相信看電影、玩遊戲的童鞋都對這個幀有過必定的瞭解。通俗的來講咱們更多提到的是「每秒多少幀(FPS)」,好比24幀以上咱們就不會感覺到「卡」,這是由於咱們的眼睛能將咱們看到的畫面暫時保存在腦中約1/24秒,因此只要保證每秒展現24副連續圖片,那麼正常人其實就能感覺到一個連續的影像了。可是反過來,幀數的提升其實並不能改變人眼對圖像的識別機制,因此動畫《進擊的巨人》部分劇集使用了60幀/秒的技術,還有去年剛上映的《比利林恩的中場故事》甚至還有120幀/秒的場次,其實大多數人的眼睛或說腦子實際上是處理不過來的,那麼問題來了,他們這麼作到底有什麼用呢?有興趣的童鞋能夠研究下,因爲離題太遠本文就不展開了。
接下來是遊戲,由於遊戲的涵蓋範圍更廣,這裏就僅僅只對本文中涉及到的遊戲作一個膚淺的解釋:
「凡是可以進行交互的,它們一般還會涉及到動畫。」
固然,這裏剝離了它的最大的價值「帶來快樂」。其實生活在這個互聯網時代,遊戲基本成了生活的一個必備要素之一,或多或少,或深或淺,遊戲都現實的充當着生活的調劑品或者更多的做用,本文便再也不贅述了。
如何實現動畫與遊戲?
接下來咱們來談下「怎麼辦」的問題。動畫的實現方式上文中已經說起了,結合筆者前端的技術背景,在這裏羅列一些大概的動畫實現方式:
非JS的:
CSS3提供給了咱們兩個很是實用的特性,讓咱們可以藉助非JS的方法實現動畫,並且很是的簡單。那麼,首先是
transition,嚴格的來講它並不能算是動畫,至少字面翻譯它應該被稱爲過渡,可是咱們在平常工做中,常用這個屬性來快速實現簡易的動畫效果,這裏附上相關
連接,若是不清楚的童鞋能夠查閱下;另外一種就是
animation,一樣是CSS3的新特性,這個應該是各類意義上真正被用做動畫的屬性了,結合keyframe的使用,你能夠實現不少你想要的動畫效果
連接。固然,基於css3的動畫其實也是HTML5的動畫的一個分支,要提升性能的話還須要藉助3D加速,如preserve-3d,will-change,translate3d等hack技術提升體驗,不過本文就不在這裏展開了。
着重說下另外一種,也是本文着重想講的一點,JS的動畫實現:
首先咱們須要一個繪圖容器(雖然div也是一種選擇,不過它太陳舊了,並且和html5並無什麼關係,本文就只闡述canvas了),給它取個名字,而後經過js找到它
var canvas = document.querySelector('YourCanvasId'); //固然是用getElementById也是能夠的,畢竟它是沒有兼容問題的
而後,既然是「畫」,那麼咱們須要一支畫筆:
var ctx = canvas.getContext('2d');//畫筆爲何叫「ctx」呢?這實際上是context的縮寫,是canvas api提供的繪圖對象的引用
拿到筆,經過什麼方法畫呢:
ctx.drawImage(YourParams);//這個重載了n次(?)的方法剛接觸的時候可謂複雜之極(笑)
這樣,就能畫出你須要繪製的圖像的一幀了,至於
圖片須要加載完成以後才能繪製之類的基礎知識,這裏就不展開了。
接下來,咱們須要讓它動起來,該怎麼辦呢?咱們須要每一個一個固定的時間就調用drawImage方法,不斷更新繪圖容器裏的圖案,讓咱們的眼睛看到運動的影響:
requestAnimationFrame(YourTicker);//固然這是一個須要自調用的函數,因此請保證你不會讓它死循環
綜上所述,咱們只要讓它自調用就能達到咱們的目的了。
有過其餘語言經歷特別是從事過客戶端開發的童鞋應該對以上過程比較熟悉,咱們想經過代碼實現動畫每每是一個蠻費力不討好的事情,就像上圖中的「序列幀」,若是使用css3實現動畫的話,只須要使用keyframe驅動background-position或者transform變化就能夠了,可是想經過代碼實現,首先咱們至少須要一個INTERVAL句柄,來幫助咱們在至少1/24秒調用一次繪製的方法,手動讓圖片展現咱們但願他展現的部分,這實際上是一件蠻廢力的事情,而若是使用CSS3則不須要你那麼麻煩,寫一個幀動畫也許就完事了。固然這也是css3爲何會有animation屬性的緣由所在,但隨着業務的發展(或者說。。人們「腦洞」愈來愈大?)咱們須要玩的愈來愈多的「新花樣」,css3的animation解決的大抵是網頁維度的動畫問題,而canvas的api才能解決webapp的動畫和遊戲的問題。舉個栗子:
咱們須要讓剛纔的動畫「動起來」,或者說產生和用戶的複雜交互,這個時候該怎麼辦?也許CSS3玩得很溜的童鞋會說,css3依然能夠實現這些!
是的,誠然,筆者也這樣實現過。可是當你打開的chrome devtools,特別是看到你的頁面在執行你複雜css3動畫是產生的paint flashing和FPS meter裏過山車同樣的幀率變化時,你會發現,特別的,當你費勁心血寫的那些動畫在手機(特別是前幾年的Android機器上)的時候,s**t,爲何這麼卡?電腦上明明好好的!
雖然技術突飛猛進,可是依託於瀏覽器的webview的性能依然是有限的,它並非一個可以讓咱們隨意發揮創造力的一個舞臺,咱們在實現心中所想的時候,還須要考慮到性能問題,當reflow repaint等瀏覽器渲染機制一次又一次折磨着你的時候,canvas無疑是一劑「包治百病」的良藥(固然,歷史老是驚人的類似)。
扯了那麼多,回到實現動畫與遊戲的問題上來,接着動畫日後走,咱們繼續向咱們場景中注入事件監聽(固然,理想狀況下這應該事件事件捕獲)
stage.addEventListener('click', YourClickHandler, true); //這裏咱們須要向canvas的上級,也就是整個舞臺添加事件監聽
這一個操做構建了用戶與動畫的交互,使之成爲了一個最初級的遊戲。
一切就這樣順應而生。
記得第一次寫遊戲的時候,能想起一句話
「上帝說,要有光,因而,便有了光」
不知道有多少人會和筆者有同樣的感覺(笑)
這是一個快餐的時代,若是更快的實踐?
簡單的講完了原理以後,不少童鞋可能會筆者當初剛知道原理的時候同樣,有一樣的問題,時代發展得那麼快,咱們哪有功夫慢慢去堆一套這麼個東西出來呢?這個問題一樣困擾着筆者,在筆者剛過去的2016年就碰到了這樣一個場景:android
業務方須要在春節上線5個HTML5的遊戲,時間基本只有兩週,整個項目處於劇本暫無、人員未定、資源沒有,可是檔期已定的嚴苛條件下css3
拿2016年的年度詞彙來講,這是一個典型的「黑天鵝」事件,可是既然它已經發生了,那麼咱們能作的,就只有「面對不肯定性」。這個時候筆者也想起來早在2012年學習HTML5的時候就嘗試着寫過一個html5的遊戲引擎,也通過團隊的平衡考量,最終選擇一個蠻有淵源的H5引擎Hilo,用於快速開發。雖然至今對內部事件機制仍抱有一些質疑,可是總體api的可用性,易用性,還有針對web端這樣的一個特性,筆者最終選擇了這個引擎,順利完成了那個「甲方虐我千百遍,我待甲方如初戀」的需求。git
回到這個問題上,此次也許咱們面對的是一個遊戲的開發需求,下次也許是一個其餘的什麼的開發需求,在這個迅速變化、發展的時代,做爲一個前端工程師(請記住,咱們是工程師),咱們應該如何自處呢?我想起了《黑天鵝》一書做者納西姆·塔勒布的另一部書《反脆弱》他在扉頁裏這樣寫道github
「從不肯定性中獲益」web
但願你我能都從中獲益吧。
總結一下
在html5時代,如何高效的的實現動畫和遊戲呢?
首先,咱們能夠依賴CSS3提供的強大支持來實現部分靜態、固定的動畫效果(固然別忘了那些硬件加速的小姿式,可以迅速的幫你提升性能),而對於那些複雜的動畫,咱們則須要依賴canvas的強大能力,固然,再進一步,咱們還能使用webgl(可是不得不說,雖然canvas在部分android的表現已經不算太好,可是webgl。。還有是否支持的前置問題)。
而後,對於基於canvas的動畫或者遊戲,高效的抽離和管理interval和事件監聽,如使用requestAnimationFrame替換setInterval和setTimeout,全部tick在一個統一的ticker中管理,以及將避免過多的事件監聽,都能有效的提升整個webapp的性能。
最後,前文可能並無說起的,內存的管理,資源的回收,這也是大部分性能問題的癥結所在,基於過往的習慣,咱們大多數時候都只關心如何構建和使用資源,而在遊戲的場景中,卻多了一個回收資源(好比說restart)的場景存在,咱們不能總寄但願於依賴頁面刷新去處理,可以對本身建立申請的內存資源進行管理和回收,這也是工程化思想很重要的一部分。如何更好的使用單例模式構造一經建立而不須要頻繁回收的對象,使用工廠模式批量的構造咱們須要的對象,使用裝飾者模式在不破壞遊戲結構的前提下完成打點,這都是咱們須要關心的工做。
一些也許有用的小知識
講道理文章到上面就應該結束了,可是說了一堆你們都懂的話未免也太無聊了,如下羅列蒐集一點本身的實踐中的幹活,但願再各位童鞋踩坑的時候可以幫到你們:
一、首先是關於canvas的,一樣也是咱們老僧常談的一倍屏VS多倍屏的問題,咱們都知道手機如今至少都是個2倍屏,那麼聰明的你確定會想到,既然圖片都須要針對2倍屏作適配,那麼canvas應該也會有相似的問題纔對吧?沒錯,canvas也會由於手機的2倍屏而致使成像模糊,最簡單的處理方式是:
你的dom中給一個2倍寬高的canvas:
<canvas width="YourScreenWidth * 2" height="YourScreenHeight * 2"></canvas>
另外,再繪製以前,縮放你的ctx爲原始的1/2:
ctx.scale(.5, .5)
想知道具體緣由的童鞋,不妨思考一下爲何。
二、想必不少同窗都有過使用rem開發響應式頁面的經歷,那樣的頁面確實可以很好的填充整個屏幕,可是若是換作是canvas呢?rem的那套實現還可以好用麼?其實這個問題你們只要想一想,好比你玩的如LOL、DOTA、或者某某手遊,它會由於你屏幕變化而隨着等比例變化麼?就很好回答了。
三、和CSS3的的渲染同樣,開啓硬件加速每每是解決性能瓶頸的少數很少的方法,固然canvas平臺上還有一個webgl的解決方案,可以優化你的html5 app的性能。
四、也許細心的童鞋在上文中也發現了,爲何要使用事件委託來添加監聽呢?由於整個canvas容器就一個eventHandler,基於性能考量,這是性能最好的一個方案(至於實現嘛,事件隊列也是蠻成熟的思想了,這裏就不贅述了)。而一樣的,全部須要延時調用的,咱們也會都是用那惟一的已經啓動ticker來處理。
五、關於裝飾者模式的數據採集。其實大多數公司都會有數據監控的需求,由於他們須要知道他們作的這些webapp的實際效果如何,那麼數據監控則是勢在必行之舉,但它自己是和遊戲是沒有關係的,因此基於aop的思想,也依賴babel強大的能力,咱們可以使用裝飾者模式,以最少的成本換取項目儘量少的破壞。
目前能想到的就這些了,也許將來碰到了新的坑也會更新上來。
而後最後按慣例須要昇華下主題。生而有幸,在前端最迅猛發展的幾年選對了行,入了行,而且一步步走到了今天,隨着前端的大潮席捲着全球:
動畫 遊戲 node 數據庫 hybridapp
這又讓我想起了著名的羅馬大帝蓋烏斯·尤利烏斯·愷撒說過的一句話:
I came, I saw, I conquered
也許如今的大前端的浪潮也正是這樣,可是最後這位偉大的皇帝(無冕之皇)死在了本身元老會的面前。