Chrome 65 的新功能
自 Chrome 65 版起,系統預設會啟用 CSS Paint API (又稱為「CSS Custom Paint」或「Houdini 的顏料)」。這是什麼?你可以如何運用?運作方式為何?繼續閱讀吧...
有了 CSS Paint API,只要 CSS 屬性預期圖片,即可透過程式輔助方式產生圖片。background-image
或 border-image
等屬性通常會與 url()
搭配使用,以載入圖片檔,或與 linear-gradient()
等 CSS 內建函式搭配使用。您現在可以用 paint(myPainter)
來參照繪製工作列,而不用使用這些符號。
撰寫油漆工
如要定義名為 myPainter
的繪製工作列,我們必須使用 CSS.paintWorklet.addModule('my-paint-worklet.js')
載入 CSS Paint Worklet 檔案。在這個檔案中,我們可以使用 registerPaint
函式註冊 Paint Worklet 類別:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
在 paint()
回呼中,我們可以使用 ctx
的方式與從 <canvas>
中得知的 CanvasRenderingContext2D
相同。如果您知道如何在 <canvas>
中繪製,即可在繪製工作小程式中繪圖!geometry
會指出我們處置的畫布寬度和高度。properties
我稍後會在本文中說明這一點。
做為入門範例,我們要編寫一個棋盤繪製工作小程式,然後用它做為 <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 參數化
幸好, Paintlet 可以存取其他 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);
現在,我們可以針對各種類型的檢查板使用相同的程式碼。不過更棒的是,現在可以進入開發人員工具並比對值,直到找到正確的樣式為止。
不支援漆彈的瀏覽器
本文撰寫時,只有 Chrome 實作了漆工。雖然其他瀏覽器供應商都有正面信號,但進度不多。若要掌握最新資訊,請定期查看「Is Houdini Ready't?」(是否已準備好了嗎?)。在此期間,即使不支援繪製小程式,仍請務必使用漸進式的強化功能,讓程式碼能夠持續運作。為確保運作順利,您必須在兩個地方調整程式碼: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 繪製 Polyfill
有許多用途也能使用 CSS 繪製 Polyfill,讓新式瀏覽器支援 CSS 自訂繪製和繪製工具。
應用情境
彩繪工作有許多用途,其中一些技巧也比其他工具更明顯。另一個比較明顯的做法是使用繪製工作小程式縮減 DOM 大小。多數情況下,新增元素只會用來利用 CSS 增添裝飾。舉例來說,在 Material Design Lite 中,漣漪效果按鈕包含 2 個額外的 <span>
元素,用於實作漣漪效果本身。如果按鈕有很多,這可能會新增大量 DOM 元素,並可能降低行動裝置效能。如果改為使用繪製工作列實作漣漪效果,會產生 0 個額外元素,且只提供一個顏料小程式。此外,這些資訊在自訂和參數方面也更為容易。
使用繪製工作列的另一個缺點是,在大多數情況下,使用繪製工作小子的解決方案只需以位元組為單位。當然,也有缺點:每當畫布大小或任何參數變更時,都會執行繪製程式碼。因此,如果您的程式碼相當複雜,耗時過長,可能會導致資源浪費。Chrome 正在嘗試將繪製工作列移出主執行緒,因此即使是長時間執行的繪製工作,也不會影響主要執行緒的回應速度。
對我而言,最令人振奮的潛在客戶是,繪製工作可有效處理瀏覽器尚未提供的 CSS 功能多元化。其中一個例子是對 polyfill 的漸層,直到其原生出現在 Chrome 中為止。另一個例子:在 CSS 會議中,系統判定現在可以設定多種邊框顏色。雖然這次會議還在進行中,我同事 Ian Kilpatrick 卻使用漆工作手冊為這個新的 CSS 行為編寫了 polyfill。
跳脫傳統思維
多數人在瞭解塗料小技巧時,都會開始思考背景圖片和邊框圖片。繪製工作小程式的另一個直觀使用案例是 mask-image
,讓 DOM 元素擁有任意形狀。例如菱形:
mask-image
會取用元素大小的圖片。遮罩圖片為透明的區域,元素是透明的。遮罩圖片不透明的區域,也就是元素不透明的區域。
現可在 Chrome 中取得
Chrome Canary 已有一段時間推出塗鴉工具。在 Chrome 65 中,這項功能預設為啟用。請大膽嘗試各種新可能性 看一下您做的!更多靈感請見 Vincent De Oliveira 系列影片。