עיבוד HTML ל-Canvas דו-ממדי

‫HTML-in-Canvas API מאפשר להציג עצי משנה נגישים ואינטראקטיביים של DOM ישירות בהקשר של קנבס דו-ממדי. כדי לקבל מידע נוסף על ה-API והיתרונות שלו, קראו את הסקירה הכללית על HTML-in-Canvas.

במדריך הזה מוסבר איך לעבד HTML ל-canvas דו-ממדי. כדי לעבד HTML ב-canvas דו-ממדי, צריך לבצע את השלבים הבאים:

  1. מכינים את הקנבס. מגדירים באופן הצהרתי את המאפיינים הנכונים כדי שהדפדפן ידע שהוא צריך לעבד HTML לתוך קנבס. ההגדרה הזו זהה בהקשרים של 2D,‏ WebGL ו-WebGPU. איך מגדירים את ה-canvas ואיך מגדירים רכיבי HTML שישורטטו באופן עצמאי.
  2. מציירים את תמונת הרכיב באזור העריכה.
  3. [אופציונלי] מסנכרנים את ה-DOM והפיקסלים.

ציור רכיבי HTML ב-canvas דו-ממדי

כשהקנבס מוכן והרכיבים מסומנים כ-drawable, מעבדים אותם להקשר הדו-ממדי באמצעות השיטה drawElementImage().

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const childElement1 = document.getElementById("childElement1");
const childElement2 = document.getElementById("childElement2");

canvas.onpaint = () => {
  ctx.reset();
  // 1. Draw the primary element at coordinate (20, 20)
  ctx.drawElementImage(childElement1, 20, 20);
  // 2. Draw the secondary nested button at a custom offset
  ctx.drawElementImage(childElement2, 40, 90);
}

הפונקציה drawElementImage() תומכת בטפסים עם 3, 5, 7 ו-9 ארגומנטים, ומקבלת פרמטר options אופציונלי:

// 1. Basic position: (element, dx, dy, options?)
ctx.drawElementImage(targetElement, 100, 150);

// 2. Scaled / resized: (element, dx, dy, dWidth, dHeight, options?)
ctx.drawElementImage(targetElement, 100, 150, 180, 60);

// 3. Cropped, default size: (element, sx, sy, swidth, sheight, dx, dy, options?)
ctx.drawElementImage(targetElement, 0, 0, 90, 30, 100, 150);

// 4. Cropped and resized: (element, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, options?)
ctx.drawElementImage(targetElement, 0, 0, 90, 30, 100, 150, 180, 60);
טופס עומס יתר פרמטרים אפקט הפריסה
גודל ברירת המחדל (element, dx, dy) הרכיב כולו בגודל ברירת המחדל שלו, כשהפינה השמאלית העליונה נמצאת ב-(dx, dy).
תיבת יעד מותאם אישית (element, dx, dy, dwidth, dheight) משנה את קנה המידה של תמונת הרכיב המלאה לאזור תצוגה של dw × dh בקנבס.
חתוך, גודל ברירת המחדל (element, sx, sy, swidth, sheight, dx, dy) רק האזור swidth × sheight שמתחיל ב-(sx, sy), שמשורטט בגודל ברירת המחדל של האזור הזה (באותו קנה מידה כמו הטופס בגודל ברירת המחדל).
פרוסת מקור ליעד (element, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) חיתוך של רכיב המקור החל מ-(sx, sy) עם המידות swidth × sheight, ואז מיפוי שלו ליעד (dx, dy, dwidth, dheight).

סנכרון ה-DOM והפיקסלים

מכיוון שהעיבוד של <canvas> הוא הכרחי, בעוד שהפריסה של DOM והסגנון של CSS הם ריאקטיביים, הדפדפן צריך מנגנון שיסנכרן בין הפיקסלים של לוח ה-Canvas שעובדו, אזורי הבדיקה האינטראקטיביים של מיקום המצביע ועצי הנגישות. לפני שאתם לומדים איך לסנכרן את ה-DOM והפיקסלים, חשוב שתבינו את האירוע paint שמופעל כשאלמנטים של DOM מתעדכנים וצריך לשרטט אותם מחדש.

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

עדכון ידני של הגיאומטריה של האלמנטים

בתרחישים מתקדמים, אתם יכולים לבטל את ההסכמה לסנכרון גיאומטרי אוטומטי ולשלוט בטרנספורמציות מרחביות באופן ידני. מעבירים את { preserveElementGeometry: true } אל drawElementImage() כדי למנוע מהדפדפן לשנות באופן אוטומטי את הקואורדינטות הקיימות של בדיקת מיקום המצביע של הרכיב:

// 1. Draw the element without altering its existing hit-test bounds
ctx.drawElementImage(targetElement, elementX, 200, { preserveElementGeometry: true });

// 2. Construct a custom transform matrix as needed,
// assuming scaleX/scaleY = canvas grid pixels per CSS pixel,
// from your resize observer (see "Units and sizing").
const currentTransform = new DOMMatrix();
currentTransform.a = 1.5;          // scale X
currentTransform.e = x / scaleX;   // translate X: grid px → CSS px
currentTransform.f = y / scaleY;   // translate Y: grid px → CSS px

// 3. Explicitly apply the custom transform
canvas.updateElementGeometry(targetElement, { canvasTransform: currentTransform });
בדו-ממד, drawElementImage() מעדכן את הגיאומטריה הזו באופן אוטומטי. בהדגמה הזו אפשר לראות איך הגיאומטריה יוצאת מסנכרון כשמגדירים את { preserveElementGeometry: true }.

השלבים הבאים