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