亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

剖析 createjs.Graphics

EasonTyler / 2240人閱讀

摘要:需要分析第二類。這意味著第二類方法生成的命令會(huì)在下次調(diào)用是被追加到數(shù)組上。另外不會(huì)清空數(shù)組,卻會(huì)把置為見行。功能上在形式上完全一樣,所以只需要分析即可。正好與語(yǔ)式左右互換。

在長(zhǎng)期使用 createjs 的過(guò)程中,我一直有這樣一個(gè)經(jīng)驗(yàn):「beginFill 必須在 drawXXX 之前調(diào)用,否則 beginFill 會(huì)被忽略(是的不報(bào)錯(cuò))」。
但是為什么會(huì)這樣,其實(shí)并沒有去深究它。今天很想知道 Graphics 是怎么工作的。

原因

createjs.Graphics上的繪制圖形API最終都會(huì)轉(zhuǎn)換成原生canvas語(yǔ)句。所以先看一下原生 canvas 的表現(xiàn)。

代碼一
var ctx = canvas.getContext("2d");

ctx.rect(0, 0, 100, 100);
ctx.fillStyle = "#ff0000"; 
ctx.fill(); 

最終的樣子是:

代碼二
var ctx = canvas.getContext("2d");

ctx.fillStyle = "#ff0000"; 
ctx.fill();
ctx.rect(0, 0, 100, 100); 

最終的樣子是:

為什么代碼二看不到紅色的矩形?
在 photoshop 上畫一個(gè)既沒有填充顏色又沒有描邊顏色的圖形,畫完之后這個(gè)圖形是看不到的,這個(gè)道理也同樣適用于 canvas。fill 這個(gè)API就是用于填充顏色。

對(duì)于 canvas 來(lái)說(shuō),要先繪制圖形再進(jìn)行填充(fill)或描邊(stroke),圖形最終才會(huì)被渲染到畫布上。如果先填充或描邊后再繪制圖形,那么圖形不會(huì)被渲染。

打個(gè)比喻:

先挖坑再倒水 == 一坑水;
先倒水再挖坑 == 一個(gè)坑

原生 canvas 的填充或描邊方法共有4個(gè),如下:

fill

stroke

fillRect

strokeRect

分析 createjs.Graphics 的源碼

createjs.Graphics 的源碼地址:http://www.createjs.com/docs/...

我在之前的描述中說(shuō)過(guò)「所有的Graphics上的繪制圖形API最終都會(huì)轉(zhuǎn)換成原生canvas語(yǔ)句」,看 createjs 的源碼也確實(shí)如此。由于 API 太多,只能先從 Graphics.prototype.drawRect 切入。

首先研究的對(duì)源碼其實(shí)是 Graphics 構(gòu)造函數(shù),如下圖:

其次, G 表示 Graphics 構(gòu)造函數(shù)本身,p 代表 Graphics.prototype,如下:

第三,Graphics.prototype.drawRect 指向了 Graphics.prototype.rect,如下:

第四,Graphics.prototype.rect直接返回了 this.append 并同時(shí)調(diào)用了 G.Rect 方法,如下 :

第五,先看一下 G.Rect 做了什么,如下:

這里出現(xiàn)了 exec 不知道是做什么的,不過(guò)可以看到 execdrawRect 轉(zhuǎn)換成原生的 canvas 代碼了?。?!

第六,回頭看 Graphics.prototype.append ,如下:

這里得到的信息就是把 new G.Rect(x, y, w, h) push 到數(shù)組 _activeInstructions中。似乎沒有我想要的東西,不過(guò),我往上看它的注釋如下:

// TODO: deprecated.
/**
 * Removed in favour of using custom command objects with {{#crossLink "Graphics/append"}}{{/crossLink}}.
 * @method inject
 * @deprecated
 **/

/**
 * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
 * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
 * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
 *
 * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
 * built-in or custom graphics commands. For example:
 *
 *         // attach data to our shape, so we can access it during the draw:
 *         myShape.color = "red";
 *
 *         // append a Circle command object:
 *         myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30));
 *
 *         // append a custom command object with an exec method that sets the fill style
 *         // based on the shape"s data, and then fills the circle.
 *         myShape.graphics.append({exec:function(ctx, shape) {
 *             ctx.fillStyle = shape.color;
 *             ctx.fill();
 *         }});
 *
 * @method append
 * @param {Object} command A graphics command object exposing an "exec" method.
 * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
 * @chainable
 **/

這里的信息太重要了:p.append 的作用是把「命令對(duì)象(command object)」推到「圖像隊(duì)列(graphics queue)」中。當(dāng)「圖形實(shí)例(Shape instance)」調(diào)用 draw 方法時(shí),會(huì)從「圖像隊(duì)列(graphics queue)」取出「命令對(duì)象」,并把繪制出這個(gè)實(shí)例的樣子。

第七步,查閱 p.draw,如下:

這里一目了然,在執(zhí)行 draw 時(shí)是對(duì)數(shù)組 _instructions 做出隊(duì)列操作。但是,第六步提到的數(shù)組是_activeInstructions,那么 _instructions_activeInstructions 是什么關(guān)系呢?上圖有一個(gè)叫 this._updateInstructions() 或許可以給我答案。

第八步,查閱 _updateInstructions 方法:

從上圖代碼可知:_activeInstructions_instructions 的一部分。再深入分析可以看到上圖代碼接下來(lái)的 this._fillthis._stroke。

在我看來(lái) createjs 把Graphics 的方法分成兩類三種。

第一類 繪制圖形 繪制方法;如: rect/moveTo/lineTo 等
第二類 渲染圖形 填充方法(fill);
描邊方法(stroke);

當(dāng)前分析的 drawRect 就是第一類。需要分析第二類。

第九步,分析 beginFill

可以發(fā)現(xiàn),beginFill 不調(diào)用 this.append 而是 this._setFill 了。

第十步,查閱 p._setFill,如下:

第二類方法直接就調(diào)用了 this._updateInstructions(true) 了,而且第二類方法生成的命令也不再是存入 _activeInstructions數(shù)組中了(其實(shí) _activeInstructions 數(shù)組就是第一類方法生成的命令的數(shù)組)。

1615 行的 this.command = this._fill = fill,其實(shí)很重要?;仡^看第八步的 _updateInstructions ,第二類方法內(nèi)部調(diào)用 _updateInstructions 并傳入 boolean 值true,它的作用是清空 _activeInstructions 數(shù)組(見 1602 行)。
分析「1577行~1606行」的代碼可以知道這20行的代碼的作用是把第二類方法生成的命令追加到 _instructions 數(shù)組上。這里有一個(gè)邏輯陷井:把當(dāng)前的第二命令追加到 instructions 數(shù)組上。

為什么是個(gè)陷井呢?
回頭看 1614~1615 行,this._fill 在 調(diào)用 _updateInstructions 后被賦值。這意味著第二類方法生成的命令會(huì)在下次調(diào)用 _updateInstructions 是被追加到 _instructions 數(shù)組上。

哪些操作會(huì)調(diào)用到 _updateInstructions?
第二類方法 與 p.draw。

這意味著:第二類方法生成的命令位于隊(duì)列的位置是下一個(gè)第二類方法所在的鏈?zhǔn)轿恢?如果只有一個(gè)第二類方法則在鏈?zhǔn)阶詈?。
但是上面的結(jié)論并不能解決本文本拋出的知識(shí)點(diǎn):「beginFill必須放在 drawXXX 之前,否則beginFill會(huì)被忽略」。

回到1577行的判斷語(yǔ)句:if (this._dirty && active.length) 。上面其實(shí)提到了第二類方法調(diào)用 _updateInstructions 方法后會(huì)把 _activeInstructions 數(shù)組清空( 即active.length === 0)。另外 p.draw 不會(huì)清空 _activeInstructions 數(shù)組,卻會(huì)把 this.dirty 置為 false(見行:1599)。
這意味著:Graphics的鏈?zhǔn)侥┪捕际堑诙惙椒ǎ敲催@些方法生成的命令不會(huì)被追加到 _instructions 數(shù)組上(即不會(huì)被執(zhí)行)。如下:
var rect = new createjs.Shape();
rect.graphics.drawRect(0, 0, 100, 100).beginFill("#ff0000").setStrokeStyle("#000000").beginStroke(4);
stage.addChild(rect);
上面的代碼執(zhí)行后是空白。

PS:第二類命令所有的方法 ---- beginFill, beginStroke, setStrokeStyle, setStrokeDash。
功能上 beginFill 在形式上完全一樣,所以只需要分析 beginFill 即可。如下:

多圖形實(shí)例

createjs.Graphics 是可以創(chuàng)建一個(gè)多圖形實(shí)例的,如下:
var instance = new createjs.Shape();
instance.graphics

.beginFill("#ff0000").drawRect(0, 0, 100, 100) // 矩形
.beginFill("#ffff00").drawCircle(150, 150, 50) // 圓形

其實(shí)我想象中的樣子是一個(gè) Shape 實(shí)例只能創(chuàng)建一個(gè)圖形,但事實(shí)是一個(gè) Shape 實(shí)例是可以創(chuàng)建多個(gè)圖形的。從原生 canvas 說(shuō)一下多圖形是怎么繪制的:

var ctx = canvas.getContext("2d");

ctx.beginPath(); 
ctx.rect(0, 0, 100, 100); 
ctx.fillStyle = "#ff0000"; 
ctx.fill(); 
ctx.closePath(); 
ctx.beginPath(); 
ctx.arc(150, 150, 50, 150, 100 * Math.PI * 2); 
ctx.fillStyle = "#ffff00"; 
ctx.fill(); 
ctx.closePath();

嚴(yán)格上說(shuō),原生 canvas 的一個(gè)圖形的繪制與渲染由 beginPath() 開始,再由 closePath() 結(jié)束。
實(shí)際上,beginPath() 代表上一個(gè)圖形的結(jié)束和下一個(gè)圖形的開始。

所以代碼可以簡(jiǎn)單為:
var ctx = canvas.getContext("2d");

ctx.rect(0, 0, 100, 100); 
ctx.fillStyle = "#ff0000"; 
ctx.fill();
ctx.beginPath(); 
ctx.arc(150, 150, 50, 150, 100 * Math.PI * 2); 
ctx.fillStyle = "#ffff00"; 
ctx.fill();

如果矩形與圓形中間的 beginPath() 沒有了,會(huì)怎么樣?
var ctx = canvas.getContext("2d");

ctx.rect(0, 0, 100, 100); 
ctx.fillStyle = "#ff0000"; 
ctx.fill(); 
ctx.arc(150, 150, 50, 150, 100 * Math.PI * 2); 
ctx.fillStyle = "#ffff00"; 
ctx.fill();

這種情況,矩形和圓形同屬于一個(gè)圖形,所以 fill 填充取的是最后一次的顏色。

回頭看 createjs.Graphicsp._updateInstructions

很容易得出另一個(gè)結(jié)論:第二類方法會(huì)在其鏈?zhǔn)缴纤谖恢貌迦隻eginPath的命令以標(biāo)記上一個(gè)圖形結(jié)束和下一個(gè)圖形開始
如果以一個(gè)圖形為研究對(duì)象不難得出Graphics 繪制渲染一個(gè)圖形的語(yǔ)式:第二類方法().[.第二類方法()...].第一類方法()[.第一類方法()...]

上面的語(yǔ)式可以簡(jiǎn)單地寫成: 第二類方法組.第一類方法組。
然后如果把方法轉(zhuǎn)化為對(duì)應(yīng)的原生命令,那么這些命令的執(zhí)行順序是:第一類方法生成的命令 -> 第二類方法生成的命令。正好與語(yǔ)式左右互換。

總結(jié)

本文對(duì) Graphics 源碼解析后,給出的結(jié)論如下:

第二類方法生成的命令位于隊(duì)列的位置是下一個(gè)第二類方法所在的鏈?zhǔn)轿恢?如果只有一個(gè)第二類方法則在鏈?zhǔn)阶詈?

Graphics的鏈?zhǔn)侥┪捕际堑诙惙椒ǎ敲催@些方法生成的命令不會(huì)被追加到 _instructions 數(shù)組上(即不會(huì)被執(zhí)行)

第二類方法在鏈?zhǔn)降奈恢脴?biāo)志上一個(gè)圖形的結(jié)束和下一個(gè)圖形的開始

雖然有三個(gè)結(jié)論,不過(guò)不便被記憶。

更有價(jià)值的應(yīng)該是繪制圖形的語(yǔ)式:「第二類方法組.第一類方法組
但論實(shí)用價(jià)值還是開頭的那句話:beginFill 必須在 drawXXX 之前調(diào)用,否則 beginFill 會(huì)被忽略(是的不報(bào)錯(cuò))

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88379.html

相關(guān)文章

  • 模擬easeljs做動(dòng)畫

    摘要:昨天看了老外的視頻教程,介紹了做大大節(jié)約了開發(fā)的成本,老外用原生的和各實(shí)現(xiàn)了一遍方塊旋轉(zhuǎn)動(dòng)畫。 昨天看了老外的視頻教程,介紹了easeljs做canvas大大節(jié)約了開發(fā)的成本,老外用原生的canvas和easeljs 各實(shí)現(xiàn)了一遍方塊旋轉(zhuǎn)動(dòng)畫。 這時(shí)的我感覺很驚訝,原來(lái)動(dòng)畫做起來(lái)并不是我想得這么復(fù)雜,于是自己用模擬easeljs也做了一個(gè)動(dòng)畫旋轉(zhuǎn),感覺棒棒噠~ ...

    Taste 評(píng)論0 收藏0
  • CreateJS入門 -- 注釋詳細(xì)到爆炸(My Style)

    摘要:以后所有的文章都會(huì)第一時(shí)間更新到這里,然后同步到其他平臺(tái)。獲取畫布的寬和高,后面計(jì)算使用定義靜態(tài)資源人物動(dòng)作雪碧圖天空地面遠(yuǎn)山近山創(chuàng)建資源加載隊(duì)列用還是用標(biāo)簽來(lái)加載如果是的時(shí)候,就用標(biāo)簽來(lái)加載,如果不能用標(biāo)簽的話,就用來(lái)加載。 寫在前面 首先,還是謝謝大家的支持,謝謝!記得在之前的文章中我說(shuō)過(guò)自己算是一個(gè)半文藝程序員,也一直想著寫一寫技術(shù)性和其他偏文學(xué)性的文章。雖然自己的底子沒有多么優(yōu)...

    Render 評(píng)論0 收藏0
  • CreateJS入門 -- 注釋詳細(xì)到爆炸(My Style)

    摘要:以后所有的文章都會(huì)第一時(shí)間更新到這里,然后同步到其他平臺(tái)。獲取畫布的寬和高,后面計(jì)算使用定義靜態(tài)資源人物動(dòng)作雪碧圖天空地面遠(yuǎn)山近山創(chuàng)建資源加載隊(duì)列用還是用標(biāo)簽來(lái)加載如果是的時(shí)候,就用標(biāo)簽來(lái)加載,如果不能用標(biāo)簽的話,就用來(lái)加載。 寫在前面 首先,還是謝謝大家的支持,謝謝!記得在之前的文章中我說(shuō)過(guò)自己算是一個(gè)半文藝程序員,也一直想著寫一寫技術(shù)性和其他偏文學(xué)性的文章。雖然自己的底子沒有多么優(yōu)...

    liangdas 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<