OffscreenCanvas—سرعت عملیات بوم خود را با یک وب‌کارگر افزایش دهید

Tim Dresser

بوم روشی محبوب برای ترسیم انواع گرافیک روی صفحه و نقطه ورود به دنیای WebGL است. می توان از آن برای ترسیم اشکال، تصاویر، اجرای انیمیشن ها یا حتی نمایش و پردازش محتوای ویدیویی استفاده کرد. اغلب برای ایجاد تجربیات کاربری زیبا در برنامه های کاربردی وب غنی از رسانه و بازی های آنلاین استفاده می شود.

قابل اسکریپت است، به این معنی که محتوای ترسیم شده روی بوم را می توان به صورت برنامه نویسی، به عنوان مثال، در جاوا اسکریپت ایجاد کرد. این به بوم انعطاف پذیری زیادی می دهد.

در عین حال، در وب سایت های مدرن، اجرای اسکریپت یکی از رایج ترین منابع مشکلات پاسخگویی کاربر است. از آنجایی که منطق بوم و رندر در همان رشته تعامل کاربر اتفاق می افتد، محاسبات (گاهی اوقات سنگین) درگیر در انیمیشن ها می تواند به عملکرد واقعی و درک شده برنامه آسیب برساند.

خوشبختانه، OffscreenCanvas پاسخی به آن تهدید است.

پشتیبانی مرورگر

  • کروم: 69.
  • لبه: 79.
  • فایرفاکس: 105.
  • سافاری: 16.4.

منبع

قبلاً، قابلیت‌های طراحی بوم به عنصر <canvas> گره خورده بود، که به این معنی بود که مستقیماً به DOM وابسته بود. همانطور که از نامش پیداست، OffscreenCanvas با انتقال آن به خارج از صفحه، DOM و Canvas API را جدا می کند.

به لطف این جداسازی، رندر OffscreenCanvas به طور کامل از DOM جدا می شود و بنابراین برخی از بهبودهای سرعت را نسبت به بوم معمولی ارائه می دهد زیرا هیچ هماهنگی بین این دو وجود ندارد.

اما نکته بیشتر این است که می توان از آن در Web Worker استفاده کرد، حتی اگر DOM در دسترس نباشد. این امکان انواع موارد استفاده جالب را فراهم می کند.

از OffscreenCanvas در یک کارگر استفاده کنید

Workers نسخه وب رشته ها هستند—آنها به شما اجازه می دهند وظایف را در پس زمینه اجرا کنید.

انتقال برخی از اسکریپت‌های خود به یک Worker به برنامه شما فضای بیشتری برای انجام وظایف حیاتی کاربر در رشته اصلی می‌دهد. بدون OffscreenCanvas، هیچ راهی برای استفاده از Canvas API در یک کارگر وجود نداشت، زیرا 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 )

موضوع اصلی را رفع انسداد کنید

انتقال محاسبات سنگین به یک کارگر به شما امکان می دهد منابع قابل توجهی را در موضوع اصلی آزاد کنید. از روش transferControlToOffscreen برای انعکاس بوم معمولی به نمونه OffscreenCanvas استفاده کنید. عملیات اعمال شده روی OffscreenCanvas به طور خودکار بر روی بوم منبع ارائه می شود.

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

در مثال زیر، محاسبه سنگین زمانی اتفاق می‌افتد که تم رنگی در حال تغییر است - حتی در یک دسک‌تاپ سریع باید چند میلی‌ثانیه طول بکشد. شما می توانید انتخاب کنید که انیمیشن ها روی رشته اصلی یا کارگر اجرا شوند. در مورد رشته اصلی، نمی‌توانید در حالی که کار سنگین در حال اجرا است با دکمه تعامل داشته باشید - رشته مسدود شده است. در مورد کارگر، هیچ تاثیری بر پاسخگویی UI وجود ندارد.

نسخه ی نمایشی

این به روش دیگری نیز کار می کند: موضوع اصلی شلوغ بر انیمیشنی که روی یک کارگر اجرا می شود تأثیر نمی گذارد. همانطور که در نسخه ی نمایشی زیر نشان داده شده است، می توانید از این ویژگی برای جلوگیری از jank بصری و تضمین یک انیمیشن روان با وجود ترافیک رشته اصلی استفاده کنید.

نسخه ی نمایشی

در مورد بوم معمولی، انیمیشن زمانی متوقف می شود که موضوع اصلی به طور مصنوعی بیش از حد کار شود، در حالی که OffscreenCanvas مبتنی بر کارگر به آرامی پخش می شود.

از آنجایی که OffscreenCanvas API به طور کلی با Canvas Element معمولی سازگار است، می توانید از آن به عنوان یک پیشرفت پیشرونده، همچنین با برخی از کتابخانه های گرافیکی پیشرو در بازار استفاده کنید.

به عنوان مثال، می‌توانید آن را شناسایی کنید و در صورت وجود، با تعیین گزینه canvas در سازنده رندر، از آن با Three.js استفاده کنید:

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 در یک worker نشان داده شده است:

نسخه ی نمایشی

به خاطر داشته باشید که برخی از API های مرتبط با DOM به راحتی در یک Worker در دسترس نیستند، بنابراین اگر می خواهید از ویژگی های پیشرفته تر Three.js مانند بافت ها استفاده کنید، ممکن است به راه حل های بیشتری نیاز داشته باشید. برای برخی ایده‌ها در مورد چگونگی شروع آزمایش با این موارد، به ویدیوی Google I/O 2017 نگاهی بیندازید.

اگر به شدت از قابلیت‌های گرافیکی بوم استفاده می‌کنید، OffscreenCanvas می‌تواند بر عملکرد برنامه شما تأثیر مثبت بگذارد. در دسترس قرار دادن زمینه های رندر بوم برای کارگران، موازی سازی در برنامه های کاربردی وب را افزایش می دهد و باعث استفاده بهتر از سیستم های چند هسته ای می شود.

منابع اضافی