אפשרויות חדשות ב-Chrome 65
CSS Paint API (נקרא גם 'CSS Custom Paint' או 'Houdini's paint worklet') מופעל כברירת מחדל החל מגרסה 65 של Chrome. מה זה? מה אפשר לעשות איתה? איך זה עובד? אז כדאי להמשיך לקרוא…
באמצעות CSS Paint API אפשר ליצור תמונה באופן פרוגרמטי בכל פעם שמופיעה תמונה בנכס CSS. בדרך כלל משתמשים במאפיינים כמו background-image
או border-image
עם url()
כדי לטעון קובץ תמונה, או עם פונקציות מובנות של CSS כמו linear-gradient()
. במקום להשתמש בהם, עכשיו אפשר להשתמש ב-paint(myPainter)
כדי להפנות לworklet של ציור.
כתיבת וורקלט של Paint
כדי להגדיר רכיב עבודה לצביעה בשם myPainter
, צריך לטעון קובץ של רכיב עבודה לצביעה ב-CSS באמצעות CSS.paintWorklet.addModule('my-paint-worklet.js')
. בקובץ הזה אפשר להשתמש בפונקציה registerPaint
כדי לרשום סוג של רכיב עבודה לציור:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
בתוך הקריאה החוזרת (callback) paint()
, אפשר להשתמש ב-ctx
באותו אופן שבו משתמשים ב-CanvasRenderingContext2D
כפי שאנחנו מכירים אותו מ-<canvas>
. אם אתם יודעים איך לצייר ב-<canvas>
, תוכלו לצייר גם ב-Worklet לציור. 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>
בעבר, הקוד הזה אמור להיות מוכר לך. כאן תוכלו לצפות בהדגמה בשידור חי.
ההבדל ביחס לשימוש בתמונה רגילה לרקע הוא שהתבנית תתואר מחדש על פי דרישה, בכל פעם שהמשתמש ישנה את הגודל של textarea. כלומר, תמונה הרקע תמיד תהיה בגודל הנדרש, כולל התאמה למסכים עם צפיפות פיקסלים גבוהה.
זה מגניב, אבל גם די סטטי. האם נצטרך לכתוב פונקציית worklet חדשה בכל פעם שנרצה ליצור את אותו דפוס אבל עם ריבועים בגדלים שונים? התשובה היא לא!
הגדרת פרמטרים ל-worklet
למרבה המזל, ל-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 ולשנות את הערכים עד שמוצאים את המראה הנכון.
דפדפנים שלא תומכים ב-worklet של צביעה
נכון למועד כתיבת המאמר, רק ב-Chrome יש הטמעה של רכיב עבודה לצביעה. יש סימנים חיוביים מכל יצרני הדפדפנים האחרים, אבל אין התקדמות רבה. כדי להתעדכן, מומלץ לבדוק באופן קבוע את העדכון האם Houdini מוכן? בינתיים, חשוב להשתמש בשיפור הדרגתי כדי שהקוד ימשיך לפעול גם אם אין תמיכה ב-worklet לצביעה. כדי לוודא שהכל פועל כמצופה, צריך לשנות את הקוד בשני מקומות: ב-CSS וב-JS.
כדי לזהות תמיכה ב-worklet של צביעה ב-JS, בודקים את האובייקט CSS
:
js
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}
בצד ה-CSS, יש שתי אפשרויות. אתם יכולים להשתמש ב-@supports
:
@supports (background: paint(id)) {
/* ... */
}
טריק קומפקטי יותר הוא להשתמש בעובדה ש-CSS מבטל הצהרה שלמה על מאפיין אם יש בה פונקציה לא מוכרת, ולאחר מכן מתעלם מההצהרה. אם מציינים נכס פעמיים – קודם ללא רכיב ה-worklet לצביעה, ואז עם רכיב ה-worklet לצביעה – מתקבל שיפור הדרגתי:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
בדפדפנים עם תמיכה ב-worklet לצביעה, ההצהרה השנייה של background-image
תמחק את הראשונה. בדפדפנים ללא תמיכה ב-worklet לצביעה, ההצהרה השנייה לא חוקית ותימחק, וההצהרה הראשונה תישאר בתוקף.
CSS Paint Polyfill
לשימושים רבים, אפשר גם להשתמש ב-CSS Paint Polyfill, שמוסיף תמיכה ב-CSS Custom Paint וב-Paint Worklets לדפדפנים מודרניים.
תרחישים לדוגמה
יש הרבה תרחישים לדוגמה לשימוש ב-worklets של צביעה, חלקם ברורים יותר וחלקם פחות. אחת מהשיטות הברורות יותר היא שימוש ב-worklet של ציור כדי לצמצם את הגודל של ה-DOM. לרוב, מוסיפים אלמנטים רק כדי ליצור קישוטים באמצעות CSS. לדוגמה, ב-Material Design Lite, הלחצן עם אפקט הרטט מכיל 2 רכיבי <span>
נוספים להטמעת הרטט עצמו. אם יש לכם הרבה לחצנים, הם יכולים להצטבר לכמות גדולה של רכיבי DOM, וכתוצאה מכך לגרום לירידה בביצועים בנייד. אם במקום זאת מטמיעים את אפקט הרטט באמצעות רכיב עבודה לצביעה, בסופו של דבר יהיו 0 רכיבים נוספים ורק רכיב עבודה אחד לצביעה.
בנוסף, יש לכם משהו שקל יותר להתאים אישית ולהגדיר לו פרמטרים.
יתרון נוסף של שימוש ב-worklet לצביעה הוא שברוב התרחישים, הפתרון באמצעות ה-worklet לצביעה הוא קטן מבחינת בייטים. כמובן, יש לכך מחיר: קוד הציור יפעל בכל פעם שגודל הלוח או אחד מהפרמטרים ישתנו. לכן, אם הקוד שלכם מורכב ונמשך זמן רב, הוא עלול לגרום לבעיות בקוד. אנחנו ב-Chrome עובדים על העברת משימות צביעה מהשרשור הראשי, כדי שגם משימות צביעה ממושכות לא ישפיעו על תגובתיות השרשור הראשי.
לדעתי, ההזדמנות המרגשת ביותר היא ש-worklet של צביעה מאפשר מילוי יעיל של תכונות CSS שעדיין לא קיימות בדפדפן. דוגמה לכך היא שימוש ב-polyfill לעקומות טרנספורמציה אליפטיות עד שהן יתווספו ל-Chrome באופן מקורי. דוגמה נוספת: בפגישה של קבוצת CSS הוחלט שאפשר להשתמש עכשיו בכמה צבעים של גבולות. בזמן הפגישה, הקולגה שלי Ian Kilpatrick כתב polyfill להתנהגות החדשה הזו של CSS באמצעות worklet של צביעה.
חשיבה מחוץ לקופסה
רוב האנשים מתחילים לחשוב על תמונות רקע ועל תמונות גבול כשהם לומדים על ה-worklet של Paint. mask-image
הוא תרחיש לדוגמה פחות אינטואיטיבי לשימוש ב-worklet של צביעה, שבו רכיבי DOM יכולים להיות בצורות שרירותיות. לדוגמה, ריבוע:
mask-image
לוקח תמונה בגודל הרכיב. באזורים שבהם תמונה המסיכה שקופה, הרכיב שקוף. באזורים שבהם תמונת המסכה אטומה, הרכיב אטום.
עכשיו ב-Chrome
ה-worklet של Paint נמצא ב-Chrome Canary כבר זמן מה. ב-Chrome 65, התכונה מופעלת כברירת מחדל. אנחנו מזמינים אתכם לנסות את האפשרויות החדשות שמציע הווידג'ט של Paint, ולהראות לנו מה יצרתם. לקבלת השראה נוספת, תוכלו לעיין באוסף של Vincent De Oliveira.