เป็นเวลาหลายปีที่นักพัฒนาเว็บต้องตัดสินใจเลือกสถาปัตยกรรมที่ยากลำบากเมื่อสร้างแอปพลิเคชันภาพที่ซับซ้อนและมีการโต้ตอบสูงบนเว็บ นั่นคือจะใช้ DOM สำหรับฟีเจอร์ความหมายที่หลากหลาย หรือจะแสดงผลโดยตรงไปยังองค์ประกอบ <canvas> เพื่อประสิทธิภาพกราฟิกในระดับต่ำ
HTML-in-Canvas API แบบทดลองใช้ใหม่ซึ่งพร้อมให้บริการแล้วในช่วงทดลองใช้จากต้นทางจะช่วยให้คุณไม่ต้องเลือก API นี้ช่วยให้คุณวาดเนื้อหา DOM ลงใน Canvas 2 มิติหรือพื้นผิว WebGL/WebGPU ได้โดยตรง ในขณะที่ยังคงให้ UI โต้ตอบได้ เข้าถึงได้ และเชื่อมต่อกับฟีเจอร์เบราว์เซอร์โปรด การผสานรวม HTML กับการประมวลผลกราฟิกในระดับต่ำจะช่วยให้คุณสร้างประสบการณ์ที่ก่อนหน้านี้ไม่สามารถทำได้
DOM กับ Canvas
หากต้องการทำความเข้าใจประสิทธิภาพของ API ใหม่นี้ คุณควรพิจารณาความแข็งแกร่งที่เกี่ยวข้องของทั้ง DOM และ Canvas
DOM เป็นองค์ประกอบหลักของ UI บนเว็บ โดยมีโซลูชันการจัดวางข้อความสำเร็จรูป ซึ่งใช้เนื้อหาที่เข้าใจความหมายเพื่อสร้างอินเทอร์เฟซที่สมบูรณ์ ซึ่งช่วยให้ผู้ใช้ดำเนินการทั่วไปในหน้าเว็บได้อย่างราบรื่น เช่น การไฮไลต์ข้อความเพื่อคัดลอก หรือการคลิกขวาที่รูปภาพเพื่อบันทึก นอกจากนี้ DOM ยังผสานรวมกับฟีเจอร์สำคัญของเบราว์เซอร์ ได้แก่ เครื่องมือช่วยเหลือพิเศษ แปลภาษา ค้นหาในหน้าเว็บ โหมดโปรแกรมอ่าน ส่วนขยาย โหมดมืด การซูมเบราว์เซอร์ และการป้อนข้อความอัตโนมัติ
ในทางกลับกัน Canvas (และ WebGL/WebGPU) จะอนุญาตให้เข้าถึงระดับต่ำเพื่อขับเคลื่อนตารางพิกเซลสำหรับกราฟิก 2 มิติและ 3 มิติขั้นสูง เกมและเว็บแอปที่ซับซ้อน (เช่น Google เอกสารหรือ Figma) ต้องใช้การเข้าถึงระดับต่ำที่มีประสิทธิภาพนี้ เนื่องจาก Canvas เป็นตารางพิกเซลโดยพื้นฐาน การรองรับฟีเจอร์ต่างๆ เช่น ข้อความที่ปรับเปลี่ยนตามพื้นที่ จึงต้องใช้ตรรกะ UI ที่กำหนดเองที่ซับซ้อน ซึ่งจะเพิ่มขนาด Bundle อย่างมาก ที่สำคัญ ฟีเจอร์เบราว์เซอร์ที่มีประสิทธิภาพทั้งหมดที่ผสานรวมเข้ากับ DOM จะหยุดทำงานโดยสิ้นเชิงเมื่อ UI ติดอยู่ในตารางพิกเซล Canvas แบบคงที่
ข้อดีของการนำ DOM ไปยัง Canvas
HTML-in-Canvas API เป็นสะพานที่ช่วยให้คุณได้ประโยชน์จากทั้ง 2 อย่าง การวาง HTML ไว้ภายในองค์ประกอบ <canvas> และการซิงค์การเปลี่ยนรูปแบบจะช่วยให้มั่นใจได้ว่าเนื้อหายังคงโต้ตอบได้อย่างเต็มที่ และการผสานรวมเบราว์เซอร์ทั้งหมดจะทำงานโดยอัตโนมัติ
สิ่งที่คุณจะได้รับจากการให้ DOM จัดการ UI ภายในองค์ประกอบ <canvas> มีดังนี้
- การจัดวางและจัดรูปแบบข้อความ: การจัดวางและจัดรูปแบบข้อความที่เรียบง่าย รวมถึงข้อความหลายบรรทัดหรือข้อความสองทิศทางที่มีการใช้สไตล์ CSS
- การควบคุมแบบฟอร์ม: การควบคุมแบบฟอร์มที่สื่อความหมายและใช้งานง่ายขึ้นพร้อมตัวเลือกการปรับแต่งที่หลากหลาย
- การเลือกข้อความ การคัดลอก/วาง และการคลิกขวา: ผู้ใช้สามารถไฮไลต์ข้อความภายในฉาก 3 มิติ หรือคลิกขวาที่เมนูตามบริบทได้โดยตรง
- การเลือกข้อความ การคัดลอก/วาง และการคลิกขวา: ผู้ใช้สามารถไฮไลต์ข้อความภายในฉาก 3 มิติ หรือคลิกขวาที่เมนูตามบริบทได้โดยตรง
- การช่วยเหลือพิเศษ: เนื้อหาที่แสดงใน Canvas จะแสดงในโครงสร้างการช่วยเหลือพิเศษ ระบบการช่วยเหลือพิเศษสามารถแยกวิเคราะห์ UI ได้เช่นเดียวกับ HTML ปกติ และแสดงต่อระบบต่างๆ เช่น โปรแกรมอ่านหน้าจอ
- Find-in-page: ผู้ใช้สามารถใช้ฟีเจอร์ค้นหาในหน้าเว็บ (Ctrl/Cmd+F) เพื่อค้นหาข้อความ และเบราว์เซอร์จะไฮไลต์ข้อความนั้นโดยตรงภายในพื้นผิว WebGL
- Find-in-page: ผู้ใช้สามารถใช้ฟีเจอร์ค้นหาในหน้าเว็บ (Ctrl/Cmd+F) เพื่อค้นหาข้อความ และเบราว์เซอร์จะไฮไลต์ข้อความนั้นโดยตรงภายในพื้นผิว WebGL
- ความสามารถในการจัดทำดัชนีและอินเทอร์เฟซ AI Agent: Web Crawler และ AI Agent สามารถจัดทำดัชนีและอ่านข้อความที่แสดงในฉาก 2 มิติและ 3 มิติได้อย่างราบรื่น
- การผสานรวมส่วนขยาย: ส่วนขยายของเบราว์เซอร์จะทำงานโดยค่าเริ่มต้น เช่น ส่วนขยายการแทนที่ข้อความจะอัปเดตข้อความที่แสดงในโมเดล 3 มิติโดยอัตโนมัติ
- การผสานรวม DevTools: คุณสามารถตรวจสอบเนื้อหา Canvas รวมถึงองค์ประกอบ UI ของ WebGL/WebGPU ได้โดยตรงในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ปรับแต่งสไตล์ CSS ในเครื่องมือตรวจสอบ แล้วดูการอัปเดตพื้นผิว 3 มิติได้ทันที
กรณีการใช้งานระดับสูง
API นี้ปลดล็อกศักยภาพอันน่าทึ่งในหลายโดเมน ดังนี้
- แอปพลิเคชันขนาดใหญ่ที่ใช้ Canvas: ตอนนี้เว็บแอปที่มีขนาดใหญ่ เช่น Google เอกสาร, Miro หรือ Figma สามารถแสดงผลคอมโพเนนต์ UI ของแอปพลิเคชันที่ซับซ้อนในพื้นที่ทำงานที่ขับเคลื่อนด้วย Canvas ได้โดยตรง ซึ่งจะช่วยปรับปรุงการเข้าถึงและลดขนาดของแพ็กเกจ
- ฉากและเกม 3 มิติ: ตอนนี้เว็บไซต์การตลาด ประสบการณ์ WebXR ที่สมจริง และเกมบนเว็บสามารถวาง UI ของเว็บที่โต้ตอบได้อย่างเต็มที่ลงในฉาก 3 มิติได้แล้ว เช่น หนังสือ 3 มิติที่ใช้ข้อความ DOM จริง หรือเทอร์มินัลในเกมที่รองรับการคัดลอกและวางโดยกำเนิด
วิธีใช้ API
การใช้ API จะเกิดขึ้นใน 3 เฟส ได้แก่ การตั้งค่า Canvas การแสดงผลใน Canvas และการอัปเดตการเปลี่ยนรูปแบบ CSS เพื่อให้เบราว์เซอร์ทราบตำแหน่งที่องค์ประกอบอยู่บนหน้าจอ
ข้อกำหนดเบื้องต้น
HTML-in-Canvas API อยู่ในช่วงทดลองใช้จากต้นทางใน Chrome 148 ถึง 150 หากต้องการทดสอบในเว็บไซต์ ให้ใช้ Chrome Canary 149 ขึ้นไปโดยเปิดใช้ Flag chrome://flags/#canvas-draw-element หากต้องการเปิดใช้ API สำหรับผู้ใช้รายอื่น ให้ลงทะเบียนช่วงทดลองใช้ Origin
ขั้นตอนที่ 1: การตั้งค่า Canvas ขั้นพื้นฐาน
ก่อนอื่น ให้เพิ่มแอตทริบิวต์ layoutsubtree ลงในแท็ก <canvas> ซึ่งจะทำให้เบราว์เซอร์ทราบถึงเนื้อหาที่ซ้อนอยู่ภายใน Canvas เตรียมพร้อมที่จะแสดงเนื้อหาภายใน Canvas และเปิดเผยเนื้อหาดังกล่าวต่อโครงสร้างการช่วยเหลือพิเศษ
<canvas id="canvas" style="width: 200px; height: 200px;" layoutsubtree>
<div id="form_element">
<label for="name">Name:</label> <input id="name" type="text">
</div>
</canvas>
ปรับขนาดตารางกริดของ Canvas
โปรดตรวจสอบว่าได้ปรับขนาดตารางกริดของ Canvas ให้ตรงกับค่าตัวคูณมาตราส่วนของอุปกรณ์เพื่อไม่ให้เนื้อหาที่แสดงผลเบลอ
const observer = new ResizeObserver(([entry]) => {
const dpc = entry.devicePixelContentBoxSize;
canvas.width = dpc ? dpc[0].inlineSize : Math.round(entry.contentRect.width * window.devicePixelRatio);
canvas.height = dpc ? dpc[0].blockSize : Math.round(entry.contentRect.height * window.devicePixelRatio);
});
const supportsDevicePixelContentBox =
typeof ResizeObserverEntry !== 'undefined' &&
'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);
ขั้นตอนที่ 2: การแสดงผล
สำหรับบริบท 2 มิติ ให้ใช้เมธอด drawElementImage ทําเช่นนี้ภายใน paint event ซึ่งจะทริกเกอร์เมื่อใดก็ตามที่องค์ประกอบวาดใหม่ เช่น ระหว่างการไฮไลต์ข้อความหรือข้อมูลจากผู้ใช้ คุณต้องอัปเดตการเปลี่ยน CSS ขององค์ประกอบด้วยค่าที่ส่งคืนเพื่อให้การโต้ตอบทํางานต่อไป
const ctx = document.getElementById('canvas').getContext('2d');
const form_element = document.getElementById('form_element');
const canvas = document.getElementById('canvas');
canvas.onpaint = () => {
ctx.reset();
// Draw the form element at x:0, y:0
let transform = ctx.drawElementImage(form_element, 0, 0);
// Use the transform returned later on...
};
แสดงผลด้วย WebGL
สำหรับ WebGL คุณใช้ texElementImage2D ซึ่งทำงานคล้ายกับ texImage2D แต่ใช้ DOM Element เป็นแหล่งที่มา
canvas.onpaint = () => {
if (gl.texElementImage2D) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element);
}
};
แสดงผลด้วย WebGPU
WebGPU ใช้เมธอด copyElementImageToTexture ในคิวอุปกรณ์ ซึ่งคล้ายกับ copyExternalImageToTexture ดังนี้
canvas.onpaint = () => {
root.device.queue.copyElementImageToTexture(
valueElement,
{ texture: targetTexture }
);
};
ขั้นตอนที่ 3: อัปเดตการเปลี่ยนรูปแบบ CSS
เมื่อเรนเดอร์องค์ประกอบลงใน Canvas แล้ว คุณจะต้องอัปเดตเบราว์เซอร์เกี่ยวกับตำแหน่งขององค์ประกอบ ซึ่งช่วยให้มั่นใจได้ว่าการซิงค์เชิงพื้นที่ระหว่าง Canvas กับเลย์เอาต์ของ DOM จะเป็นไปอย่างถูกต้อง ซึ่งเป็นสิ่งสำคัญเพื่อให้เบราว์เซอร์สามารถจับคู่โซนเหตุการณ์ได้อย่างถูกต้อง เช่น ตำแหน่งที่ผู้ใช้คลิกหรือวางเมาส์ กับตำแหน่งที่แสดงผลองค์ประกอบ
สำหรับกรณีบริบท 2 มิติ ให้ใช้การแปลงที่การเรียกการแสดงผลส่งคืนไปยัง .style.transform property
const ctx = document.getElementById('canvas').getContext('2d');
const form_element = document.getElementById('form_element');
const canvas = document.getElementById('canvas');
canvas.onpaint = () => {
ctx.reset();
// Draw the form element at x:0, y:0
let transform = ctx.drawElementImage(form_element, 0, 0);
// Sync the DOM location with the drawn location
form_element.style.transform = transform.toString();
};
เมื่อใช้ WebGL หรือ WebGPU ตำแหน่งบนหน้าจอขององค์ประกอบจะขึ้นอยู่กับวิธีที่โค้ด Shader ใช้เท็กซ์เจอร์เอาต์พุต และไม่สามารถอนุมานจากบริบทการแสดงผล Canvas ได้ อย่างไรก็ตาม หากโปรแกรม Shader ใช้การฉายภาพมุมมองโมเดลทั่วไปเพื่อวาดพื้นผิว คุณจะใช้ฟังก์ชันอำนวยความสะดวกใหม่ element.getElementTransform() เพื่อคำนวณการเปลี่ยนรูปแบบที่ใช้ในลักษณะเดียวกับค่าที่ส่งคืนจาก drawElementImage() ได้ หากต้องการดำเนินการดังกล่าว คุณต้องทำดังนี้
- แปลง MVP Matrix ของ WebGL เป็น DOM Matrix
- ทำให้องค์ประกอบ HTML เป็นมาตรฐาน องค์ประกอบ HTML มีขนาดเป็นพิกเซล (เช่น กว้าง 200 พิกเซล) อย่างไรก็ตาม โดยปกติแล้ว WebGL จะถือว่าออบเจ็กต์เป็น "สี่เหลี่ยมจัตุรัสหน่วย" เช่น ตั้งแต่ 0 ถึง 1 หากไม่ปรับให้เป็นมาตรฐาน ปุ่มขนาด 200 พิกเซลจะดูใหญ่ขึ้น 200 เท่า
- แมปกับวิวพอร์ต Canvas ขั้นตอนนี้คือเฟส "การปรับขนาด" ซึ่งจะขยายการคำนวณพื้นที่หน่วยนั้นกลับไปให้ตรงกับขนาดพิกเซลจริงขององค์ประกอบ
<canvas>บนหน้าจอ นอกจากนี้ยังพลิกแกน Y ด้วย เนื่องจากใน WebGL ขึ้นคือบวก แต่ใน CSS ลงคือบวก - คำนวณการแปลงขั้นสุดท้าย คูณเมทริกซ์ตามลำดับ:
Viewport * MVP * Normalization.การรวมเมทริกซ์เป็นทรานส์ฟอร์มสุดท้ายจะสร้าง "แผนที่" ที่บอกเบราว์เซอร์อย่างชัดเจนว่าเลเยอร์องค์ประกอบ HTML นั้นควรอยู่ที่ใดเพื่อให้สอดคล้องกับการวาดภาพ 3 มิติ - ใช้การเปลี่ยนรูปแบบกับองค์ประกอบ HTML ซึ่งจะย้ายเลเยอร์องค์ประกอบ HTML ไปอยู่ด้านบนสุดของพิกเซลที่แสดงผล ซึ่งจะช่วยให้มั่นใจได้ว่าเมื่อผู้ใช้คลิกปุ่มหรือเลือกข้อความ ผู้ใช้จะคลิกองค์ประกอบ HTML จริง
if (canvas.getElementTransform) {
// 1. Convert WebGL MVP Matrix to DOM Matrix
const mvpDOM = new DOMMatrix(Array.from(htmlElementMVP));
// 2. Normalize the HTML element (pixels -> 1x1 unit square)
const width = targetHTMLElement.offsetWidth;
const height = targetHTMLElement.offsetHeight;
const cssToUnitSpace = new DOMMatrix()
.scale(1 / width, -1 / height, 1) // Shrink to unit size and flip Y
.translate(-width / 2, -height / 2); // Center the element
// 3. Map to the canvas viewport
const clipToCanvasViewport = new DOMMatrix()
.translate(canvas.width / 2, canvas.height / 2) // Move origin to center
.scale(canvas.width / 2, -canvas.height / 2, 1); // Stretch to canvas dimensions
// 4. Multiply: (Clip -> Pixels) * (MVP) * (pixels -> unit square)
const screenSpaceTransform = clipToCanvasViewport
.multiply(mvpDOM)
.multiply(cssToUnitSpace);
// 5. Apply to the transform
const computedTransform = canvas.getElementTransform(targetHTMLElement, screenSpaceTransform);
if (computedTransform) {
targetHTMLElement.style.transform = computedTransform.toString();
}
}
การรองรับไลบรารีและเฟรมเวิร์ก
ไลบรารียอดนิยมบางรายการได้เปิดตัวการรองรับฟีเจอร์ HTML ใน Canvas แล้ว
Three.js
การอัปเดตเมทริกซ์ด้วยตนเองอาจเป็นเรื่องน่าเบื่อ ซึ่งเป็นเหตุผลที่เฟรมเวิร์กต่างๆ เริ่มหันมาใช้การอัปเดตอัตโนมัติ Three.js มีการรองรับเวอร์ชันทดลองโดยใช้ THREE.HTMLTexture ใหม่
const material = new THREE.MeshBasicMaterial();
material.map = new THREE.HTMLTexture(uiElement); // Pass the DOM element
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
PlayCanvas
PlayCanvas ยังรองรับ HTML ใน Canvas โดยใช้ Texture API ของตนเองด้วย
// Wait for the 'paint' event to set the source
canvas.addEventListener('paint', () => {
htmlTexture.setSource(htmlElement);
}, { once: true });
canvas.requestPaint();
// Keep up to date
canvas.addEventListener('paint', onPaintUpload);
const material = new pc.StandardMaterial();
material.diffuseMap = htmlTexture;
material.update();
การสาธิต
ก่อนลองใช้เดโม โปรดตรวจสอบว่าสภาพแวดล้อมได้รับการกำหนดค่าอย่างถูกต้อง
มีการสาธิตหลายรายการที่ใช้เป็นข้อมูลอ้างอิงสำหรับการใช้ API ได้ เราเห็นโซลูชันที่สร้างสรรค์จากชุมชนอยู่แล้ว ตั้งแต่หนังสือ 3 มิติที่แปลได้ไปจนถึงองค์ประกอบ UI ที่หักเหผ่านเชเดอร์แก้ว
- หนังสือ 3 มิติ: หนังสือ 3 มิติที่แสดงผลด้วย WebGL ซึ่งใช้เลย์เอาต์ HTML สำหรับหน้าต่างๆ ผู้ใช้สามารถสลับแบบอักษรด้วย CSS ได้ เนื่องจากเป็นการแปลที่อิงตาม DOM การแปลในตัวจึงทำงานได้ทันที และเอเจนต์ AI สามารถดึงข้อความออกมาได้โดยมีความซับซ้อนน้อยลง
- UI 3 มิติแบบอินเทอร์แอกทีฟ: แถบเลื่อนเยลลี่ WebGPU ที่หักเหแสงตามโมเดล 3 มิติพื้นฐาน ขณะที่ยังคงตอบสนองต่อแอตทริบิวต์
<input type="range">step ของ HTML มาตรฐาน - พื้นผิวแบบเคลื่อนไหว: ป้ายบิลบอร์ด 3 มิติแบบไดนามิกที่แสดงผลดินสอ SVG แบบเคลื่อนไหวโดยใช้ DOM โดยตรงในพื้นผิว WebGL โดยไม่ต้องใช้ลูปภาพเคลื่อนไหวที่กำหนดเอง
- ภาพซ้อนทับแบบหักเห: เลเยอร์การพิมพ์แบบอินเทอร์แอกทีฟที่บิดเบือนโดยเคอร์เซอร์ 3 มิติที่เคลื่อนไหว แต่ยังเลือกและค้นหาได้โดยใช้ฟีเจอร์ค้นหาในหน้าเว็บ
ดูคอลเล็กชันเดโมที่ชุมชนสร้างขึ้น หากต้องการให้เดโม HTML ใน Canvas แสดงในคอลเล็กชันนี้ โปรดสร้าง Pull Requestเพื่อเพิ่ม
ข้อจำกัด
แม้ว่า API จะมีประสิทธิภาพ แต่ก็มีข้อจำกัดบางประการที่ตั้งใจไว้ ดังนี้
- เนื้อหาข้ามต้นทาง: API จะใช้กับเนื้อหา iframe ข้ามต้นทางไม่ได้เนื่องด้วยเหตุผลด้านความปลอดภัยและความเป็นส่วนตัว
- การเลื่อนในเทรดหลัก: HTML ใน Canvas จะวาดด้วย JavaScript ซึ่งหมายความว่าการเลื่อนและภาพเคลื่อนไหวจะอัปเดตแยกจาก JavaScript ไม่ได้เหมือนกับที่ทำนอก Canvas นักพัฒนาแอปควรพิจารณาลักษณะประสิทธิภาพของการวางเนื้อหาที่เลื่อนได้ภายใน Canvas อย่างรอบคอบเทียบกับการให้ Canvas ทั้งหมดเลื่อน
ความคิดเห็น
หากคุณกำลังทดลองใช้ HTML-in-Canvas API เราอยากทราบความคิดเห็นจากคุณ คุณสามารถลงชื่อสมัครใช้ช่วงทดลองใช้จากต้นทางเพื่อเปิดใช้ฟีเจอร์ในเว็บไซต์ขณะที่ฟีเจอร์ยังอยู่ในระยะทดลองเพื่อช่วยเราออกแบบ API นอกจากนี้ คุณยังยื่นปัญหาเพื่อแสดงความคิดเห็นได้ด้วย
แหล่งข้อมูล
- การรองรับ HTML ใน Canvas ใน Three.js
- การสาธิต HTML ใน Canvas ใน Three.js
- การรองรับ HTML ใน Canvas ใน PlayCanvas: เอกสารประกอบสำหรับนักพัฒนาซอฟต์แวร์
- HTML ใน Canvas ในการสาธิต PlayCanvas
- HTML ใน Canvas: คำอธิบาย
- คำแนะนำเกี่ยวกับเว็บสมัยใหม่สำหรับเครื่องมือการเขียนโค้ด AI สำหรับ HTML ใน Canvas
- การสาธิต HTML ใน Canvas ใน Chrome.dev
- คอลเล็กชันการสาธิต HTML ใน Canvas ที่ยอดเยี่ยมจากชุมชน