在原生 Canvas 中,其實並無 DisplayObject 的概念,它只有繪製圖像的概念。javascript
大部分的原生繪製圖形或圖像的 API 通常是這樣的:html
api(x, y, ...)
例如 rect 就是:ctx.rect(x, y, width, height)
。java
這類繪製 API 大部分被封裝在 CreateJS 的 Graphics 類中,它們有一個共同的特色 ---- 名字都是以 draw
開始,例如:drawRect / drawCircle 。segmentfault
爲何說是大部分繪製的API被封裝在 Grahpics 類中呢?
很簡單,Graphics 類處理的是圖形繪製,因此也能夠說全部的圖形繪製 API 被封裝在 Graphics 類中。繪製API還有另外一個功能 ------ 繪製圖像,這個功能被封裝在 Bitmap 類中。api
我曾經這樣以爲:「graphics.draw*(x, y, ...) 的參數 <x, y> 就是 displayObject 的屬性 x, y」。可是其實這個想法很容易 就能夠驗證獲得它的錯誤性:數組
var rect = new createjs.Shape(); rect.graphics.drawRect(50, 50, 100 100); stage.addChild(rect); console.log(rect.x, rect.y);
來分析 Graphics 的 drawRect API 源碼:
https://www.createjs.com/docs...app
https://www.createjs.com/docs...函數
https://www.createjs.com/docs...測試
https://www.createjs.com/docs...動畫
代碼很簡單,以下:
建立一個「Rect 實例」;
「Rect 構造函數」將 drawRect
的實現代碼掛載在 prototype.exec
下
將「Rect 實例」append 到數組 _activeInstructions
中(第四張截圖);
返回「Graphics」實例
爲何須要一個數組(_activeInstructions)來存儲原生指令?
對於矩形、圓形和圓角矩形來講,確實不須要數組來存儲指令;可是梯形、三角形等圖形沒有對應的原生API,此時須要使用繪製線段或孤線(ctx.moveTo/ctx.lineTo/ctx.arc)來組合產生,而數組 _activeInstructions
就是來處理組合圖形產生的。
Rect實例有什麼做用?
Rect實例是 drawRect
返回的實例,Graphics 裏還有其它的 draw*
API,它們會返回各自圖形的實例,因此能夠把 Rect實例和其它實例統稱爲「基礎圖形實例」。「基礎圖形實例」的做用是提供一個修改圖形接口。經過第四張截圖,能夠看到「基礎圖形實例」最終被掛載在「Graphics 實例」的 command
屬性下,也就是說經過「Graphics實例」的 command
能夠訪問到最新的「基礎圖形實例」,寫一個測試代碼以下:
var gcircle = circle.graphics.beginFill("00ff00").drawCircle(0, 0, 10); circle.x = circle.y = 100; stage.addChild(circle); stage.update(); setTimeout(function() { gcircle.command.radius = 50; stage.update(); }, 1000);
這個特性在動態繪製圖形上是頗有用的,不過,它有一個短板:經過 command
屬性只能訪問到最近的「基礎圖形實例」。也就是說自定義的組合圖形,若是須要訪問到對應的「基礎圖形實例」就須要把繪製過程拆散,舉個例子:
// 三角形 var threeangle = new createjs.Shape(); threeangle.graphics.beginFill("00ff00").moveTo(50, 0).lineTo(0, 50).lineTo(100, 50).closePath(); stage.addChild(threeangle); stage.update();
若是底部兩個端點要作動畫:
// 三角形 var threeangle = new createjs.Shape(); threeangle.graphics.beginFill("00ff00").moveTo(50, 0); var p1 = threeangle.graphics.lineTo(0, 50).command; var p2 = threeangle.graphics.lineTo(100, 50).command; threeangle.graphics.closePath(); stage.addChild(threeangle); stage.update(); setTimeout(function() { p1.y = 100; stage.update(); }, 1000); setTimeout(function() { p2.y = 200; stage.update(); }, 2000);
https://www.createjs.com/docs...
Shape 極簡單,就是生成一個 DisplayObject 實例,而後在這個實例中掛載一個 Graphics 實例。而它的工做原理(即它的渲染)以下:
https://www.createjs.com/docs...
也就是 Shape 實例的 draw 方法實際上是直接調用 Graphics 實例的 draw 方法。
DisplayObject 的核心功能是處理 DisplayObject 實例的「矩陣轉換」(即位移與形變),以下:
https://www.createjs.com/docs...
我在 聊聊 Container 的實現 中介紹了使用原生 Canvas 實現 Container
的原理。其實,Shape 實例也是一個Container,只是 CreateJS 限制了 shape 只能放 Graphics 實例,並且 shape 只能存放一個 Graphics 實例(即shape 下的 graphics 屬性),Graphics 實例下存放多個「基礎圖形實例」。
**若是從嵌套的維度來講 Graphics 與 DisplayObject 的關係是:
Container > Shape > Graphics > Rect/Circle/RoundRect等基礎圖形**
其實,CreateJS 徹底不須要這麼實現。像 PIXI 在實現 Graphics 的時候,Graphics 也一個 Container,它能夠像一個正常的 DisplayObject 同樣被使用,而不用像 CreateJS 同樣麻煩。