jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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}

APIMục đích {Purpose}
layoutsubtree attributeOpt-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 eventFire 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}

html2canvasHTML-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)
PerformanceChậ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 updatesPhải gọi lại mỗi frame {Must re-call each frame}Paint event tự động {Paint event automatic}
AccessibilityMất hoàn toàn {Completely lost}Giữ nguyên DOM {DOM preserved}
GPU supportKhông {No}WebGL + WebGPU texture
Browser supportMọ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}CanvasSVG
Số lượng edge {Number of edges}> 100 → Canvas wins< 100 → SVG fine
AnimationrequestAnimationFrameCSS animations, SMIL
Interactivity trên edgeManual hit testing {Thủ công}Native DOM events
Zoom qualityCần redraw {Need redraw}Vector, auto sharp {Tự động sắc nét}
MemoryBitmap bufferDOM 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)SVGCanvas 2DWebGL/WebGPU
Performance với nhiều element {Performance with many elements}Chậm > 1000 {Slow > 1000}Chậm > 500 {Slow > 500}Tốt > 10KTốt > 100K
Text renderingNative, perfectGood, nhưng flow layout yếu {but weak flow layout}Manual, khó {Manual, hard}Manual, rất khó {very hard}
AccessibilityFullPartial (aria)Không {None}Không {None}
InteractivityNative eventsNative eventsManual hit testingManual hit testing
GPU accelerationLimited (transform/opacity)Limited2D context (CPU)Full GPU
Zoom/PanCSS transform (reflow risk)viewBox (vector)Manual redrawMatrix 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)}

AspectStatus
SpecWICG proposal, được review tại Google I/O 2026 {reviewed at Google I/O 2026}
ChromeCanary (Chromium 147+) với flag canvas-draw-element
FirefoxNegative signal (concerns về fingerprinting)
SafariChưa có signal {No signal yet}
Origin TrialM148–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}

  1. Cài Chrome Canary (149+) {Install Chrome Canary}
  2. Vào chrome://flags/#canvas-draw-element → Enable
  3. Restart browser
  4. 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}:

  • layoutsubtree attribute 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ó display khác none {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 riskRender pixels có thể khác nhau giữa các browser/OS → fingerprint vector
Cross-origin restrictionTương tự canvas tainted — cross-origin content có thể bị chặn {Similar to tainted canvas — cross-origin content may be blocked}
Snapshot timingdrawElementImage() 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}