現代Web應用對圖像的需求量很大,它們佔據網絡下載的大部分字節。經過優化它們,你能夠更好地利用它們的性能。若是你碰巧使用幾何圖形做爲背景圖像,有一個替代方案:你可使用CSS Paint API以編程方式生成背景。javascript
在本教程中,咱們將探討其功能,並探討如何使用它來動態建立與分辨率無關的動態背景。這將是本教程的輸出:css
首先,建立一個新的 index.html
文件,並編寫以下代碼:html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>🎨 CSS Paint API</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<textarea class="pattern"></textarea>
<script> if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('pattern.js'); } </script>
</body>
</html>
複製代碼
有幾件事要注意:前端
paintWorklet
。textarea
進行演示,所以咱們能夠看到調整畫布的大小將如何重繪圖案。pattern.js
(用於註冊繪畫工做區)以及一個 styles.css
,咱們能夠在其中定義幾個樣式。Paint worklet是一個定義了應該畫在畫布上的內容的類。它們的工做原理與 canvas
元素相似。若是你之前有這方面的知識,代碼會看起來很熟悉。然而,它們並非100%相同的。例如,在worklet中還不支持文本渲染方法。java
在這裏,咱們還要定義CSS樣式。這是咱們要使用worklet的地方:git
.pattern {
width: 250px;
height: 250px;
border: 1px solid #000;
background-image: paint(pattern);
}
複製代碼
我添加了一個黑色的邊框,這樣咱們能夠更好地看到 textarea
。要引用一個paint worklet,你須要把 paint(worklet-name)
做爲一個值傳遞給 background-image
屬性。可是 pattern
是從哪裏來的呢?咱們尚未定義它,因此讓咱們把它做爲咱們的下一步。github
打開 pattern.js
並添加如下內容:編程
class Pattern {
paint(context, canvas, properties) {
}
}
registerPaint('pattern', Pattern);
複製代碼
在這裏可使用 registerPaint
方法註冊paintWorklet
。你能夠在此處定義的CSS中引用第一個參數。第二個參數是定義應在canvas上繪畫的類。它具備一個包含三個參數的 paint
方法:canvas
context
:這將返回一個 PaintRenderingContext2D
對象,該對象實現 CanvasRenderingContext2D API
的子集。canvas
:這是咱們的canvas,一個 PaintSize
對象,只有兩個屬性:width和height。properties
:這將返回一個 StylePropertyMapReadOnly
對象,咱們可使用該對象經過JavaScript讀取CSS屬性及其值。下一步是顯示一些東西,因此讓咱們繪製矩形。將如下內容添加到 paint
方法中:api
paint(context, canvas, properties) {
for (let x = 0; x < canvas.height / 20; x++) {
for (let y = 0; y < canvas.width / 20; y++) {
const bgColor = (x + y) % 2 === 0 ? '#FFF' : '#FFCC00';
context.shadowColor = '#212121';
context.shadowBlur = 10;
context.shadowOffsetX = 10;
context.shadowOffsetY = 1;
context.beginPath();
context.fillStyle = bgColor;
context.rect(x * 20, y * 20, 20, 20);
context.fill();
}
}
}
複製代碼
咱們在這裏所作的就是建立一個嵌套循環,以循環遍歷畫布的寬度和高度。因爲矩形的大小爲20,所以咱們要將矩形的高度和寬度除以20。
在第4行,咱們可使用模數運算符在兩種顏色之間切換。我還爲深度添加了一些陰影。最後,咱們在畫布上繪製矩形。若是在瀏覽器中打開它,則應具備如下內容:
遺憾的是,除了調整 textarea
的大小和一窺Paint API是如何重繪一切的,這大部分仍是靜態的。因此,讓咱們經過添加咱們能夠改變的自定義CSS屬性來讓事情變得更加動態。
打開你的 styles.css
並在其中添加如下幾行:
.pattern {
width: 250px;
height: 250px;
border: 1px solid #000;
background-image: paint(pattern);
+ --pattern-color: #FFCC00;
+ --pattern-size: 23;
+ --pattern-spacing: 0;
+ --pattern-shadow-blur: 10;
+ --pattern-shadow-x: 10;
+ --pattern-shadow-y: 1;
}
複製代碼
你能夠經過在CSS屬性前加上 —
來定義自定義CSS屬性。這些屬性能夠被 var()
函數使用。但在咱們的案例中,咱們將在咱們的paint worklet中使用它。
爲確保支持Paint API,咱們還能夠檢查CSS中的支持。爲此,咱們有兩個選擇:
@supports
規則守護規則。/* 第一種選擇 */
@supports (background: paint(pattern)) {
/** * 若是該部分獲得評估,則意味着Paint API 支持 **/
}
/** * 第二種選擇 * 若是支持Paint API,後一條規則將覆蓋第一條規則。 * 若是不支持,CSS將使其無效,並將應用url()的規則。 **/
.pattern {
background-image: url(pattern.png);
background-image: paint(pattern);
}
複製代碼
要讀取 pattern.js
中的這些參數,您須要向定義paint worklet的類中添加一個新方法:
class Pattern {
// `inputProperties`方法返回的任何東西,paint worklet均可以訪問。
static get inputProperties() {
return [
'--pattern-color',
'--pattern-size',
'--pattern-spacing',
'--pattern-shadow-blur',
'--pattern-shadow-x',
'--pattern-shadow-y'
];
}
}
複製代碼
要在 paint
方法內部訪問這些屬性,可使用 properties.get
:
paint(context, canvas, properties) {
const props = {
color: properties.get('--pattern-color').toString().trim(),
size: parseInt(properties.get('--pattern-size').toString()),
spacing: parseInt(properties.get('--pattern-spacing').toString()),
shadow: {
blur: parseInt(properties.get('--pattern-shadow-blur').toString()),
x: parseInt(properties.get('--pattern-shadow-x').toString()),
y: parseInt(properties.get('--pattern-shadow-y').toString())
}
};
}
複製代碼
對於顏色,咱們須要將其轉換爲字符串。其餘全部內容都須要轉換爲數字。這是由於 properties.get
返回 CSSUnparsedValue
。
爲了使內容更具可讀性,我建立了兩個新函數來爲咱們處理解析:
paint(context, canvas, properties) {
const getPropertyAsString = property => properties.get(property).toString().trim();
const getPropertyAsNumber = property => parseInt(properties.get(property).toString());
const props = {
color: getPropertyAsString('--pattern-color'),
size: getPropertyAsNumber('--pattern-size'),
spacing: getPropertyAsNumber('--pattern-spacing'),
shadow: {
blur: getPropertyAsNumber('--pattern-shadow-blur'),
x: getPropertyAsNumber('--pattern-shadow-x'),
y: getPropertyAsNumber('--pattern-shadow-y')
}
};
}
複製代碼
如今咱們要作的就是用相應的 prop
值替換for循環中的全部內容:
for (let x = 0; x < canvas.height / props.size; x++) {
for (let y = 0; y < canvas.width / props.size; y++) {
const bgColor = (x + y) % 2 === 0 ? '#FFF' : props.color;
context.shadowColor = '#212121';
context.shadowBlur = props.shadow.blur;
context.shadowOffsetX = props.shadow.x;
context.shadowOffsetY = props.shadow.y;
context.beginPath();
context.fillStyle = bgColor;
context.rect(x * (props.size + props.spacing),
y * (props.size + props.spacing), props.size, props.size);
context.fill();
}
}
複製代碼
如今回到你的瀏覽器,試着改變一下。
爲何CSS Paint API對咱們有用?有哪些用例?
最明顯的是,它減少了響應的大小。經過消除圖像的使用,你能夠節省一個網絡請求和幾千字節。這樣能夠提升性能。
對於使用DOM元素的複雜CSS效果,你還能夠減小頁面上的節點數量。由於你能夠用Paint API建立複雜的動畫,因此不須要額外的空節點。
在我看來,最大的好處是它的可定製性遠高於靜態背景圖片。API還能夠建立與分辨率無關的圖像,因此你不用擔憂錯過單一屏幕尺寸。
若是你今天選擇使用CSS Paint API,請確保你提供polyfill,由於它仍然沒有被普遍採用。若是你想調整完成的項目,你能夠從這個GitHub倉庫中克隆它。
來源:medium.com/better-prog…,做者:Ferenc Almasi,翻譯:公衆號《前端全棧開發者》
本文首發於公衆號 《前端全棧開發者》 ID:by-zhangbing-dev,第一時間閱讀最新文章,會優先兩天發表新文章。關注後私信回覆:大禮包,送某網精品視頻課程網盤資料,準能爲你節省很多錢!