HTML5 canvas 元素詳細教程六:組合。
之前的例子里面,我們總是將一個圖形畫在另一個之上,大多數情況下,這樣是不夠的。比如說,它這樣受制于圖形的繪制順序。不過,我們可以利用 globalCompositeOperation
屬性來改變這些做法。
globalCompositeOperation
我們不僅可以在已有圖形后面再畫新圖形,還可以用來遮蓋,清除(比 clearRect
方法強勁得多)某些區域。
globalCompositeOperation = type
type
是下面 12 種字符串值之一:
注意:下面所有例子中,藍色方塊是先繪制的,即“已有的 canvas 內容”,紅色圓形是后面繪制,即“新圖形”。
source-over (default) 這是默認設置,新圖形會覆蓋在原有內容之上。 |
|
destination-over 會在原有內容之下繪制新圖形。 |
|
source-in 新圖形會僅僅出現與原有內容重疊的部分。其它區域都變成透明的。 |
|
destination-in 原有內容中與新圖形重疊的部分會被保留,其它區域都變成透明的。 |
|
source-out 結果是只有新圖形中與原有內容不重疊的部分會被繪制出來。 |
|
destination-out 原有內容中與新圖形不重疊的部分會被保留。 |
|
source-atop 新圖形中與原有內容重疊的部分會被繪制,并覆蓋于原有內容之上。 |
|
destination-atop 原有內容中與新內容重疊的部分會被保留,并會在原有內容之下繪制新圖形 |
|
lighter 兩圖形中重疊部分作加色處理。 |
|
darker 兩圖形中重疊的部分作減色處理。 |
|
xor 重疊的部分會變成透明。 |
|
copy 只有新圖形會被保留,其它都被清除掉。 |
|
注意:copy
和 darker
屬性值在 Gecko 1.8 型的瀏覽器(Firefox 1.5 betas,等等)上暫時還無效。
裁切路徑 Clipping paths
裁切路徑和普通的 canvas 圖形差不多,不同的是它的作用是遮罩,用來隱藏沒有遮罩的部分。如右圖所示。紅邊五角星就是裁切路徑,所有在路徑以外的部分都不會在 canvas 上繪制出來。
如果和上面介紹的 globalCompositeOperation
屬性作一比較,它可以實現與source-in
和 source-atop
差不多的效果。最重要的區別是裁切路徑不會在 canvas 上繪制東西,而且它永遠不受新圖形的影響。這些特性使得它在特定區域里繪制圖形時相當好用。
在 繪制圖形 一章中,我只介紹了 stroke
和 fill
方法,這里介紹第三個方法clip
。
clip()
我們用 clip
方法來創建一個新的裁切路徑。默認情況下,canvas 有一個與它自身一樣大的裁切路徑(也就是沒有裁切效果)。
clip
的例子
這個例子,我會用一個圓形的裁切路徑來限制隨機星星的繪制區域。
首先,我畫了一個與 canvas 一樣大小的黑色方形作為背景,然后移動原點至中心點。然后用 clip
方法創建一個弧形的裁切路徑。裁切路徑也屬于 canvas 狀態的一部分,可以被保存起來。如果我們在創建新裁切路徑時想保留原來的裁切路徑,我們需要做的就是保存一下 canvas 的狀態。
裁切路徑創建之后所有出現在它里面的東西才會畫出來。在畫線性漸變時這個就更加明顯了。然后在隨機位置繪制 50 大小不一(經過縮放)的顆,當然也只有在裁切路徑里面的星星才會繪制出來。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.fillRect(0,0,150,150); ctx.translate(75,75); // Create a circular clipping path ctx.beginPath(); ctx.arc(0,0,60,0,Math.PI*2,true); ctx.clip(); // draw background var lingrad = ctx.createLinearGradient(0,-75,0,75); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-75,-75,150,150); // draw stars for (var j=1;j<50;j++){ ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(75-Math.floor(Math.random()*150), 75-Math.floor(Math.random()*150)); drawStar(ctx,Math.floor(Math.random()*4)+2); ctx.restore(); } } function drawStar(ctx,r){ ctx.save(); ctx.beginPath() ctx.moveTo(r,0); for (var i=0;i<9;i++){ ctx.rotate(Math.PI/5); if(i%2 == 0) { ctx.lineTo((r/0.525731)*0.200811,0); } else { ctx.lineTo(r,0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }