OutscreenCanvas – האצת פעולות בד קנבס בעזרת worker באינטרנט

Tim Dresser

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

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

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

למרבה המזל, OffscreenCanvas הוא פתרון לאיום הזה.

תמיכה בדפדפנים

  • Chrome: ‏ 69.
  • Edge: ‏ 79.
  • Firefox: ‏ 105.
  • Safari: 16.4.

מקור

בעבר, יכולות הציור על לוח הציור היו קשורות לרכיב <canvas>, כלומר הן היו תלויות ישירות ב-DOM. כפי שרואים מהשם, OffscreenCanvas מפריד בין DOM לבין Canvas API על ידי העברת ה-API מחוץ למסך.

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

יתרה מכך, אפשר להשתמש בו ב-Web Worker, גם אם אין DOM זמין. כך אפשר ליצור כל מיני תרחישים לדוגמה מעניינים.

שימוש ב-OffscreenCanvas בעובד

Workers הם הגרסה של האינטרנט לשרשור – הם מאפשרים להריץ משימות ברקע.

העברת חלק מהסקריפטים לעובד נותנת לאפליקציה יותר מקום פנוי לביצוע משימות קריטיות למשתמש בשרשור הראשי. בלי OffscreenCanvas, לא הייתה אפשרות להשתמש ב-Canvas API ב-worker, כי לא היה DOM זמין.

OffscreenCanvas לא תלוי ב-DOM, כך שאפשר להשתמש בו. בדוגמה הבאה נעשה שימוש ב-OffscreenCanvas כדי לחשב צבע של שיפוע בעובד:

// file: worker.js
function getGradientColor(percent) {
  const canvas = new OffscreenCanvas(100, 1);
  const ctx = canvas.getContext('2d');
  const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
  gradient.addColorStop(0, 'red');
  gradient.addColorStop(1, 'blue');
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, ctx.canvas.width, 1);
  const imgd = ctx.getImageData(0, 0, ctx.canvas.width, 1);
  const colors = imgd.data.slice(percent * 4, percent * 4 + 4);
  return `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, ${colors[3]})`;
}

getGradientColor(40);  // rgba(152, 0, 104, 255 )

ביטול החסימה של השרשור הראשי

העברת חישובים כבדים לעובד מאפשרת לפנות משאבים משמעותיים בשרשור הראשי. משתמשים ב-method‏ transferControlToOffscreen כדי לשקף את הלוח הרגיל למכונה של OffscreenCanvas. פעולות שיוחלו על OffscreenCanvas ירעננו באופן אוטומטי בקנבס המקור.

const offscreen = document.querySelector('canvas').transferControlToOffscreen();
const worker = new Worker('myworkerurl.js');
worker.postMessage({canvas: offscreen}, [offscreen]);

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

דמו

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

דמו

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

מאחר ש-OffscreenCanvas API תואם באופן כללי לרכיב Canvas הרגיל, אפשר להשתמש בו כתוספת מתקדמת, גם עם חלק מספריות הגרפיקה המובילות בשוק.

לדוגמה, אפשר לזהות את התכונה, ואם היא זמינה, להשתמש בה עם Three.js על ידי ציון האפשרות canvas ב-constructor של ה-renderer:

const canvasEl = document.querySelector('canvas');
const canvas =
  'OffscreenCanvas' in window
    ? canvasEl.transferControlToOffscreen()
    : canvasEl;
canvas.style = {width: 0, height: 0};
const renderer = new THREE.WebGLRenderer({canvas: canvas});

הבעיה היחידה היא ש-Three.js מצפה של-canvas יהיו מאפיינים style.width ו-style.height. ל-OffscreenCanvas, שמנותק לחלוטין מ-DOM, אין אותו, לכן צריך לספק אותו בעצמכם, על ידי יצירת סטאב או על ידי מתן לוגיקה שמקשרת את הערכים האלה למאפייני הקנבס המקוריים.

בדוגמה הבאה מוסבר איך להריץ אנימציה בסיסית של Three.js בעובד:

דמו

חשוב לזכור שחלק מה-API שקשורים ל-DOM לא זמינים בקלות ב-worker, כך שאם רוצים להשתמש בתכונות מתקדמות יותר של Three.js כמו טקסטורות, יכול להיות שיהיה צורך בדרכים נוספות לעקיפת הבעיה. בסרטון הזה מ-Google I/O 2017 מפורטות כמה רעיונות לניסויים ראשוניים.

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

מקורות מידע נוספים