Canvas中的剪下clip()方法
Canvas中的剪下
接下來我們要聊的不是影象的合成,而是Canvas中的另一個有用的功能:剪下區域。它是Canvas之中由路徑所定義的一塊區域,瀏覽器會將所有的繪圖操作都限制在本區域內執行。在預設情況下,剪輯區域的大小與Canvas畫布大小一致。除非你通過建立路徑並呼叫Canvas繪圖環境物件的clip()
方法來顯式的設定剪輯區域,否則預設的剪輯區域不會影響Canvas之中所繪製的內容。然而,一旦設定好剪輯區域,那麼你在Canvas之中繪製的所有內容都將侷限在該區域內。這也意味著在剪輯區域以外進行繪製是沒有任何效果的。
簡單點講,Canvas的裁切路徑和普通的Canvas圖形差不多,不同的是它的作用是遮罩,用來隱藏沒有遮罩的部分。如下圖所示,紅邊五角星就是裁切路徑,所有在路徑以外的部分都不會在Canvas上繪製出來。
從上圖所示的效果來看,跟上一節介紹的globalCompositeOperation
中source-in
和source-atop
差不多的效果。最重要的區別是裁切路徑不會在Canvas上繪製東西,而且它永遠不受新圖形的影響。這些特性使得它在特定區域裡繪製圖形時相當好用。
clip()使用
上面也說了,Canvas中的clip()
方法是裁切區可用於限制影象描繪的區域,具體的用法:
- 使用Canvas的繪製函式比如,
rect()
、arc()
之類的方法選擇好繪圖區域 - 使用
clip()
函式將該區域(由rect()
、arc()
方法指定的繪圖區域)設定為裁選區
設定裁選區之後,無論在Canvas上繪製什麼,只有落在裁選區內的那部分才能得以顯示,其餘都會被遮蔽掉
先來看一個示例,繪製一個四個圓而且未使用裁選區:
ctx.save(); // 繪製第一個圓 ctx.beginPath(); ctx.fillStyle = 'tomato'; ctx.arc(cx, cy, radius, 0, Math.PI * 2, false); ctx.fill(); // 繪製第二個圓 ctx.beginPath(); ctx.fillStyle = '#333'; ctx.arc(cx + 100, cy, radius, 0, Math.PI * 2, false); ctx.fill(); // 繪製第三個圓 ctx.beginPath(); ctx.fillStyle = 'cornsilk'; ctx.arc(cx, cy + 100, radius, 0, Math.PI * 2, false); ctx.fill(); // 繪製第四個圓 ctx.beginPath(); ctx.strokeStyle = '#ccc'; ctx.lineWidth = 10; ctx.arc(cx, cy, radius, 0, Math.PI * 2, false); ctx.stroke(); ctx.restore();
看到的效果如下:
接下來我們分別來看看加上clip()
的效果,先來看看在第三個圓後面新增:
// 繪製第三個圓
ctx.beginPath();
ctx.fillStyle = 'cornsilk';
ctx.arc(cx, cy + 100, radius, 0, Math.PI * 2, false);
ctx.fill();
ctx.clip();
從效果圖上,可以看出第四個圓(灰色的,只有邊框的圓)只有部分繪製出來。接著往下,把clip()
往上移,放到第二個圓的後面。
原理同樣的,第二個圓成為裁剪區域,第三個圓和第四個圓與第二個圓有交集的地方才會繪製出來,所以看到的效果如下:
繼續按類似的方法操作,把clip()
放置在第一個圓的後面。
最終的效果,或許你已經可以猜得到了:
取消裁切區
當使用裁切區clip()
進行繪圖後,可能需要取消該裁選區或者重新定義裁切區。在Canvas中,可以通過save()
函式和restore()
函式來實現——在構建裁切區之前儲存狀態,完成裁切區內的繪圖之後進行狀態讀取。
同樣拿上面的示例來舉例,依舊在第三個圓後面做clip()
,並且同時做restore()
:
// 繪製第三個圓
ctx.beginPath();
ctx.fillStyle = 'cornsilk';
ctx.arc(cx, cy + 100, radius, 0, Math.PI * 2, false);
ctx.fill();
ctx.clip();
ctx.restore();
// 繪製第四個圓
ctx.beginPath();
ctx.strokeStyle = '#ccc';
ctx.lineWidth = 10;
ctx.arc(cx, cy, radius, 0, Math.PI * 2, false);
ctx.stroke();
和前面的示例效果不一樣,第四個灰色的邊框圓,他並沒有僅在第三個圓(裁切區)繪製,而是整個繪製出來了。接著往下看,把clip()
往上提,提到第二個圓之後,而restore()
位置不變:
如果把clip()
移動第一個圓之後,然後restore()
繼續放置在第三個圓之後,看到的效果如下:
製作探照燈
通過前面的介紹,我們瞭解了:Canvas中的clip()
方法用於從原始畫布中剪下任意形狀和尺寸。一旦剪下了某個區域,則所有之後的繪圖都會被限制在被剪下的區域內(不能訪問畫布上的其他區域)。也可以在使用clip()
方法前通過使用save()
方法對當前畫布區域進行儲存,並在以後的任意時間通過restore()
方法對其進行恢復。
根據這個原理,我們就可以很輕鬆的實現下個探照燈的效果。
- 使用
arc()
繪製一個圓形的區域,然後在其後呼叫clip()
,設定剪下區域 - 使用
drawImage()
在畫布中繪製一個影象 - 動態改變
arc()
的位置,從而看到一個類似探照燈的效果
該方法實現的效果沒有光圈的感覺,不知道大家有什麼解決辦法嗎?我嘗試了繪製圓的時候使用徑向漸變變化圓的透明度,但是沒有效果。
具體的實現效果以及程式碼可以看下面:
這個探照燈的效果是使用clip()
方法來實現的,如果你感興趣的話,可以把這個效果的實現原理與文章《Canvas學習:globalCompositeOperation詳解》中提供的示例(影象合成製作探照燈效果)對比,看看這兩者製作方案有何不同之處。
總結
這篇文章主要介紹了Canvas中的clip()
方法的特性。clip()
方法將剪下區域設定為當前剪下區域與當前路徑的交集。在第一次呼叫clip()
方法之前,剪下區域與整個Canvas畫布大小一致。因為clip()
方法會將剪下區域設定為當前剪下區域與當前路徑的交集,所以對該方法的呼叫一般都是嵌入save()
和restore()
方法之間的。否則,剪下區域將會越變越小,這通常不是我們想要的效果。合理的運用好clip()
和save()
以及restore()
方法我們就可製作出不同的效果,比如文章中介紹的探照燈的效果。