Chrome 65 新增無限可能
從 Chrome 65 開始,系統會預設啟用 CSS Paint API (也稱為「CSS 自訂繪圖」或「Houdini 的繪圖工作項」)。這是什麼?您可以如何運用?運作方式為何?繼續往下看吧...」
只要 CSS 屬性需要圖片,CSS Paint API 就能透過程式輔助產生圖片。background-image
或 border-image
等屬性通常會與 url()
搭配使用,用於載入圖片檔案,或與 linear-gradient()
等 CSS 內建函式搭配使用。您現在可以改用 paint(myPainter)
來參照繪圖工作區,而非使用上述方法。
編寫繪圖工作區
如要定義名為 myPainter
的繪圖工作區,我們需要使用 CSS.paintWorklet.addModule('my-paint-worklet.js')
載入 CSS 繪圖工作區檔案。在該檔案中,我們可以使用 registerPaint
函式註冊繪圖工作區類別:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
在 paint()
回呼中,我們可以使用 ctx
,就像我們在 <canvas>
中使用 CanvasRenderingContext2D
一樣。如果您知道如何在 <canvas>
中繪圖,那麼您也可以在繪圖工作區中繪圖!geometry
會告訴我們可用的畫布寬度和高度。properties
我會在本文稍後說明。
我們先透過簡單的範例,瞭解如何編寫格狀圖形繪圖工作區,並將其用於 <textarea>
的背景圖片。(我使用 textarea,因為它預設可調整大小):
<!-- index.html -->
<!doctype html>
<style>
textarea {
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
paint(ctx, geom, properties) {
// Use `ctx` as if it was a normal canvas
const colors = ['red', 'green', 'blue'];
const size = 32;
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
const color = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(x * size, y * size, size, size);
ctx.fill();
}
}
}
}
// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);
如果您過去曾使用 <canvas>
,應該對這段程式碼很熟悉。請前往這裡查看現場示範。
這裡使用常見的背景圖片的不同之處在於,每當使用者調整文字區域大小時,模式就會隨選重新繪製。這表示背景圖片一律會維持所需大小,包括為高密度螢幕提供的補償。
很酷,但也很靜態。我們是否希望每次想要使用相同圖案但不同大小的方塊時,都需要編寫新的 worklet?答案是不會!
對工作單元設定參數
幸運的是,繪圖工作區塊可以存取其他 CSS 屬性,這就是額外參數 properties
發揮作用的地方。只要為類別提供靜態 inputProperties
屬性,即可訂閱任何 CSS 屬性 (包括自訂屬性) 的變更。系統會透過 properties
參數提供這些值。
<!-- index.html -->
<!doctype html>
<style>
textarea {
/* The paint worklet subscribes to changes of these custom properties. */
--checkerboard-spacing: 10;
--checkerboard-size: 32;
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
// inputProperties returns a list of CSS properties that this paint function gets access to
static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }
paint(ctx, geom, properties) {
// Paint worklet uses CSS Typed OM to model the input values.
// As of now, they are mostly wrappers around strings,
// but will be augmented to hold more accessible data over time.
const size = parseInt(properties.get('--checkerboard-size').toString());
const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
const colors = ['red', 'green', 'blue'];
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
ctx.fillStyle = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);
我們現在可以使用相同的程式碼,為所有不同類型的棋盤格式產生圖像。更棒的是,我們現在可以進入 DevTools,調整值,直到找到正確的外觀。
不支援繪圖工作區的瀏覽器
在撰寫本文時,只有 Chrome 實作了繪圖工作單元。雖然所有其他瀏覽器供應商都提供正面信號,但進展不大。如要隨時掌握最新消息,請定期查看「Houdini 是否已就緒?」在此同時,請務必使用漸進式增強功能,即使不支援繪圖工作區塊,也能讓程式碼持續執行。為確保一切正常運作,您必須在兩個地方調整程式碼:CSS 和 JS。
您可以檢查 CSS
物件,藉此偵測 JS 中是否支援繪圖工作區:
js
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}
CSS 方面有兩種做法。您可以使用 @supports
:
@supports (background: paint(id)) {
/* ... */
}
更精簡的技巧是利用 CSS 會在屬性宣告中出現不明函式時,將整個屬性宣告視為無效並忽略這項事實。如果您兩次指定屬性 (第一次不含繪圖工作區,第二次則含繪圖工作區),就會獲得漸進式增強功能:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
在支援繪圖工作區的瀏覽器中,第二個 background-image
宣告會覆寫第一個宣告。在不支援繪圖工作區的瀏覽器中,第二個宣告無效,並且會遭到捨棄,讓第一個宣告生效。
CSS 繪圖填充
在許多情況下,您也可以使用 CSS Paint Polyfill,為新式瀏覽器新增 CSS 自訂 Paint 和 Paint Worklet 支援。
用途
繪圖工作項的用途相當多元,其中有些用途相當明顯。其中一個較明顯的做法是使用繪圖工作區,縮減 DOM 的大小。有時會加入元素,純粹是為了使用 CSS 製作修飾圖案。舉例來說,在 Material Design Lite 中,漣漪效果按鈕包含 2 個用於實作漣漪效果的額外 <span>
元素。如果您有許多按鈕,這可能會增加 DOM 元素的數量,並導致行動裝置效能降低。如果您改為使用繪圖工作區塊實作漣漪效果,最終只會產生 0 個額外元素,以及一個繪圖工作區塊。此外,您可以更輕鬆地自訂及設定參數。
使用顏料工作的另一個好處是,在大部分情況下,使用顏料工作的解決方案是少量位元組的解決方案。當然,這麼做有其代價:每當畫布的大小或任何參數發生變更時,繪圖程式碼就會執行。因此,如果程式碼複雜且耗時,可能會導致卡頓。Chrome 正在努力將繪圖工作項移出主執行緒,這樣即使繪圖工作項長時間執行,也不會影響主執行緒的回應速度。
對我來說,最令人期待的前景是,繪圖工作區塊可讓瀏覽器針對尚未提供的 CSS 功能,進行有效的半透明處理。舉例來說,您可以使用 polyfill 為 圓錐漸層提供支援,直到 Chrome 原生支援這類效果為止。另一個例子:在 CSS 會議中,我們決定現在可以使用多種邊框顏色。在這個會議進行期間,我的同事 Ian Kilpatrick 使用繪圖工作區塊,為這項新的 CSS 行為編寫了 polyfill。
跳脫框架思考
大多數人學習繪圖工作區時,都會開始思考背景圖片和邊框圖片。繪圖工作區塊的一個較不直覺的用途是 mask-image
,可讓 DOM 元素具有任意形狀。例如鑽石:
mask-image
會擷取與元素大小相同的圖片。遮罩圖片透明的區域,元素就會是透明的。遮罩圖片不透明的區域 (元素不透明)。
現已在 Chrome 中推出
Paint 工作區塊已在 Chrome Canary 中運作一段時間。在 Chrome 65 中,這項功能預設為啟用。快試試看畫圈的新可能性 看看你的成果吧!如需更多靈感,請參考 Vincent De Oliveira 的收藏。