זה תמיד אתה, Canvas2D

אהרון קרייסקי
אהרון קרייסקי

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

אומנם ה-API קצת יותר מפגין את הזמנים בכל הנוגע לשרטוט דו-ממדי מתקדם. למרבה המזל, השקענו מאמצים רבים בפיתוח תכונות חדשות ב-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);

חרוט הדרגתי.

מגבילי טקסט

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

לכל המאפיינים האלה יש שמות זהים במאפייני ה-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 למה It Is Awesome, שמציג חלק מהפוטנציאל המדהים שלהם.

מסננים בסגנון SVG כבר זמינים עבור Canvas2D! עליכם רק להיות מוכנים להעביר את המסנן ככתובת אתר שמפנה לרכיב אחר של מסנן 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: שיפורי ביצועים

עם ה-New 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 יש הסבר טוב ב-wiki.

סיכום

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

אישורים

תמונה ראשית (Hero) של סנדי קלארק ב-Un צורף