CSS Paint API

Chrome 65 新增無限可能

自 Chrome 65 版起,系統預設會啟用 CSS Paint API (又稱「CSS 自訂繪製功能」或「Houdini 油漆工」)。是什麼?你可以如何運用?運作方式繼續往下看吧...」

CSS Paint API 可讓您在 CSS 屬性預期圖片時,透過程式輔助方式產生圖片。background-imageborder-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() 回呼中,我們可以和從 <canvas> 瞭解的 CanvasRenderingContext2D 相同,使用 ctx。如果您知道如何在 <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 參數化

幸好,繪製工作小程式可以存取其他 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 Yet?」。在此期間,請務必使用漸進式增強,讓程式碼在不支援繪製工作程式時也能持續運作。為確保能夠正常運作,您必須在兩個地方調整程式碼:CSS 和 JS。

您可以透過檢查 CSS 物件,在 JS 中偵測支援繪製工作的小程式: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } CSS 端有兩種方法。您可以使用 @supports

@supports (background: paint(id)) {
  /* ... */
}

更精簡的做法是使用 CSS 失效,並在其中存在不明函式時,忽略整個屬性宣告。如果您指定屬性兩次 (第一項沒有繪製 Worklet,然後使用油漆工作組),就可以採用漸進式的強化效果:

textarea {
  background-image: linear-gradient(0, red, blue);
  background-image: paint(myGradient, red, blue);
}

在「支援」繪畫工作程式的瀏覽器中,background-image 的第二個宣告會覆寫第一個宣告。在「不」支援繪製工作程式的瀏覽器中,第二個宣告無效並遭到捨棄,以便保留第一個宣告。

CSS 塗料 Polyfill

在許多用途中,您也可以使用 CSS 顏料 Polyfill,為新型瀏覽器新增 CSS 自訂繪製和 Paint Worklet 支援。

用途

油漆工件有許多用途,其中有些較明確。其中一個比較明顯的做法是使用顏料工作來縮減 DOM 大小。通常,元素僅供用來使用 CSS 建立裝飾。舉例來說,在 Material Design Lite 中,漣漪效果按鈕包含 2 個額外的 <span> 元素,用於實作漣漪效果。如果您有多個按鈕,可能會加入大量 DOM 元素,導致行動裝置的效能降低。如果您改用顏料工作程式實作漣漪效果,會最後獲得 0 個元素和一個繪製小程式。此外,您還能更輕鬆地自訂及參數化。

使用顏料工作的另一個好處是,在大多數情況下,使用顏料工作的解決方案是少量位元組的解決方案。當然,需要有所取捨:每當畫布大小或任何參數發生變更時,繪圖程式碼就會執行。因此,如果您的程式碼很複雜且花費較長時間,可能會導致卡頓。Chrome 正設法將繪製工作程式移出主執行緒,因此即使是長時間執行的繪製工作,也不會影響主執行緒的回應速度。

我認為最令人振奮的潛在客戶,就是透過繪製工作程式有效率地將各種 CSS 功能聚集到瀏覽器尚未具備的特色。其中一個例子就是將 Polyfill Conic 梯度,一直到 Chrome 以原生方式降落為止。另一個範例:在 CSS 會議中,我們認為現在可以設定多種邊框顏色。當這場會議繼續進行時,我的同事 Ian Kilpatrick 就使用顏料工作程式為這個新的 CSS 行為編寫 polyfill

跳脫「盒子」的思維框架

大多數人在學習繪畫工事時,就會開始思考背景圖片和邊框圖片。繪製工作小程式的一個較直覺用途,就是使用 mask-image,讓 DOM 元素具有任意形狀。例如菱形

鑽石形狀的 DOM 元素。
採用鑽石形狀的 DOM 元素。

mask-image 會擷取元素大小的圖片。遮罩圖片透明的區域,元素會顯示為透明。遮罩圖片不透明的區域 (元素不透明)。

現已在 Chrome 中

套用小程式的 Chrome Canary 已有一段時間。在 Chrome 65 中,這項功能預設為啟用。快試試看畫圈的新可能性 看看你的成果吧!如需更多資訊,請查看 Vincent De Oliveira 的系列作品