利用Egret Texture Merger和canvas快速開發幀動畫

本篇文章介紹了如何利用白鷺引擎的免費幀合成工具Egret Texture Merger和canvas製作幀動畫,並且將自己的最終成果開源到了這裡

本片文章要從一個需求說起,想想你剛剛把產品這邊的需求處理完成提交測試,正準備休息一下的時候,產品這邊又扔過來一個需求:

「我們要把原來的某些靜態圖片,替換成動畫的形式」。

「上面已經決定了,這個需求由你明天之前完成」。

What?好端端的圖片不用,還要替換成動畫的形式?辛苦設計同志製作動畫不說,這一個動畫要給用戶額外增加多少流量?要知道,我每少些一行代碼,累計給用戶節省的流量就已經十分可觀了,現在還要換成動畫,那麼...

當然,這件事情是沒有多少改變的餘地的,你要做的就是實現,然後優化,優化,優化。

(以上內容純屬玩笑,接下來進入正題)

實現幀動畫,一般我們有以下三種方式:

1.利用css的animation實現幀動畫

這種方式不用寫js,最為方便,當然,為了動畫顯示的流暢性,我們需要合成雪碧圖(否則,我們每一幀都會發出一個請求,對用戶設備和伺服器資源都是一種消耗,更重要的是沒有辦法保證動畫的連續性),但是,為了避免我們要手動書寫每一幀的background-position屬性,我們需要將合成圖變成一整條和一整列,之後利用from...to..和steps來書寫:

@keyframes frameAni2 {n from { background-position-y:0}n to { background-position-y:3420px;}n}nn.ani1{n background-image: url(./sprite1.png);n background-position-x: 0;n animation: frameAni2 1s steps(19) infinite !important;n}n

比如twitter的著名的點贊動畫(地址:10000h.top/images/twitt):

(圖片載入失敗)

就可以非常簡單的利用如下雪碧圖配合css實現:

但是可能不用我說,大家也發現了這種方式有一些弊端:

  • 必須保證所有幀佔據空間大小一致,保證直接累加的時候不出現問題,最終會是一個很長的或者很寬的圖片,不方便操作
  • 對於幀間空白沒有辦法進行處理(實際上在png中,大塊的透明區域雖然不佔據多少空間,但這個的確是可以優化的地方,感興趣的同學還可以看看之前我寫的流行圖片格式分析以及圖片壓縮探究,裡面講到了一些png圖片壓縮的內容)
  • 對於相同幀沒有辦法進行處理,實際上在某些幀動畫中,有些幀是相同的,這種方式沒有辦法共用相同幀(除非你手撕逐幀...)

存在以上問題我們就沒有辦法用這種方式了么?nope,我所在的Alloyteam的大大已經寫好了一個工具,可以一次性解決以上大部分問題,附上傳送門

剛剛準備直接用這種方式做了,需求這邊又提出一個小要求(最好能控制動畫的播放和暫停/恢復)

思考了一下,用js控制類的添加和移除可以控制動畫的播放和結束,但是這就沒有辦法保證連貫性,所以,如果有這種需求,我們可以考慮用下面的兩種方式。

2.利用js改變背景和定位

這種方式其實就是將之前@keyframes中控制的內容用js來實現了,這裡我們可以利用上面的圖,之後循環給backgroundPositionY累加一個數字。

當然既然我們已經用上了js,本身可控力度就會大很多,這個時候我們就可以用上Egret Texture Merger這個工具了(win/mac都支持)。

這個工具用法非常簡單,主要是可以將所有幀的圖片切除四周空白之後進行合併,並且可以優化分配來減少圖片的總面積,最後會生成一個json來保留原來的信息。

比如我們有一個50幀的動畫,合成的結果可能是這樣的:

然後同時會生成一個json文件來保存各個信息,json文件的每一條內容類似下面這樣:

"donghua1_00000":{"x":123,"y":689,"w":120,"h":104,"offX":28,"offY":37,"sourceW":180,"sourceH":180},n

保存的信息足夠讓我們來控制每一幀的信息。

提醒

這裡還有一個需要注意的地方,我們在繪製每一幀動畫的時候,可以選擇用window. requestAnimationFrame來完成或者用setTimeout/setInterval來完成,不過對於前者,問題在於每一幀的時間不是特別精確並且沒辦法自己定義(理論上大多數應該是16.7ms,實際16-18ms不等,並且有的時候前幾幀的時間會出現10ms以內的情況),如果用setTimeout/setInterval也不是不可行,但是為了避免丟幀的情況,我們不要將幀間隔設置的足夠小(實際上這除了浪費資源也沒什麼用),一般推薦大於16.7ms,但筆者認為如果25幀/秒效果可以接受的話可以選擇40ms。

(圖:如果設置的太小紅色箭頭所代表的幀就會被丟失)

這種方式可控力度較強,並且一般不會出現兼容性問題,只是會不斷觸發瀏覽器的重繪(這也是沒有辦法的事情)。

3.利用canvas進行繪製

這是本篇最後的一個方式也是最推薦的一個方式。

上面的第二個方式沒有什麼問題,但是一般來說,使用canvas繪製的效率會更高一些,並且canvas本身對圖像的繪製就提供了非常方便的API,我們可以嘗試使用canvas來完成這個工作。

另外canvas在目前也幾乎不存在兼容性問題,所以我們可以比較放心的使用。

使用canvas來進行繪製的核心就是使用它提供的drawImage API並且控制各個參數,我們也是可以選擇用window.requestAnimationFrame來完成或者用setTimeout/setInterval來完成並且同樣遵循上面的提醒。

另外為了完成停止、恢復、結束以及運行幾個周期就結束這種功能,我們需要進行一個狀態的保持和維護,這裡我用了ES6的類來完成這個事情,另外動畫幀也用到了Egret Texture Merger這個工具。

具體代碼這裡就不貼了,並沒有太多難理解的地方。

有了這個類之後,我們將一個原來是圖片的地方替換成動畫只需要如下幾步:

  • 將原來的img標籤替換成canvas標籤並且進行width和height的指定(這裡需要注意的是為了能保證在視網膜屏能夠清楚繪製,我們需要講width和height屬性設置成實際dom的兩倍)。
  • 利用Egret Texture Merger將素材圖片合成一個整圖,並且存儲生成的json文件供js調用。
  • 在js中我們只需要兩行代碼:

const ani = new animationByCanvas("frameResult.png",$theDomNode,ArrayFromJsonResult,60,1.15);nani.begin();n

之後如果有這種需求,我們就可以在幾分鐘內完成這件事情了。

第三部分的可服用代碼以及使用方式可以移步這裡進行參考。

(完)


推薦閱讀:

canvas可以替代html與css了嗎?
【canvas】一個少女心滿滿的例子帶你入門 canvas
vue-schart : vue.js 的圖表組件
網頁中實現正六邊形的N種姿勢

TAG:Canvas | HTML5 | 前端开发 |