אפשרויות חדשות ב-Chrome 65
CSS Paint API (שנקרא גם 'CSS Custom Paint' או 'Houdini's Paintlet) מופעל כברירת מחדל החל מ-Chrome 65. מה זה? מה אפשר לעשות איתו? ואיך זה פועל? ובכן, ממשיכים לקרוא...
באמצעות CSS Paint API אפשר ליצור תמונה באופן פרוגרמטי בכל פעם שמאפיין CSS מצפה לתמונה. מאפיינים כמו background-image
או border-image
בדרך כלל משמשים יחד עם url()
כדי לטעון קובץ תמונה או כדי להפעיל פונקציות מובנות של CSS, כמו linear-gradient()
. במקום להשתמש בהם, עכשיו אפשר להשתמש ב-paint(myPainter)
כדי להפנות לסביבת עבודה של צבע.
כתיבת worklet של צבע
כדי להגדיר worklet של צבע בשם myPainter
, עלינו לטעון קובץ worklet של CSS באמצעות CSS.paintWorklet.addModule('my-paint-worklet.js')
. בקובץ הזה, אפשר להשתמש בפונקציה registerPaint
כדי לרשום מחלקה של worklet של צבע:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
בתוך הקריאה החוזרת של paint()
, אפשר להשתמש ב-ctx
באותו אופן שבו אנחנו משתמשים ב-CanvasRenderingContext2D
כפי שאנחנו מכירים אותו מ-<canvas>
. אם יודעים איך לצייר ב-<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 חדש בכל פעם שנרצה לקבל את אותו תבנית, אבל עם ריבועים בגדלים שונים? התשובה היא לא.
הגדרת פרמטרים של 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);
עכשיו נוכל להשתמש באותו קוד לכל הסוגים השונים של לוחות דמקה. אבל אפילו יותר טוב, אנחנו יכולים עכשיו להיכנס לכלי פיתוח ולהתמודד עם הערכים עד שמוצאים את המראה הנכון.
דפדפנים שלא תומכים ב-worklet של צבע
בזמן הכתיבה, הוטמעו רק worklet של Chrome. יש אותות חיוביים מכל ספקי הדפדפנים האחרים, אבל אין התקדמות משמעותית. כדי להתעדכן, כדאי לבדוק באופן קבוע Is Houdini Ready עדיין?. בינתיים, כדאי להשתמש בתכונה 'שיפור הדרגתי' כדי שהקוד ימשיך לפעול, גם אם אין תמיכה ב-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
לשימושים רבים אפשר גם להשתמש ב-CSS Paint Polyfill, שמוסיף תמיכה ב-CSS Custom Paint ו-Worklets של CSS לדפדפנים מודרניים.
תרחישים לדוגמה
יש הרבה תרחישים לדוגמה של לעבודות צבע, חלקם ברורים יותר מאשר אחרים. אחת הדרכים הבולטות יותר היא להשתמש ב- worklet של צבע כדי להקטין את גודל ה-DOM. לעיתים קרובות, רכיבים נוספים מתווספים רק כדי ליצור קישוטים באמצעות CSS. לדוגמה, ב-Material Design Lite, הלחצן עם אפקט ההדים מכיל שני רכיבי <span>
נוספים כדי להטמיע את הגל עצמו. אם יש הרבה לחצנים, הפעולה הזאת יכולה להוסיף מספר גדול של רכיבי DOM עלולה לפגוע בביצועים בנייד. אם מטמיעים את אפקט הגלים באמצעות worklet של צבע, מקבלים 0 רכיבים נוספים ועבודת צבע אחת בלבד.
בנוסף, יש לכם משהו שקל יותר להתאים אישית ולהוסיף לו פרמטרים.
חיסרון נוסף של שימוש ב-worklet של צבע הוא שברוב המקרים – פתרון באמצעות worklet של צבע הוא קטן מבחינת בייטים. כמובן שיש פשרה: קוד צבע הקנבס יפעל בכל פעם שגודל הקנבס או פרמטרים כלשהם ישתנו. לכן אם הקוד מורכב ולוקח זמן רב, הוא עלול לגרום לבעיות בממשק (jank). ב-Chrome מתבצעים תהליכי צבע מה-thread הראשי, כך שגם לעבודות צבע ממושכות לא ישפיעו על רמת הרספונסיביות של ה-thread הראשי.
מבחינתי, הלקוח הפוטנציאלי המלהיב ביותר הוא שסביבת עבודה של צבע מאפשרת מילוי יעיל של פוליגונים ל-CSS שעדיין אין לדפדפן. דוגמה אחת היא שימוש בהדרגתיות של conic מ-polyfill עד שהם מגיעים ל-Chrome באופן טבעי. דוגמה נוספת: בפגישת CSS, התחרטנו שאתם יכולים עכשיו להשתמש בכמה צבעי גבולות. בזמן שהפגישה עדיין נמשכה, הקולגה שלי איאן קילפטריק כתב polyfill להתנהגות החדשה של שירות ה-CSS באמצעות מחברת צבע.
חשיבה מחוץ לתיבה
רוב האנשים מתחילים לחשוב על תמונות רקע ותמונות גבולות כשהם לומדים על worklet של צבע. תרחיש שימוש פחות אינטואיטיבי ב-worklet של צבע הוא mask-image
לגרום לרכיבי DOM ליצור צורות שרירותיות. לדוגמה, יהלום:
mask-image
לוקח תמונה בגודל הרכיב. אזורים שבהם תמונת המסכה שקופה והאלמנט שקוף. אזורים שבהם תמונת המסכה אטומה, האלמנט אטום.
עכשיו ב-Chrome
worklet של צבע קיים ב-Chrome Canary כבר זמן מה. ב-Chrome 65 הוא מופעל כברירת מחדל. אתם מוזמנים לנסות את האפשרויות החדשות שנפתחות לעבודת צביעה, ותראו לנו מה יצרתם. לקבלת השראה נוספת, כדאי לצפות בקולקציה של וינסנט דה אוליביירה.