javascript
canvas 动画库 CreateJs 之 EaselJS(上篇)
本文來(lái)自網(wǎng)易云社區(qū)
作者:田亞楠
須知
本文主要是根據(jù) createjs 中的 EaselJS 在 github 上的 tutorials 目錄下的文章整理而來(lái) (原文鏈接),同時(shí)也包含了很多本人的理解,如過(guò)有敘述不當(dāng)?shù)牡胤?#xff0c;請(qǐng)聯(lián)系我 :-D?
本文對(duì)原文中的一些知識(shí)點(diǎn)的解釋進(jìn)行了刪減,對(duì)另外一些進(jìn)行了擴(kuò)展,同時(shí)對(duì)文中的 demo 進(jìn)行了改寫,如果感到閱讀困難的話請(qǐng)參考原文(本文中有對(duì)應(yīng)的原文鏈接)
如果文中提供的 demo 無(wú)法打開(kāi),可以參考原文中的 demo。
從一個(gè)簡(jiǎn)單例子開(kāi)始
對(duì)應(yīng)原文:Getting Started
EaselJS 類的結(jié)構(gòu):
我們把上圖所有的類都稱作「元件」
上圖列出了 createjs 的主要類結(jié)構(gòu),如圖可以得到如下信息:
所有元件的基類都是 DisplayObject,所有元件都是一個(gè)構(gòu)造函數(shù)(類)
Container 可以包含其他(任何)元件。由于它本身也是一個(gè)元件,所以不同的 Container 之間可以相互包含
舞臺(tái) Stage 是一個(gè)特殊的 Container ,也是一個(gè) DisplayObject,它內(nèi)部封裝了 canvas 對(duì)象
Sprite 元件用來(lái)表現(xiàn)幀動(dòng)畫(類似 gif)
Bitmap 元件用來(lái)表現(xiàn)純靜態(tài)的圖片
Shape 元件用來(lái)表現(xiàn)矢量圖形,它的實(shí)例包含一個(gè) Graphics 元件,用來(lái)描述圖形
Filter 和 Shadow 則是濾鏡分支,可以針對(duì)任意元件實(shí)現(xiàn)顏色變換、模糊、陰影等效果。 使用濾鏡的方式跟 Flash 一致,需要新建 Filter 實(shí)例,添加到目標(biāo)元件的 FilterList 中,Createjs 框架在下一幀就會(huì)把該元件加上濾鏡效果
另外圖中沒(méi)有畫的元件還有:Text 元件用來(lái)表現(xiàn)文本、DOMElement 元件用來(lái)控制 HTML DOM 元素
在一個(gè)應(yīng)用中,各個(gè)元件類的實(shí)例之間的關(guān)系類似下面這張圖:
每個(gè)類都有各自的靜態(tài)方法,如:createjs.Ticker.setFPS(20) ,同時(shí)也可以用來(lái)初始化一個(gè)對(duì)象,如:new createjs.Stage('myCanvas')
參考:
博客園 kenkofox 的文章
API doc
Container 容器
如果不太好理解,可以先大概看一看,對(duì)閱讀后面的部分沒(méi)有影響
首先所有可以繪制到畫布上的對(duì)象(圖形、圖片等)都稱作「顯示對(duì)象」,而「容器」可以容納一個(gè)顯示對(duì)象列表。
例如你可以將頭、四肢、軀干 3 個(gè)顯示對(duì)象容納到一個(gè) person 容器中,通過(guò):
//var?head?=?..,?body?=?..,?arm?=?..//實(shí)例化一個(gè)容器對(duì)象var?person?=?new?createjs.Container();//將顯示對(duì)象放入容器中person.addChild(head,?body,?arm);這樣你可以將 person 對(duì)象作為一個(gè)整體進(jìn)行圖形變換,也可以單獨(dú)的將每一部分進(jìn)行圖形變換。即人可以整體向前移動(dòng),而他的頭部也可以同時(shí)左右擺動(dòng)。
Stage 舞臺(tái)
舞臺(tái)(Stage)對(duì)象指向我們的 canvas 元素 ,它是一個(gè)特殊的 Container 對(duì)象,它包含了所有我們希望繪制到 canvas 上的圖形/圖片,即 任何一個(gè)希望展示在畫布上的圖形/圖片都必須包含在 Stage 對(duì)象中。
例如一個(gè)圖形 s1 希望繪制到 canvas 上,則必須先 stage.addChild(s1
Stage 對(duì)象的實(shí)例化過(guò)程入下:
<canvas?id=canvasId?width=400?height=400></canvas>//...?balabala?...//參數(shù)為?canvas?id?或者?canvas?DOMElementvar?stage?=?new?createjs.Stage('canvasId');Shape
shape 對(duì)象幫助我們?cè)?canvas 上繪制矢量圖形(vector graphics)
注:canvas 繪制的圖形都是位圖,createjs 應(yīng)該是通過(guò)計(jì)算繪制出類似矢量圖的效果
shape 對(duì)象有一個(gè) graphics 屬性,它指向一個(gè) Graphics 實(shí)例,它擁有所有有關(guān)定義矢量圖形的方法。
所有「顯示對(duì)象」(可以在畫布上看到的對(duì)象,類似 shape 矢量圖形),都擁有統(tǒng)一的 位置 和 圖形變換 屬性,如 x y(位置屬性)、rotation(旋轉(zhuǎn)屬性)、scaleX scaleY(縮放屬性)等。
繪制一個(gè)紅色的圓形,如下:
var?circle?=?new?createjs.Shape();//紅色的半徑為50的圓形,它位于(0,?0)點(diǎn)坐標(biāo)//?-?注意:(0,?0)點(diǎn)是相對(duì)于?circle?對(duì)象來(lái)說(shuō)的,是相對(duì)坐標(biāo)circle.graphics.beginFill("red").drawCircle(0,?0,?50);//所有「顯示對(duì)象」通用的「位置」屬性circle.x?=?100; circle.y?=?100;//只有添加到?stage?舞臺(tái)之后,才可以顯示stage.addChild(circle);//返回值為?circle?本身以上代碼也可以簡(jiǎn)寫如下:
stage.addChild(new?createjs.Shape()).set({x:?100,?y:?100}).graphics.f('red').dc(0,?0,?50);update
雖然我們已經(jīng)將 circle 矢量圖添加到 stage 的顯示列表中了,但只有我們?cè)?update 之后才會(huì)繪制到 canvas 上。
stage.update();update 函數(shù)會(huì)先將畫布清空(通過(guò) clearRect ),然后執(zhí)行 stage 中所有 children 的繪制方法,最終將他們繪制在畫布上。
最終的 DEMO
動(dòng)畫 & Ticker
對(duì)應(yīng)原文:Animation and Ticker
動(dòng)畫基礎(chǔ)
canvas 2d 的原生基本動(dòng)畫實(shí)現(xiàn)請(qǐng)參考另一篇文章的第一部分:canvas 學(xué)習(xí)總結(jié)
除了直接使用 setTimeout、requestAnimationFrame 等方法,以某個(gè)固定間隔循環(huán)以實(shí)現(xiàn)動(dòng)畫效果之外,createjs 還提供了 Ticker 類用來(lái)更方便實(shí)現(xiàn)動(dòng)畫效果。
使用 Ticker ,你可以暫停,或修改循環(huán)頻率。還可以有選擇的使用 setTimeout 或 requestAnimationFrame。
Ticker 是 createjs 上的靜態(tài)接口,不需要 new instance。
默認(rèn)幀率為 20FPS。有 3 種修改方法:
//方法一:createjs.Ticker.setFPS(40);//方法二:createjs.Ticker.framerate?=?40;//方法三:單位?ms,(修改的是時(shí)間間隔?regular?interval)//?-?25ms?與前兩個(gè)方法等價(jià),因?yàn)?#xff1a;1000ms?/?25ms?=?40fpscreatejs.Ticker.interval?=?25;基本使用:
//?on?也可以改成?addEventListener。用來(lái)監(jiān)聽(tīng)?tick?事件(最終綁定在?window?對(duì)象上)createjs.Ticker.on('tick',?function(event){circle.x?+=?5;stage.update(event); });參考:DEMO;
基于時(shí)間的動(dòng)畫(而非刷新頻率)
問(wèn)題
請(qǐng)耐心看完 :z
?
使用 requestAnimationFrame 的動(dòng)畫都存在一個(gè)問(wèn)題,當(dāng)運(yùn)行在性能較差的設(shè)備上的時(shí)候,瀏覽器的刷新頻率會(huì)降低。
如果動(dòng)畫的位移是根據(jù)頻率計(jì)算的。也就是說(shuō):我們是根據(jù)頻率(假設(shè)為 60fps)計(jì)算的每一次循環(huán)的時(shí)間間隔,假設(shè)為 1000 / 60 = 16.667 毫秒,即一次循環(huán)時(shí)間間隔為 16.667 毫秒, 然后可以通過(guò)這個(gè)時(shí)間得出對(duì)應(yīng)的位移量:
假設(shè)我們希望小球 1秒 位移 500px,那么 16.667ms 需要位移 500/60 = 8.333px,由此計(jì)算出位移量為 8.333px。
那么當(dāng)瀏覽器的刷新頻率降低,我們預(yù)期的 60fps 沒(méi)有達(dá)到,可能變?yōu)?40fps ,那么每次循環(huán)時(shí)間間隔變?yōu)?1000 / 40 = 25ms, 因?yàn)槲覀冊(cè)O(shè)置的每次循環(huán)的位移量還是 8.333px,那么 1s 的位移量就變?yōu)榱?333.333px,其結(jié)果就是在看起來(lái)小球的移動(dòng)速度降低了。
另外,在代碼中動(dòng)態(tài)修改設(shè)置的 FPS 值的時(shí)候,所有的位移值都需要重新計(jì)算,也會(huì)遇到上述問(wèn)題。
解決
Ticker 可以將動(dòng)畫與幀率解耦,通過(guò)保證動(dòng)畫的執(zhí)行是基于時(shí)間(而非頻率), 來(lái)實(shí)現(xiàn)動(dòng)態(tài)的改變FPS(無(wú)論是因?yàn)樵O(shè)備性能差,還是代碼中進(jìn)行了修改)時(shí)不會(huì)影響動(dòng)畫的速率。
也就是說(shuō),「位移量」取決于時(shí)間,而不是變來(lái)變?nèi)サ膸省.?dāng)幀率下降的時(shí)候,每次循環(huán)中的位移量會(huì)相應(yīng)變大。
它的實(shí)現(xiàn)方式很簡(jiǎn)單:
在 tick 事件的回調(diào)函數(shù)的中增加參數(shù) delta。 delta 的值是上一次觸發(fā) tick 事件到現(xiàn)在(兩次 tick 事件之間)的時(shí)間間隔。如果代碼運(yùn)行在一個(gè)性能很差的設(shè)備上時(shí),delta 的值將明顯比預(yù)期的 1000/FPS 大。 我們可以根據(jù) delta 來(lái)計(jì)算當(dāng)前 tick 的位移。
參考:DEMO,在例子中,切換幀率不影響小球的運(yùn)動(dòng)速率
注意:必須在 update 的時(shí)候?qū)?evt 作為參數(shù)傳入 stage.update(evt);,才能正確的獲取 evt.delta 值
另外 Ticker 還會(huì)暴露一個(gè) getTime 方法,可以獲取到從 Ticker 初始化(監(jiān)聽(tīng) tick 事件開(kāi)始)到現(xiàn)在的總時(shí)間。
createjs.Ticker.getTime();同時(shí),使用精靈圖實(shí)現(xiàn) gif 圖片效果的時(shí)候,也可以使用基于時(shí)間的動(dòng)畫。涉及API:Sprite、SpriteSheet等。
TIMING MODE
較新的瀏覽器都開(kāi)始支持 requestAnimationFrame, 它能帶來(lái)很多好處,包括動(dòng)畫的平滑運(yùn)行、減少 CPU 和電量的消耗等。 然而 createjs 默認(rèn)不啟用它,而是使用 setTimeout。
我們可以通過(guò) Ticker.timingMode 來(lái)啟用 requestAnimationFrame。
它的默認(rèn)值為 Ticker.TIMEOUT 從而使用的 setTimeout ,它兼容所有瀏覽器,并提供了可預(yù)期的、靈活的幀率(見(jiàn)上一小節(jié)), 然而拋棄了 requestAnimationFrame 的各種好處。不過(guò)你可以通過(guò)減少幀率來(lái)降低 CPU/GPU 的消耗。
有兩種模式可以啟用 requestAnimationFrame,它們都會(huì)在瀏覽器不支持 requestAnimationFrame 的時(shí)候,自動(dòng)回退到 setTimeout:
Ticker.RAF 模式會(huì)單純的使用 RAF ,忽略掉設(shè)置的幀速率等值(3種設(shè)置幀率的方式全部失效)。
因?yàn)?RAF 的頻率是不確定的(由瀏覽器和當(dāng)前運(yùn)行環(huán)境決定)。如果使用了這種模式,建議一定要使用前面提到的「基于時(shí)間的動(dòng)畫」來(lái)確保動(dòng)畫速率的一致。
Ticker.RAF_SYNCHED 模式試圖協(xié)調(diào) RAF 和你設(shè)置的幀率,這種方式結(jié)合了 setTimeout 和 RAF 的優(yōu)點(diǎn), 但會(huì)在幀周期中造成極大的差異(無(wú)法理解原文的含義),因此這種模式在幀率為 60 的因子時(shí)運(yùn)行的最好,如:10、12、15、30、60。
暫停
所有的 Ticker 都是可以暫停的,如果設(shè)置
Ticker.paused?=?true;//或者createjs.Ticker.setPaused(true);那么 createjs.Ticker.getPaused() 的返回值為 true,在循環(huán) tick 中,我們可以通過(guò)這個(gè)返回值判斷需要跳過(guò)執(zhí)行的代碼:
function?tick?(evt){????if(!createjs.Ticker.getPaused()){????????//...這段代碼當(dāng)?paused?的時(shí)候不會(huì)執(zhí)行}stage.update(evt); }另外,createjs.Ticker.getTime(true) 返回除去暫停的時(shí)間的真正運(yùn)行的總時(shí)長(zhǎng)。
參考:DEMO
Tick
當(dāng)調(diào)用 stage.update(event) 方法的時(shí)候,每一個(gè) stage 的 children 都會(huì)觸發(fā)它本身暴露的 tick 事件。
通過(guò)這些 children 的 tick 事件,我們可以靈活的處理每一個(gè)顯示對(duì)象,且上下文為它本身:
//原來(lái)的寫法createjs.Ticker.on('tick',?function?(evt)?{circle.x?+=?5;stage.update(evt); });//現(xiàn)在可以寫成createjs.Ticker.on('tick',?function?(evt)?{stage.update(evt);?//會(huì)觸發(fā)?circle?的?'tick'?事件}); circle.on('tick',?function(){?this.x?+=?5?});?//上下文為?circle性能
注意高的幀率不一定意味著更好的流暢度,較低的幀率會(huì)減少 CPU/GPU 的使用,并提供始終如一的體驗(yàn)。 請(qǐng)為你的項(xiàng)目選擇一個(gè)適合的幀率。
可以通過(guò) createjs.Ticker.getMeasuredFPS() 獲取到過(guò)去一秒鐘的平均幀率。
或者通過(guò) createjs.Ticker.getMeasuredTickTime() 獲取到過(guò)去一秒鐘,每一次 tick 循環(huán)的平均時(shí)間
如果 getMeasuredFPS 接近設(shè)定的 FPS 值,說(shuō)明性能良好。
getMeasuredTickTime 的值表示一次循環(huán)中,執(zhí)行動(dòng)畫函數(shù)消耗的時(shí)間, 比 1000 / fps 越小,說(shuō)明在一次循環(huán)中有更多的時(shí)間冗余,剩余更充足的時(shí)間提供給瀏覽器進(jìn)行渲染等其他工作。
參考:DEMO
TWEENJS
通過(guò)緩動(dòng)函數(shù)創(chuàng)建動(dòng)畫或動(dòng)畫序列。
createjs.Tween.get(txt).to({x:300},?1000).to({x:0},?0).call(onAnimationCompleteFn);文本
有關(guān)字體的一點(diǎn)知識(shí)
如果瀏覽器無(wú)法再本機(jī)中找到 css 要求的字體,那么會(huì)自動(dòng)匹配類似的字體(主要是英文),如下:
serif,襯線體,在字的主要線條后面會(huì)有一個(gè)小尾巴,常見(jiàn):Georgia、Times、Times New Roman
sans-serif,無(wú)襯線體,沒(méi)有小尾巴更清爽,常見(jiàn):Arial、Verdana、Helvetica
monospace,等寬字體,顧名思義(反義:non-monospace),常見(jiàn):Courier、Courier New
cursive,手寫體,例如:Comic Sans、Monotype Corsiva
fantasy,裝飾用的字體,常用來(lái)表示標(biāo)題等少量文本,不適宜文章主體使用,例如:Impact、Haettenschweiler
展示文本
var?text?=?new?createjs.Text("Hello?World",?"bold?86px?Arial",?"#ff7700");或者:
var?text?=?new?createjs.Text(); text.text?=?"Hello?World!"; text.font?=?"bold?96px?Dorsa"; text.color?=?"#000000";其他的參數(shù)還有:(與 canvas 2d 中 context 關(guān)于文本的屬性含義相同)
text.lineHeight?=?15; text.textAlign?=?'center'; text.textBaseline?=?'top';初始化的第二個(gè)參數(shù),跟 css 中 font 的格式完全一致。
另外如果要顯示在畫布上,不要忘記寫:
stage.addChild(text); stage.update();還是上一個(gè)DEMO
網(wǎng)易云免費(fèi)體驗(yàn)館,0成本體驗(yàn)20+款云產(chǎn)品!?
更多網(wǎng)易研發(fā)、產(chǎn)品、運(yùn)營(yíng)經(jīng)驗(yàn)分享請(qǐng)?jiān)L問(wèn)網(wǎng)易云社區(qū)。
? ?
相關(guān)文章:
【推薦】?責(zé)任鏈模式的使用-NettyChannelPipeline和MinaIoFilterChain分析
【推薦】?防不勝防這些游戲被外掛活生生地毀了
【推薦】?Kylin性能調(diào)優(yōu)記——業(yè)務(wù)技術(shù)兩手抓
轉(zhuǎn)載于:https://www.cnblogs.com/163yun/p/9717314.html
總結(jié)
以上是生活随笔為你收集整理的canvas 动画库 CreateJs 之 EaselJS(上篇)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Servlet 文件上传
- 下一篇: Arduino 代码机制