OffscreenCanvas - เพิ่มความเร็วให้กับการทำงานของ Canvas ด้วย Web Worker

Tim Dresser

Canvas เป็นวิธียอดนิยม ของการวาดภาพกราฟิกทุกชนิดบนหน้าจอ และเป็นจุดแรกเข้าไปยังโลกของ WebGL ซึ่งใช้เพื่อวาดรูปร่าง รูปภาพ แสดงภาพเคลื่อนไหว หรือแม้แต่แสดงและประมวลผลเนื้อหาวิดีโอ มักใช้เพื่อสร้างประสบการณ์ของผู้ใช้ที่สวยงามในแอปพลิเคชันเว็บที่เต็มไปด้วยสื่อและ เกมออนไลน์

สคริปต์นี้เป็นแบบเขียนสคริปต์ได้ ซึ่งหมายความว่าเนื้อหาที่วาดบนผืนผ้าใบสามารถสร้างแบบเป็นโปรแกรมได้ เช่น ใน JavaScript วิธีนี้ทำให้แคนวาสมีความยืดหยุ่นมาก

ในขณะเดียวกัน ในเว็บไซต์สมัยใหม่ การใช้สคริปต์เป็นสิ่งหนึ่งที่ แหล่งที่มาของปัญหาการตอบสนองของผู้ใช้ เนื่องจากตรรกะของ Canvas และการแสดงภาพเกิดขึ้นในเทรดเดียวกันกับการโต้ตอบของผู้ใช้ การคำนวณ (บางครั้งอาจหนัก) ที่เกี่ยวข้องในภาพเคลื่อนไหวอาจเป็นอันตรายต่อ และประสิทธิภาพที่รับรู้ได้

โชคดีที่ OffscreenCanvas คือการตอบสนองต่อภัยคุกคามนั้น

การรองรับเบราว์เซอร์

  • 69
  • 79
  • 105
  • 16.4

แหล่งที่มา

ก่อนหน้านี้ ความสามารถในการวาด Canvas จะ เชื่อมโยงกับองค์ประกอบ <canvas> ซึ่งหมายความว่าขึ้นอยู่กับ DOM โดยตรง OffscreenCanvas ตามที่ชื่อบ่งบอก แยก DOM และ Canvas API ออกโดยการย้ายออกจากหน้าจอ

การแยกออกทำให้การแสดงผลของ OffscreenCanvas ถูกปลดออกจาก DOM และ จึงมีการปรับปรุงความเร็วบางประการเมื่อเทียบกับผืนผ้าใบปกติเนื่องจากไม่มีการซิงค์ข้อมูล ระหว่างเมตริกทั้งสองนี้

แต่ยิ่งไปกว่านั้น ยังสามารถใช้ใน Web Worker ได้แม้ว่าจะไม่มี DOM พร้อมใช้งาน ซึ่งจะเปิดใช้ Use Case ที่น่าสนใจทุกประเภท

ใช้ OffscreenCanvas ในผู้ปฏิบัติงาน

ผู้ปฏิบัติงาน คือชุดข้อความในเวอร์ชันเว็บ ซึ่งให้คุณเรียกใช้งานในเบื้องหลังได้

การย้ายสคริปต์บางส่วนไปให้ผู้ปฏิบัติงานช่วยให้แอปมีศักยภาพมากขึ้นในการทำงานที่สำคัญต่อผู้ใช้ งานในเทรดหลัก ถ้าไม่มี 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]);

ในตัวอย่างต่อไปนี้ การคำนวณจำนวนมากจะเกิดขึ้นเมื่อธีมสีเปลี่ยนไป ใช้เวลา 2-3 มิลลิวินาที แม้แต่บนเดสก์ท็อปที่ทำงานเร็ว คุณเลือกเรียกใช้ภาพเคลื่อนไหวในชุดข้อความหลักได้ หรือในผู้ปฏิบัติงาน ในกรณีของเทรดหลัก คุณจะไม่สามารถโต้ตอบกับปุ่มได้ขณะที่ งานกำลังทำงาน - ชุดข้อความถูกบล็อก ในกรณีที่ผู้ปฏิบัติงาน จะไม่ส่งผลกระทบต่อ การตอบสนองของ UI

การสาธิต

แต่จะทำงานในทางกลับกันด้วย กล่าวคือเทรดหลักที่ไม่ว่างจะไม่มีผลต่อภาพเคลื่อนไหวที่ทำงานอยู่ ผู้ปฏิบัติงาน คุณใช้ฟีเจอร์นี้เพื่อหลีกเลี่ยงภาพสั่นและรับประกันภาพเคลื่อนไหวได้อย่างราบรื่นได้ แม้จะมีการรับส่งข้อมูลของเทรดหลัก ดังที่แสดงในการสาธิตต่อไปนี้

การสาธิต

ในกรณีของผืนผ้าใบปกติ ภาพเคลื่อนไหวจะหยุดเมื่อเทรดหลักทำงานหนักเกินไป ขณะที่ OffscreenCanvas ที่อิงตามผู้ปฏิบัติงานจะเล่นได้อย่างราบรื่น

เนื่องจาก OffscreenCanvas API โดยทั่วไปแล้วสามารถทำงานร่วมกับ Canvas Element ปกติ คุณจึงสามารถดำเนินการต่อไปนี้ ใช้ในการเพิ่มประสิทธิภาพแบบต่อเนื่อง รวมไปถึงไลบรารีกราฟิกชั้นนำบางแห่งในตลาดด้วย

ตัวอย่างเช่น คุณสามารถตรวจหาฟีเจอร์ และหากมี ให้ใช้กับ Three.js โดยระบุ ตัวเลือก Canvas ในตัวสร้างโหมดแสดงภาพ

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});

Gotcha อย่างหนึ่งคือ Three.js ต้องการให้ Canvas มีพร็อพเพอร์ตี้ style.width และ style.height OffscreenCanvas ซึ่งถูกแยกออกจาก DOM โดยสมบูรณ์ไม่มี ด้วยเหตุนี้คุณจึงต้องระบุด้วยตนเอง ด้วยการตัดออกหรือให้ตรรกะที่ผูกค่าเหล่านี้กับต้นฉบับ ขนาดผืนผ้าใบ

ข้อมูลต่อไปนี้แสดงวิธีเรียกใช้ภาพเคลื่อนไหว Three.js พื้นฐานในผู้ปฏิบัติงาน

การสาธิต

อย่าลืมว่า API ที่เกี่ยวข้องกับ DOM บางส่วนอาจไม่พร้อมใช้งานในผู้ปฏิบัติงาน ดังนั้นหากคุณ ต้องการใช้ฟีเจอร์ Three.js ขั้นสูง เช่น พื้นผิว คุณอาจต้องใช้วิธีแก้ปัญหาเพิ่มเติม สําหรับแนวคิดบางส่วนเกี่ยวกับวิธีเริ่มทดลองใช้เครื่องมือเหล่านี้ โปรดดูที่ วิดีโอจากการประชุม Google I/O 2017

หากคุณใช้ความสามารถในการแสดงกราฟิกของ Canvas เป็นจำนวนมาก OutscreenCanvas สามารถให้ผลลัพธ์ในเชิงบวก ส่งผลต่อประสิทธิภาพแอปของคุณ การทำให้บริบทการแสดงผล Canvas พร้อมใช้งานสำหรับผู้ปฏิบัติงานเพิ่มขึ้น ทำงานพร้อมกันในเว็บแอปพลิเคชัน และใช้ประโยชน์จากระบบแบบหลายแกนได้ดีกว่า

แหล่งข้อมูลเพิ่มเติม