ממשק API של צבע CSS

אפשרויות חדשות ב-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, צריך לטעון צבע CSS קובץ worklet באמצעות 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 אני נסביר בהמשך המאמר הזה.

בדוגמה הבאה, בואו נכתוב worklet של לוח דמקה ונשתמש בו. כתמונת רקע של <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);

עכשיו נוכל להשתמש באותו קוד לכל הסוגים השונים של לוחות דמקה. אבל אפילו ועכשיו אנחנו יכולים להיכנס לכלי פיתוח ולשחק עם הערכים עד שמוצאים את המראה הנכון.

דפדפנים שלא תומכים ב-worklet של צבע

בזמן הכתיבה, הוטמעו רק worklet של Chrome. בזמן הם אותות חיוביים מכל ספקי הדפדפנים האחרים, אין התקדמות משמעותית. כדי להתעדכן, מסמנים את התיבה Is Houdini Ready עדיין? באופן קבוע. בינתיים, חשוב להשתמש ב-Progressive כדי שהקוד ימשיך לפעול גם אם אין תמיכה בצבע מודול 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 לדפדפנים מודרניים.

תרחישים לדוגמה

יש תרחישים רבים לדוגמה של לעבודות צבע, חלקם ברורים יותר אחרים. אחת הדרכים הברורה יותר היא להשתמש בעבודת צבע כדי להקטין את הגודל של ה-DOM. לעיתים קרובות, אלמנטים נוספים רק כדי ליצור קישוטים באמצעות CSS. לדוגמה, ב-Material Design Lite, הלחצן עם אפקט האדווה מכיל 2 רכיבי <span> נוספים כדי ליישם להדוף את עצמו. אם יש הרבה לחצנים, הם יכולים להסתכם במספר גדול של לחצנים של רכיבי DOM שעלולים לפגוע בביצועים בנייד. אם להטמיע את אפקט האדווה באמצעות worklet של צבע בסוף תיצרו 0 אלמנטים נוספים ורק עבודת צבע אחת. בנוסף, יש משהו שקל יותר להתאים אישית להגדיר את הפרמטרים.

חיסרון נוסף של שימוש ב-worklet של צבע הוא שברוב המקרים, פתרון השימוש ב-worklet של צבע קטן מבחינת בייטים. כמובן, יש וריאציה: קוד הצבע יפעל בכל פעם שהקנבס בגודל או בפרמטרים שונים. לכן אם הקוד מורכב ונדרש זמן רב, הוא עשוי ליצור ג'אנק. ב-Chrome עובדים על העברת עבודות צבע מהשרשור הראשי כדי גם לעבודות צבע ממושכות לא משפיעות על הרספונסיביות של שרשור.

מבחינתי, הלקוח הפוטנציאלי הכי מרגש הוא שעבודת צבע מאפשרת מילוי פוליגונים של תכונות CSS שעדיין לא קיים בדפדפן. דוגמה אחת לכך היא לביצוע פוליגונים להדרגתיות של conic עד נוחתים ב-Chrome באופן טבעי. דוגמה נוספת: בפגישה של שירות ה-CSS, החליט שעכשיו אפשר ליצור כמה צבעי גבולות. בזמן שהפגישה הייתה והקולגה שלי איאן קילפטריק כתב מילוי פוליגונים לשירות ה-CSS החדש באמצעות worklet של צבע.

חשיבה מחוץ לתיבה

רוב האנשים מתחילים לחשוב על תמונות רקע ותמונות שוליים ללמוד על worklet של צבע. תרחיש שימוש פחות אינטואיטיבי ב-worklet של צבע הוא mask-image כדי לגרום לרכיבי DOM להיות צורות שרירותיות. לדוגמה יהלום:

רכיב DOM בצורת יהלום.
רכיב DOM בצורת יהלום.

mask-image לוקח תמונה בגודל הרכיב. אזורים שבהם תמונת המסכה שקופה, האלמנט שקוף. אזורים שבהם לבצע את המסכה התמונה אטומה, הרכיב אטום.

עכשיו ב-Chrome

worklet של צבע קיים ב-Chrome Canary כבר זמן מה. ב-Chrome 65, אפשר מופעלת כברירת מחדל. כדאי לנסות את האפשרויות החדשות ייפתח לכם עומס של צבע, ותראו לנו מה יצרתם. כדי לקבל יותר השראה, כדאי לצפות באוסף של וינסנט דה אוליביירה.