HTML in Canvas — Vì sao nó có thể thay đổi cách xây dựng web app đồ họa
Deep-dive vào WICG HTML-in-Canvas proposal: render HTML thật vào canvas/GPU texture, giữ nguyên accessibility và CSS. Kèm demo workflow editor thực tế với Canvas edges + HTML nodes.
Mở bài: Hai thế giới chưa bao giờ hoà hợp {Two worlds that never quite merged}
Trong 20 năm lịch sử web {In 20 years of web history}, chúng ta sống với hai paradigm tách biệt {we’ve lived with two separate paradigms}:
HTML/DOM — tuyệt vời cho UI {great for UI}: text layout {bố cục chữ}, accessibility {khả năng truy cập}, form controls {điều khiển form}, responsive design {thiết kế đáp ứng}. Nhưng DOM chậm khi render hàng nghìn element {But DOM is slow when rendering thousands of elements}, và không thể áp dụng GPU shader {and can’t apply GPU shaders}.
Canvas/WebGL — tuyệt vời cho graphics {great for graphics}: vẽ hàng triệu pixel mỗi frame {draws millions of pixels per frame}, shader effects {hiệu ứng shader}, 3D transforms. Nhưng Canvas là “hố đen” {But Canvas is a “black hole”}: không có text selection {no text selection}, không có accessibility tree {no accessibility tree}, không có browser extensions {no browser extensions}, không có find-in-page {no find-in-page}.
Mọi ứng dụng phức tạp — Figma, Miro, Excalidraw, workflow builders {trình dựng luồng} — đều phải chọn một trong hai {must choose one}, hoặc hack cả hai lại với nhau bằng overlay phức tạp {or hack both together with complex overlays}.
HTML-in-Canvas {HTML trong Canvas} là proposal của WICG muốn xoá bỏ ranh giới này {is a WICG proposal that wants to erase this boundary}.
HTML in Canvas là gì? {What is HTML in Canvas?}
Định nghĩa {Definition}
HTML-in-Canvas là một Web API proposal {đề xuất Web API} cho phép render trực tiếp HTML element vào canvas {that allows rendering HTML elements directly into canvas} — không phải chụp screenshot {not screenshots}, không phải rasterize offline {not offline rasterization}, mà là render live, styled, accessible HTML thành canvas pixel hoặc GPU texture {but live, styled, accessible HTML rendered as canvas pixels or GPU textures}.
Ba primitive mới {Three new primitives}
| API | Mục đích {Purpose} |
|---|---|
layoutsubtree attribute | Opt-in element vào layout bên trong canvas {Opt element into layout inside canvas} |
drawElementImage() | Vẽ element vào 2D/WebGL/WebGPU context {Draw element into 2D/WebGL/WebGPU context} |
paint event | Fire khi element thay đổi, cho phép redraw hiệu quả {Fires when element changes, enables efficient redraw} |
// API cơ bản — vẽ HTML element vào canvas
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const myDiv = document.querySelector('.my-styled-div');
canvas.addEventListener('paint', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const transform = ctx.drawElementImage(myDiv, 50, 50);
myDiv.style.transform = transform; // align DOM position for events
});
So sánh với html2canvas {Comparison with html2canvas}
| html2canvas | HTML-in-Canvas API | |
|---|---|---|
| Cách hoạt động {How it works} | Clone DOM, re-render thủ công {Clone DOM, re-render manually} | Native browser rendering {Render native bởi browser} |
| Accuracy {Độ chính xác} | ~80% (nhiều CSS không support) {many CSS unsupported} | 100% (browser tự render) |
| Performance | Chậm (parse + render lại toàn bộ) {Slow (re-parse + re-render all)} | Nhanh (dùng render pipeline sẵn có) {Fast (uses existing render pipeline)} |
| Live updates | Phải gọi lại mỗi frame {Must re-call each frame} | Paint event tự động {Paint event automatic} |
| Accessibility | Mất hoàn toàn {Completely lost} | Giữ nguyên DOM {DOM preserved} |
| GPU support | Không {No} | WebGL + WebGPU texture |
| Browser support | Mọi browser {All browsers} | Chrome Canary (flag) — 2026 |
Điểm khác biệt cốt lõi {Core difference}: html2canvas “chụp ảnh” HTML {html2canvas “takes a photo” of HTML}. HTML-in-Canvas giữ HTML sống và render nó trực tiếp vào GPU {HTML-in-Canvas keeps HTML alive and renders it directly to GPU}.
Vì sao nó hot? {Why is it hot?}
1. GPU Acceleration {Tăng tốc GPU}
HTML elements được render thành texture trên GPU {HTML elements rendered as GPU textures}. Nghĩa là bạn có thể {Meaning you can}:
- Zoom/pan không giới hạn mà không re-layout DOM {unlimited zoom/pan without DOM re-layout}
- Apply shader effects lên HTML content {apply shader effects on HTML content}
- Mix HTML với 3D geometry {mix HTML with 3D geometry}
2. Infinite Canvas Apps {Ứng dụng canvas vô hạn}
Figma, Miro, tldraw — tất cả dùng canvas cho performance {all use canvas for performance} nhưng phải reimplementing text rendering, accessibility, form controls {but must reimplement text rendering, accessibility, form controls}. HTML-in-Canvas cho phép dùng HTML thật trên infinite canvas {allows using real HTML on infinite canvas}.
3. Workflow Editors {Trình dựng workflow}
AI agent canvas, n8n, Retool Workflows — mỗi node là một UI phức tạp {each node is a complex UI} (buttons, inputs, dropdowns). Hiện tại phải overlay HTML lên canvas {Currently must overlay HTML on canvas}. Tương lai có thể render trực tiếp vào canvas {Future can render directly into canvas}.
4. Accessibility + Performance {Khả năng truy cập + Hiệu năng}
Element vẫn là DOM thật {Elements are still real DOM}: screen readers hoạt động {screen readers work}, find-in-page hoạt động {find-in-page works}, browser extensions hoạt động {browser extensions work}, autofill hoạt động {autofill works}. Nhưng rendering được GPU tăng tốc {But rendering is GPU-accelerated}.
Use Case: Workflow Builder {Trường hợp sử dụng: Trình dựng Workflow}
Workflow builder là gì? {What is a workflow builder?}
Một ứng dụng cho phép người dùng kết nối các “node” {An app that lets users connect “nodes”} thành chuỗi xử lý {into processing chains}. Mỗi node thực hiện một tác vụ {Each node performs a task}: gọi API {call API}, biến đổi data {transform data}, kiểm tra điều kiện {check condition}, gửi email {send email}.
┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌────────────┐
│ Webhook │───→│ Transform │───→│ Check │───→│ Send │
│ Trigger │ │ Data │ │ Amount │ │ Email │
└──────────┘ └──────────────┘ └─────────────┘ └────────────┘
Kiến trúc hiện tại {Current architecture}
Hầu hết workflow editors dùng HTML overlay pattern {Most workflow editors use HTML overlay pattern}:
- Canvas layer {Tầng Canvas}: vẽ connections/edges giữa các node {draws connections/edges between nodes}
- HTML layer {Tầng HTML}: render các node dưới dạng DOM element phía trên canvas {renders nodes as DOM elements on top of canvas}
Đây là cách Figma, React Flow, và hầu hết các thư viện flow/graph đều hoạt động {This is how Figma, React Flow, and most flow/graph libraries work}.
Demo: Workflow Editor với Canvas + HTML {Demo: Workflow Editor with Canvas + HTML}
Tôi đã xây dựng một demo hoàn chỉnh {I’ve built a complete demo} minh hoạ kiến trúc HTML overlay {demonstrating the HTML overlay architecture}. Bạn có thể xem live tại {You can see it live at} /tools/workflow-canvas-demo/.
API Design {Thiết kế API}
const workflow = createWorkflow("#app");
workflow.addNode({
id: "start",
type: "trigger",
title: "Webhook Trigger",
x: 100,
y: 120,
fields: [
{ label: "Method", value: "POST" },
{ label: "Path", value: "/api/order" }
]
});
workflow.addNode({
id: "transform",
type: "action",
title: "Transform Data",
x: 420,
y: 160,
fields: [
{ label: "Input", value: "order.payload" }
]
});
workflow.connect("start", "transform");
workflow.render();
Phân tích kiến trúc {Architecture analysis}
Vì sao node nên là HTML {Why nodes should be HTML}
- Text rendering miễn phí {Free text rendering}: font shaping, ligatures, RTL, line wrapping
- Interactivity miễn phí {Free interactivity}: click, hover, focus, input, dropdown đều hoạt động native {all work natively}
- Accessibility miễn phí {Free accessibility}: screen reader, keyboard navigation, aria attributes
- Styling miễn phí {Free styling}: CSS animations, transitions, responsive, theming
Vì sao edge nên là Canvas {Why edges should be Canvas}
- Performance {Hiệu năng}: hàng trăm bezier curves mỗi frame không vấn đề {hundreds of bezier curves per frame no problem}
- Visual quality {Chất lượng hình ảnh}: anti-aliased curves, custom line styles, gradients
- Hit testing {Kiểm tra va chạm}: dễ kiểm tra point-on-curve hơn SVG hit testing phức tạp {easier point-on-curve checking than complex SVG hit testing}
Khi nào dùng SVG thay Canvas {When to use SVG instead of Canvas}
| Tiêu chí {Criteria} | Canvas | SVG |
|---|---|---|
| Số lượng edge {Number of edges} | > 100 → Canvas wins | < 100 → SVG fine |
| Animation | requestAnimationFrame | CSS animations, SMIL |
| Interactivity trên edge | Manual hit testing {Thủ công} | Native DOM events |
| Zoom quality | Cần redraw {Need redraw} | Vector, auto sharp {Tự động sắc nét} |
| Memory | Bitmap buffer | DOM nodes per path |
Quy tắc {Rule}: dưới 100 elements → SVG đơn giản hơn {under 100 elements → SVG simpler}. Trên 100 hoặc cần custom rendering → Canvas {over 100 or need custom rendering → Canvas}.
Khi nào cần WebGL/WebGPU {When you need WebGL/WebGPU}
- Hàng nghìn nodes + edges đồng thời {Thousands of nodes + edges simultaneously}
- 3D effects trên nodes (flip, rotate, perspective)
- Custom shader effects (glow, blur, distortion)
- Particle systems cho visual feedback
Trade-offs giữa DOM, SVG, Canvas {Trade-offs between DOM, SVG, Canvas}
| DOM (HTML) | SVG | Canvas 2D | WebGL/WebGPU | |
|---|---|---|---|---|
| Performance với nhiều element {Performance with many elements} | Chậm > 1000 {Slow > 1000} | Chậm > 500 {Slow > 500} | Tốt > 10K | Tốt > 100K |
| Text rendering | Native, perfect | Good, nhưng flow layout yếu {but weak flow layout} | Manual, khó {Manual, hard} | Manual, rất khó {very hard} |
| Accessibility | Full | Partial (aria) | Không {None} | Không {None} |
| Interactivity | Native events | Native events | Manual hit testing | Manual hit testing |
| GPU acceleration | Limited (transform/opacity) | Limited | 2D context (CPU) | Full GPU |
| Zoom/Pan | CSS transform (reflow risk) | viewBox (vector) | Manual redraw | Matrix transforms |
Liên hệ với HTML-in-Canvas tương lai {Connection to future HTML-in-Canvas}
Hiện tại: HTML Overlay Pattern {Current: HTML Overlay Pattern}
┌────────────────────────────────────────┐
│ HTML Layer (position: absolute) │ ← Nodes (DOM elements)
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Node │ │Node │ │Node │ │
│ └─────┘ └─────┘ └─────┘ │
├────────────────────────────────────────┤
│ Canvas Layer │ ← Edges (bezier curves)
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │
└────────────────────────────────────────┘
Vấn đề {Problems}:
- Zoom/pan phải sync cả DOM và Canvas {must sync both DOM and Canvas}
- DOM reflow khi di chuyển node {DOM reflow when moving nodes}
- Không thể apply shader lên toàn bộ scene {Can’t apply shaders to entire scene}
- Z-index management phức tạp {complex z-index management}
Tương lai: HTML-in-Canvas Native {Future: Native HTML-in-Canvas}
┌────────────────────────────────────────┐
│ Canvas (single rendering context) │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │ ← Nodes drawn via
│ │Node │ │Node │ │Node │ │ drawElementImage()
│ └─────┘ └─────┘ └─────┘ │
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ ← Edges drawn via
│ │ bezier paths
└────────────────────────────────────────┘
│ DOM still exists (accessibility) │ ← Hidden but accessible
Lợi ích {Benefits}:
- Single rendering context {Ngữ cảnh render duy nhất}: mọi thứ trên cùng một canvas, không sync issue {everything on same canvas, no sync issues}
- GPU shader cho toàn scene {GPU shader for entire scene}: blur background khi zoom out, glow effect khi node active {blur background on zoom out, glow effect when node active}
- Zoom/pan native {Native zoom/pan}: chỉ cần thay đổi canvas transform matrix {just change canvas transform matrix}
- Accessibility giữ nguyên {Accessibility preserved}: DOM vẫn tồn tại cho screen readers {DOM still exists for screen readers}
// Tương lai: render node HTML trực tiếp vào WebGPU texture
// {Future: render node HTML directly into WebGPU texture}
canvas.addEventListener('paint', () => {
for (const node of nodes) {
const transform = ctx.drawElementImage(node.element, node.x, node.y);
node.element.style.transform = transform;
}
drawEdges(ctx, connections);
});
Trạng thái hiện tại (2026) {Current Status (2026)}
| Aspect | Status |
|---|---|
| Spec | WICG proposal, được review tại Google I/O 2026 {reviewed at Google I/O 2026} |
| Chrome | Canary (Chromium 147+) với flag canvas-draw-element |
| Firefox | Negative signal (concerns về fingerprinting) |
| Safari | Chưa có signal {No signal yet} |
| Origin Trial | M148–M151 (khoảng 3 tháng) {about 3 months} |
| Production ready? | Chưa {Not yet} — chỉ nên experiment {only experiment} |
Cách thử nghiệm {How to experiment}
- Cài Chrome Canary (149+) {Install Chrome Canary}
- Vào
chrome://flags/#canvas-draw-element→ Enable - Restart browser
- Clone github.com/WICG/html-in-canvas để xem examples {to see examples}
Mở rộng thành Production App {Scaling to Production App}
Demo hiện tại là proof-of-concept {The current demo is a proof-of-concept}. Để xây production workflow editor {To build a production workflow editor}, cần thêm {you need}:
Architecture layers {Tầng kiến trúc}
┌─────────────────────────────────────┐
│ UI Layer (React/Vue/Svelte) │ Components, state management
├─────────────────────────────────────┤
│ Graph Engine │ Node/edge data model, layout algo
├─────────────────────────────────────┤
│ Renderer │ Canvas/SVG/WebGL abstraction
├─────────────────────────────────────┤
│ Interaction Manager │ Drag, zoom, pan, selection
├─────────────────────────────────────┤
│ Execution Engine │ Run the workflow, handle async
└─────────────────────────────────────┘
Tính năng cần thiết {Required features}
- Undo/redo {Hoàn tác/Làm lại}: command pattern trên graph state
- Serialization {Tuần tự hoá}: JSON export/import
- Minimap {Bản đồ thu nhỏ}: render toàn cảnh ở góc
- Snap-to-grid {Bám lưới}: alignment guides
- Group/ungroup {Nhóm/Tách nhóm}: compound nodes
- Keyboard shortcuts {Phím tắt}: copy, paste, delete, select all
- Collaboration {Cộng tác}: CRDT hoặc OT cho multi-user
Thư viện hiện có {Existing libraries}
- React Flow — React-based, HTML nodes + SVG edges
- Rete.js — Framework-agnostic node editor
- Litegraph.js — Canvas-based, Comfy UI dùng
- tldraw — Infinite canvas library
Hướng dẫn sử dụng HTML-in-Canvas API {Usage Guide for HTML-in-Canvas API}
Setup cơ bản {Basic setup}
<!-- Step 1: Canvas với layoutsubtree attribute -->
<canvas id="myCanvas" style="width: 600px; height: 400px;" layoutsubtree>
<!-- Step 2: HTML content là con trực tiếp của canvas -->
<div id="myContent" style="width: 200px; padding: 16px; background: #111; border: 1px solid #2a2a2a;">
<h3 style="color: #c8ff00;">Hello from HTML</h3>
<p>This is real DOM rendered into canvas.</p>
<button>Click me — I still work!</button>
</div>
</canvas>
Lưu ý quan trọng {Important notes}:
layoutsubtreeattribute trên<canvas>là bắt buộc {is required}- Element phải là con trực tiếp {must be a direct child} của canvas
- Element phải có
displaykhácnone{must have display other than none}
drawElementImage() — Hàm chính {The main function}
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const content = document.getElementById('myContent');
// Cách 1: Vẽ tại vị trí (x, y)
// {Method 1: Draw at position (x, y)}
canvas.onpaint = () => {
ctx.reset(); // Clear canvas
const transform = ctx.drawElementImage(content, 100, 50);
// QUAN TRỌNG: sync DOM position để events hoạt động
// {IMPORTANT: sync DOM position so events work}
content.style.transform = transform.toString();
};
// Cách 2: Vẽ với kích thước tuỳ chỉnh
// {Method 2: Draw with custom size}
canvas.onpaint = () => {
ctx.reset();
// drawElementImage(element, x, y, width, height)
const transform = ctx.drawElementImage(content, 50, 50, 300, 200);
content.style.transform = transform.toString();
};
// Cách 3: Áp dụng canvas transforms (zoom, rotate)
// {Method 3: Apply canvas transforms (zoom, rotate)}
canvas.onpaint = () => {
ctx.reset();
// Zoom 2x và xoay 5 độ
// {Zoom 2x and rotate 5 degrees}
ctx.translate(300, 200);
ctx.scale(2, 2);
ctx.rotate(0.08);
const transform = ctx.drawElementImage(content, -100, -50);
content.style.transform = transform.toString();
};
Return value {Giá trị trả về}: drawElementImage() trả về một DOMMatrix {returns a DOMMatrix} — CSS transform cần áp dụng cho element để DOM position khớp với vị trí vẽ {the CSS transform needed so DOM position matches drawn position}. Điều này đảm bảo click events, focus, accessibility hoạt động đúng {This ensures click events, focus, accessibility work correctly}.
paint event — Khi nào vẽ lại {When to redraw}
// paint event tự động fire khi content thay đổi
// {paint event automatically fires when content changes}
canvas.addEventListener('paint', (event) => {
ctx.reset();
// event.changedElements chứa danh sách element đã thay đổi
// {event.changedElements contains list of changed elements}
console.log('Changed:', event.changedElements);
// Vẽ lại tất cả elements
// {Redraw all elements}
for (const el of canvas.children) {
if (el.nodeType === Node.ELEMENT_NODE) {
const t = ctx.drawElementImage(el, getX(el), getY(el));
el.style.transform = t.toString();
}
}
});
// Yêu cầu vẽ lại thủ công (như requestAnimationFrame)
// {Request manual redraw (like requestAnimationFrame)}
canvas.requestPaint();
Khi nào paint event fire {When paint event fires}:
- Khi CSS của child element thay đổi (color, size, animation frame) {When child element’s CSS changes}
- Khi nội dung text thay đổi {When text content changes}
- Khi form input value thay đổi {When form input value changes}
- KHÔNG fire khi chỉ thay đổi
transform{Does NOT fire when only transform changes}
captureElementImage() — OffscreenCanvas {For OffscreenCanvas}
// Capture snapshot để dùng trong Worker
// {Capture snapshot for use in Worker}
canvas.onpaint = () => {
const elementImage = canvas.captureElementImage(content);
// Transfer sang worker
worker.postMessage({ elementImage }, [elementImage]);
};
// Trong worker:
// {In worker:}
self.onmessage = (e) => {
if (e.data.elementImage) {
const t = offscreenCtx.drawElementImage(e.data.elementImage, 0, 0);
self.postMessage({ transform: t });
}
};
WebGL — HTML thành texture {HTML as texture}
const gl = canvas.getContext('webgl2');
canvas.onpaint = () => {
// Vẽ HTML element thành WebGL texture
// {Draw HTML element as WebGL texture}
gl.texElementImage2D(
gl.TEXTURE_2D, // target
0, // level
gl.RGBA, // internalFormat
gl.RGBA, // format
gl.UNSIGNED_BYTE, // type
content // HTML element
);
// Bây giờ texture chứa rendered HTML
// {Now texture contains rendered HTML}
// Có thể map lên 3D geometry, apply shader, etc.
};
WebGPU — HTML thành GPU texture {HTML as GPU texture}
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const gpuCtx = canvas.getContext('webgpu');
canvas.onpaint = () => {
const texture = device.createTexture({
size: [canvas.width, canvas.height],
format: 'rgba8unorm',
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
});
// Copy HTML element vào GPU texture
// {Copy HTML element into GPU texture}
device.queue.copyElementImageToTexture(
content, // source HTML element
{ texture: texture } // destination GPU texture
);
};
Sizing và DPI handling {Sizing and DPI handling}
// Canvas grid phải match device pixel ratio để không bị mờ
// {Canvas grid must match device pixel ratio to avoid blurriness}
const observer = new ResizeObserver(([entry]) => {
canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
});
observer.observe(canvas, { box: 'device-pixel-content-box' });
Lưu ý và giới hạn {Notes and limitations}
| Vấn đề {Issue} | Chi tiết {Detail} |
|---|---|
| CSS transform trên element bị bỏ qua khi vẽ {CSS transforms on element ignored when drawing} | Browser bỏ qua transform của element khi render vào canvas, nhưng transform vẫn ảnh hưởng hit testing {Browser ignores element’s transform when rendering to canvas, but transform still affects hit testing} |
| Overflow bị clip {Overflow is clipped} | Content tràn khỏi border box bị cắt {Content overflowing border box is clipped} |
| Chỉ con trực tiếp {Direct children only} | drawElementImage() chỉ chấp nhận con trực tiếp của canvas {only accepts direct children of canvas} |
| Fingerprinting risk | Render pixels có thể khác nhau giữa các browser/OS → fingerprint vector |
| Cross-origin restriction | Tương tự canvas tainted — cross-origin content có thể bị chặn {Similar to tainted canvas — cross-origin content may be blocked} |
| Snapshot timing | drawElementImage() ngoài paint event dùng frame trước {outside paint event uses previous frame} |
Pattern: Multiple elements với transform {Multiple elements with transforms}
const nodes = [
{ el: document.getElementById('node1'), x: 100, y: 50 },
{ el: document.getElementById('node2'), x: 350, y: 150 },
{ el: document.getElementById('node3'), x: 600, y: 80 },
];
canvas.onpaint = () => {
ctx.reset();
// Apply global zoom/pan
ctx.translate(panX, panY);
ctx.scale(zoom, zoom);
// Draw each node
for (const node of nodes) {
const t = ctx.drawElementImage(node.el, node.x, node.y);
node.el.style.transform = t.toString();
}
// Draw edges (plain canvas API)
drawBezierEdges(ctx, connections, nodes);
};
Kết luận {Conclusion}
HTML-in-Canvas không phải silver bullet {is not a silver bullet}. Nó không thay thế DOM cho web app thông thường {It doesn’t replace DOM for normal web apps}, và không thay thế Canvas/WebGL cho game {and doesn’t replace Canvas/WebGL for games}.
Nhưng nó mở ra mô hình kết hợp mới {But it opens a new hybrid model}: ứng dụng có thể vừa giữ semantic HTML {apps can keep semantic HTML} (accessibility, SEO, browser features) vừa tận dụng GPU rendering {while leveraging GPU rendering} (shader, zoom, performance).
Nhận định {My take}: tương lai của web app phức tạp {the future of complex web apps} — workflow editors, design tools, AI canvas — là hybrid rendering {kết xuất lai}:
- HTML cho content {HTML for content}: text, forms, accessibility
- Canvas/WebGPU cho visuals {Canvas/WebGPU for visuals}: connections, effects, zoom
- HTML-in-Canvas cho bridge {HTML-in-Canvas for the bridge}: khi bạn cần cả hai trong cùng rendering context {when you need both in the same rendering context}
Trong khi chờ API ổn định {While waiting for the API to stabilize}, HTML overlay pattern (như demo ở trên) vẫn là cách tốt nhất {the HTML overlay pattern (as in our demo) remains the best approach}. Nó hoạt động hôm nay, trên mọi browser {It works today, on every browser}, và khi HTML-in-Canvas ship {and when HTML-in-Canvas ships}, migration path sẽ rõ ràng {the migration path will be clear}: thay overlay bằng drawElementImage() {replace overlay with drawElementImage()}.
Tài liệu tham khảo {References}
- WICG/html-in-canvas GitHub — Specification repo
- html-in-canvas.dev — Official demos và documentation
- Chrome Platform Status — Feature tracking
- Workflow Canvas Demo — Demo đi kèm bài viết này {Demo accompanying this post}