摘要:原生實(shí)現(xiàn)只存在于渲染引擎,原生是不存在的。假設(shè)有以下的代碼包括了,換種說法是劃分在同一組。于是,實(shí)現(xiàn)了一群粒子做同一件事其實(shí)就是實(shí)現(xiàn)了原生下的容器。不過,容器除了嵌套行為,它還有并列的行為兄弟容器。當(dāng)作一次記憶加深的過程。
查了一下有道:
DisplayObject 實(shí)例分類Container
n. 集裝箱;容器
我目前用到過的 DisplayObject 有5種:Bitmap, Shape, Text, MovieClip 和 Container。(好像 CreateJS 就只有這5種 DisplayObject)。不過,MovieClip 其實(shí)繼承自 Container,所以 MovieClip 可以當(dāng)作一個 Container。
與 HTML 做個類比:
Bitmap & Shape == ;
Text == 文本;
Container ==
很容易發(fā)現(xiàn)除了 Container 能嵌套子元素外,其它 4 種都不能。按這個維度分類,DisplayObject 有兩類:
容器:Container
粒子:Bitmap, Shape, Text, MovieClip
「容器」的作用是分組管理「粒子」。CreateJS 的 Stage 就是最著名的一個容器。
But...原生 Canvas 并沒有粒子與容器的概念,它有繪制圖片圖形等底層APIs。類似于 CreateJS 的 Canvas 渲染引擎的厲害之處就在于把底層 APIs 封裝起來并使之有了對象(容器與粒子)的概念。面對對象的好處大家都是知道的。
原生 APIs 實(shí)現(xiàn) ContainerContainer 只存在于渲染引擎,原生 Canvas 是不存在的。「粒子」也不存在于原生 Canvas,但是如果把 MovieClip 剔除,其它三個「粒子」其實(shí)都有對應(yīng)的原生 Canvas APIs:
Bitmap ------ drawImage
Shape ------ rect/arc/moveTo/lineTo...
Text ---- fillText
由于有一一對應(yīng)的 API,所以粒子在實(shí)現(xiàn)就是一個一一對應(yīng)搬 APIs 的過程。但是「容器」就需要討論一下了。
假設(shè)有以下的 HTML 代碼:
Text...
透明度
可見性
矩陣轉(zhuǎn)換
什么是矩陣轉(zhuǎn)換?
一個圖形的位移(translate)與形變(scale, rotate, skew)可以統(tǒng)一用一個矩陣來表示,所以「矩陣轉(zhuǎn)換」就是位移和形變的統(tǒng)稱。原生 Canvas 的 提供了矩陣轉(zhuǎn)換 API:
scale() 縮放當(dāng)前繪圖至更大或更小
rotate() 旋轉(zhuǎn)當(dāng)前繪圖
translate() 重新映射畫布上的 (0,0) 位置
transform() 替換繪圖的當(dāng)前轉(zhuǎn)換矩陣
setTransform() 將當(dāng)前轉(zhuǎn)換重置為單位矩陣。然后運(yùn)行 transform()
具體可以參見:http://www.w3school.com.cn/ta...
原生 Canvas 存在一個全局矩陣,通過上面的「矩陣轉(zhuǎn)換」API 可以修改這個全局矩陣。「矩陣轉(zhuǎn)換」在使用過程中有以下兩個特點(diǎn):
「矩陣轉(zhuǎn)換」后不影響已繪制的圖像圖形,它只作用于之后繪制 API;
「矩陣轉(zhuǎn)換」對全局矩陣的轉(zhuǎn)換是累加性的;
以下代碼可以驗(yàn)證上面兩個特性:
var ctx = canvas.getContext("2d"); ctx.rect(150, 150, 400, 400); ctx.fillStyle = "#d00000"; ctx.fill(); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(150, 150, 400, 400); ctx.fillStyle = "#00d000"; ctx.fill(); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(150, 150, 400, 400); ctx.fillStyle = "#0000d0"; ctx.fill();
截圖如下:
分「容器」后的「粒子」可以統(tǒng)一做同一件事,那么統(tǒng)一做同一件事的一群「粒子」不就可以認(rèn)為是一個「容器」。于是,實(shí)現(xiàn)了一群「粒子」做同一件事其實(shí)就是實(shí)現(xiàn)了原生 Canvas 下的「容器」。
「矩陣轉(zhuǎn)換」的第一個特性像極了「容器」的行為,第二個特性像極了「容器」嵌套「容器」的行為。不過,「容器」除了嵌套行為,它還有并列的行為(兄弟容器)。
如何實(shí)現(xiàn)「容器」并列?
兄弟「容器」A 與 B,A 比 B 早出現(xiàn)在畫布上;作為兄弟「容器」,A 的「矩陣轉(zhuǎn)換」不能對 B 產(chǎn)生影響,這好像跟第二個特性沖突了?。。?!不過,能累加的東西就有辦法可以反向累加,如下:
var ctx = canvas.getContext("2d"); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(150, 150, 400, 400); ctx.fillStyle = "#00d000"; ctx.fill(); // 反向累加 ----- 消除上次的轉(zhuǎn)換 ctx.scale(2, 2); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(600, 150, 400, 400); ctx.fillStyle = "#0000d0"; ctx.fill();
效果截圖如下:
理論上通過反向累加可以實(shí)現(xiàn)矩陣回退的效果,但在嵌套復(fù)雜的情況下,這個方案比較復(fù)雜而麻煩。But...別忘了 Canvas 有一對兄弟 APIs: save & restore。
save() 保存當(dāng)前環(huán)境的狀態(tài)
restore() 回退到上一次 save 保存的狀態(tài)
通過這兩個 APIs 可以輕松地實(shí)現(xiàn)「全局矩陣」的回退,從而達(dá)到實(shí)現(xiàn)「兄弟容器」目的,如下:
var ctx = canvas.getContext("2d"); // 保存狀態(tài) ctx.save(); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(150, 150, 400, 400); ctx.fillStyle = "#00d000"; ctx.fill(); // 恢復(fù)到上一次狀態(tài) ctx.restore(); // 縮小一倍 ctx.beginPath(); ctx.scale(.5, .5); ctx.rect(600, 150, 400, 400); ctx.fillStyle = "#0000d0"; ctx.fill();
截圖與使用反向累加一樣。
如果讓我用原生 canvas 來實(shí)現(xiàn)「容器」,我會這樣設(shè)計(jì):
「容器」是存放子元素(「粒子」與「子容器」)的數(shù)組
「粒子」是一個繪制具體任務(wù)的 Funtion
子元素由 ctx.save() 開始,ctx.restore()結(jié)束
以下是偽代碼:
var ctx = canvas.getContext("2d"); let [containerA, containerB, stage] = [[], [], [containerA, containerB]]; // 粒子是一個繪制圖形的 function let particleA1 = () => { ctx.rect(x, y, w, h); ctx.fillStyle = "#d00000"; ctx.fill(); } let particleA2 = () => ...; let particleB1 = () => ...; let particleB2 = () => ...; containerA = [particleA1, particleA2]; containerB = [particleB1, particleB2]; // 繪制 container let renderContainer = container => container.forEach( child => { // 保存狀態(tài) ctx.save(); // 子元素是容器 if(isContainer(child)) renderContainer(child); // 粒子 else renderParticle(child); // 恢復(fù)狀態(tài) ctx.restore(); } ); // 吐出 stage renderContainer(stage);CreateJS 的 Container
來看一下 CreateJS 是怎么實(shí)現(xiàn) Container的,如下:
https://www.createjs.com/docs...
跟我的設(shè)計(jì)其實(shí)是類似的。上圖紅框的 updateContext 其實(shí)就是處理「矩陣轉(zhuǎn)換」如下:
https://www.createjs.com/docs...
本來想隨手寫寫的,沒想到寫得有點(diǎn)長。當(dāng)作一次記憶加深的過程。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89641.html
摘要:本篇文章主要是跟大家聊聊的內(nèi)部架構(gòu)體系,讓大家對有個整體的認(rèn)知。方法會創(chuàng)建一個對象,調(diào)用它的方法將字節(jié)流封裝成對象,在創(chuàng)建組件時,會將組件添加到組件中組件而組件在連接器初始化時就已經(jīng)創(chuàng)建好了目前為止,只有一個實(shí)現(xiàn)類,就是。 微信公眾號「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。 老司機(jī)傾囊相授,帶你一路進(jìn)階,來不及解釋了快上車! T...
摘要:事件冒泡由微軟提出,事件會從最內(nèi)從的元素開始發(fā)生,再向外傳播,正好與事件捕獲相反。為了解決上述問題,我們可以利用事件委托的思想,在父級注冊一個事件監(jiān)聽器,統(tǒng)一進(jìn)行子元素的事件處理。 原理 事件捕獲 由網(wǎng)景最先提出,事件會從最外層開始發(fā)生,直到最具體的元素,也就是說假如父元素與子元素都綁定有點(diǎn)擊事件,又互相重疊,那么先出發(fā)的會是父元素的事件,然后再傳遞到子元素。 事件冒泡 由微軟提出,事...
閱讀 1988·2021-11-19 09:40
閱讀 1931·2021-09-28 09:36
閱讀 2279·2021-09-22 10:02
閱讀 2724·2019-08-30 14:00
閱讀 1948·2019-08-29 15:31
閱讀 2893·2019-08-29 15:11
閱讀 2905·2019-08-29 13:04
閱讀 1080·2019-08-27 10:55