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

Tim Dresser

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

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

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

แต่โชคดีที่ OffscreenCanvas ตอบสนองต่อภัยคุกคามดังกล่าว

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

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

แหล่งที่มา

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

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

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

ใช้ OffscreenCanvas ใน Worker

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 )

เลิกบล็อกชุดข้อความหลัก

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

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

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

สาธิต

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

สาธิต

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

เนื่องจาก OffscreenCanvas API โดยทั่วไปเข้ากันได้กับองค์ประกอบ Canvas ปกติ คุณจึงใช้ API นี้เป็นการปรับปรุงแบบเป็นขั้นเป็นตอนได้ รวมถึงใช้กับไลบรารีกราฟิกชั้นนําบางรายการในตลาดได้ด้วย

เช่น คุณสามารถตรวจหาองค์ประกอบและนำไปใช้กับ 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});

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

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

สาธิต

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

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

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