זה תמיד אתה, Canvas2D

Aaron Krajeski
Aaron Krajeski

בעולם של שיבושים, רשתות ומסננים, יכול להיות ש-Canvas2D לא יעניין אתכם. אבל זה צריך להיות! ב-30-40% מדפי האינטרנט יש אלמנט <canvas>, וב-98% מכל משטחי הקנבס נעשה שימוש בהקשר של עיבוד Canvas2D. יש תצוגות Canvas2D במכוניות, במקררים, ובחלל (כן, באמת).

צריך להודות ש-API קצת לא מעודכן כשמדובר בציור 2D מתקדם. למרבה המזל, עבדנו קשה כדי להטמיע תכונות חדשות ב-Canvas2D כדי להתעדכן ב-CSS, לשפר את הארגונומיה ולשפר את הביצועים.

חלק 1: עדכון לגבי CSS

ב-CSS יש כמה פקודות ציור שחסרות ב-Canvas2D. ב-API החדש הוספנו כמה מהתכונות המבוקשות ביותר:

מלבן מעוגל

מלבנים מעוגלים: אבן הפינה של האינטרנט, של המחשוב, ואפילו של הציוויליזציה.

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

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';

const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;

ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();

כל זה היה נדרש כדי ליצור מלבן פשוט וצנוע עם קצוות מעוגלים:

מלבן מעוגל.

ב-API החדש יש שיטה roundRect().

ctx.roundRect(upper, left, width, height, borderRadius);

לכן אפשר להחליף את הקוד שלמעלה באופן מלא בקוד הבא:

ctx.roundRect(10, 10, 200, 100, 20);

השיטה ctx.roundRect() מקבלת גם מערך לארגומנט borderRadius של עד ארבעה מספרים. הרדיוס קובע את ארבע הפינות של המלבן המעוגל באותו אופן כמו ב-CSS. לדוגמה:

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

כדאי לבדוק את הדמו כדי להתנסות.

מפל צבעים חרוט

ראית מעברים לינאריים:

const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);

שינוי הדרגתי לינארי.

מעברי צבע רדיאליים:

const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');

ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);

שינוי הדרגתי רדיאלי.

אבל מה דעתכם על שימוש בצבעים מדורגים בקונוס?

const grad = ctx.createConicGradient(0, 100, 100);

grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');

ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);

שינוי הדרגתי חרוט.

פונקציות לשינוי טקסט

יכולות הרינדור של טקסט ב-Canvas2D היו מאחור. ב-Chrome נוספו כמה מאפיינים חדשים לעיבוד טקסט ב-Canvas2D:

כל המאפיינים האלה תואמים למאפיינים המקבילים ב-CSS עם אותם שמות.

חלק 2: שיפורים ארגונומיים

בעבר, היה אפשר לעשות דברים מסוימים באמצעות Canvas2D, אבל היישום שלהם היה מסובך יותר מהצורך. ריכזנו כאן כמה שיפורים שחוסכים זמן למפתחי JavaScript שרוצים להשתמש ב-Canvas2D:

איפוס ההקשר

כדי להסביר איך מנקים את הלוח, כתבתי פונקציה קטנה ומטופשת לציור דפוס רטרו:

draw90sPattern();

תבנית רטרו של משולשים וריבועים.

נהדר! עכשיו, אחרי שסיימתי את התבנית הזו, אני רוצה לנקות את הלוח ולצייר משהו אחר. רגע, איך מוחקים קנבס שוב? כן! ctx.clearRect(), כמובן.

ctx.clearRect(0, 0, canvas.width, canvas.height);

אופס… זה לא עבד. כן! קודם צריך לאפס את הטרנספורמציה:

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
לוח ציור ריק.

יופי! לוח ציור ריק נחמד. עכשיו נתחיל לצייר קו אופקי נחמד:

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

קו אופקי וקו אלכסוני.

גררר! זה לא נכון. 😡 מה עושה השורה הנוספת הזו כאן? בנוסף, למה הוא ורוד? בסדר, נבדוק ב-StackOverflow.

canvas.width = canvas.width;

למה זה כל כך טיפשי? למה זה כל כך קשה?

טוב, זה כבר לא המצב. ה-API החדש מאפשר לנו להציע לכם את הפתרון הפשוט, האלגנטי והחדשני:

ctx.reset();

סליחה על העיכוב.

מסננים

פילטרים של SVG הם עולם בפני עצמו. אם אתם לא מכירים את המסננים האלה, מומלץ מאוד לקרוא את המאמר The Art Of SVG Filters And Why It Is Awesome, שבו מוסבר חלק מהפוטנציאל המדהים שלהם.

מסנני סגנון SVG כבר זמינים ל-Canvas2D! צריך רק להעביר את המסנן ככתובת URL שמצביעה על אלמנט מסנן SVG אחר בדף:

<svg>
  <defs>
    <filter id="svgFilter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
      <feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
      <feColorMatrix type="hueRotate" values="90" />
    </filter>
  </defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);

זה מערער את הדפוס שלנו:

התבנית בסגנון רטרו עם אפקט טשטוש.

אבל מה אם תרצו לבצע את הפעולות שלמעלה בלי לצאת מ-JavaScript ולא להתעסק עם מחרוזות? בעזרת ה-API החדש, זה אפשרי לגמרי.

ctx.filter = new CanvasFilter([
  { filter: 'gaussianBlur', stdDeviation: 5 },
  {
    filter: 'convolveMatrix',
    kernelMatrix: [
      [-3, 0, 0],
      [0, 0.5, 0],
      [0, 0, 3],
    ],
  },
  { filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);

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

חלק 3: שיפורי ביצועים

ב-Canvas2D API החדש רצינו גם לשפר את הביצועים במידת האפשר. הוספנו כמה תכונות כדי לתת למפתחים שליטה מדויקת יותר על האתרים שלהם ולאפשר להם להגיע לשיעורי פריימים חלקים ככל האפשר:

קוראים בתדירות גבוהה

משתמשים ב-getImageData() כדי לקרוא נתוני פיקסלים מקנבס. התהליך יכול להיות איטי מאוד. ה-API החדש מאפשר לסמן באופן מפורש לוח להצגה חוזרת (לדוגמה, לאפקטים גנרטיביים). כך תוכלו לבצע אופטימיזציה ברקע ולשמור על מהירות התצוגה בדף הבית במגוון רחב יותר של תרחישים לדוגמה. התכונה הזו קיימת ב-Firefox כבר זמן מה, ועכשיו אנחנו סוף סוף מוסיפים אותה למפרט של הקנבס.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

אובדן הקשר

ננסה להפוך את הכרטיסיות העצובות שוב לשמחות. אם ללקוח נגמר הזיכרון ב-GPU או אם יקרה אסון אחר כלשהו ללוח הציור, תוכלו לקבל קריאה חוזרת (callback) ולצייר מחדש לפי הצורך:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

מידע נוסף על הקשר ועל אובדן נתונים בקנבס זמין בהסבר הטוב של WHATWG בוויקי.

סיכום

בין אם אתם משתמשים חדשים ב-Canvas2D, משתמשים בו כבר שנים או נמנעים משימוש בו כבר שנים, אני כאן כדי להמליץ לכם לבדוק שוב את Canvas. זהו ממשק ה-API של השכן, שהיה שם כל הזמן.