Graphics 與 DisplayObject 的關係

在原生 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 的工做原理

來分析 Graphics 的 drawRect API 源碼:
https://www.createjs.com/docs...
圖片描述app

https://www.createjs.com/docs...
圖片描述函數

https://www.createjs.com/docs...
圖片描述測試

https://www.createjs.com/docs...
圖片描述動畫

代碼很簡單,以下:

  1. 建立一個「Rect 實例」;

  2. 「Rect 構造函數」將 drawRect 的實現代碼掛載在 prototype.exec

  3. 將「Rect 實例」append 到數組 _activeInstructions中(第四張截圖);

  4. 返回「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);

Shape 的工做原理

https://www.createjs.com/docs...
圖片描述

Shape 極簡單,就是生成一個 DisplayObject 實例,而後在這個實例中掛載一個 Graphics 實例。而它的工做原理(即它的渲染)以下:
https://www.createjs.com/docs...
圖片描述

也就是 Shape 實例的 draw 方法實際上是直接調用 Graphics 實例的 draw 方法。

DisplayObject 的工做原理

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 同樣麻煩。

相關文章
相關標籤/搜索