- HTML-in-Canvas là một dự án của WICG đề xuất một API chuẩn mới cho phép vẽ trực tiếp phần tử/nội dung HTML trong phần tử
<canvas> - Được đưa ra để giải quyết các vấn đề về bố cục văn bản phức tạp, accessibility, quốc tế hóa, chất lượng/hiệu năng, và cho phép tận dụng trực tiếp kiểu dáng HTML trong các trường hợp dùng Canvas đa dạng như biểu đồ, UI, menu game
- Thông qua các phương thức mới
drawElement,texElement2D,setHitTestRegions, có thể render và texture hóa phần tử HTML trực tiếp trên ngữ cảnh Canvas 2D hoặc WebGL - Bao gồm nhu cầu thực tế của nhà phát triển web app về vùng tương tác, cải thiện accessibility, UI 2D trong cảnh 3D, layout dựa trên CSS/HTML
- Hiện tại có thể kích hoạt thử nghiệm bằng flag trên Chrome Canary (từ bản 138.0.7175.0), và mọi phản hồi, báo lỗi đều được chào đón
Tổng quan và tầm quan trọng
- Đề xuất API mới cho phép vẽ trực tiếp phần tử HTML vào
<canvas> - Trước đây, khi render layout phức tạp, văn bản và nội dung dựa trên HTML lên
<canvas>, chưa có cách làm thuận tiện, nên có giới hạn về accessibility, quốc tế hóa, hiệu năng và chất lượng - HTML-in-Canvas là lớp bổ sung để hỗ trợ render HTML trong Canvas 2D và WebGL, nâng cao tính ứng dụng trong biểu đồ, hộp rich text, UI game và nhiều khu vực khác
Trường hợp sử dụng
- Việc biểu diễn nội dung văn bản và layout đã được style bên trong canvas
- Ví dụ: chú giải và trục của biểu đồ, hộp rich editor, menu trong game
- Tăng cường accessibility
- Giải quyết sự không khớp giữa nội dung thay thế của canvas truyền thống và nội dung thực sự được render
- API mới cho phép đồng bộ hóa thông tin accessibility
- Kết hợp HTML với shader của WebGL
- Hỗ trợ nhu cầu kết hợp HTML với shader WebGL thông thường, vượt ra ngoài hiệu ứng CSS filter
- Render HTML trong ngữ cảnh 3D
- Có thể chèn nội dung 2D rich vào khu vực 3D của game/website
API đề xuất và tính năng chính
- Kích hoạt layout của các phần tử HTML con trên canvas bằng thuộc tính
<canvas layoutsubtree>(mặc định chỉ render trực quan, không hiển thị cho các thuật toán điều hướng trang của UA) - CanvasRenderingContext2D.drawElement(element, x, y, options)
- Render phần tử HTML con của canvas tại vị trí đã chỉ định
options.allowReadbackkiểm soát chống rò rỉ dữ liệu (chính sách tainting sẽ áp dụng về sau)- Có thể resize theo
dwidth/dheight
- WebGLRenderingContext.texElement2D(...)
- Vẽ trực tiếp phần tử HTML đã chỉ định thành texture WebGL để dùng trong cảnh 3D
- setHitTestRegions
- Kết nối phần tử đã render ở một vùng với sự kiện hit test của canvas để tự động chuyển hướng sự kiện chuột/cảm ứng
- Tùy chọn fireOnEveryPaint (ResizeObserver)
- Tự động phát hiện thời điểm vẽ lại canvas khi nội dung HTML thay đổi/bố trí lại
Hoạt động và giới hạn
- Khi gọi drawElement, sẽ áp dụng ma trận transform của canvas (CTM) và chỉ clip ảnh trong border box của phần tử
- Hình ảnh đã vẽ lên canvas là tĩnh (nếu phần tử thay đổi, cần gọi drawElement lại)
- Không hỗ trợ với canvas không có trong DOM hoặc offscreen canvas (giới hạn kỹ thuật)
- Các phần tử có khả năng tương tác (nút, form, v.v.) có thể được render, nhưng không tự động tương tác
- iframe cross-origin, SVG foreignObject không được hỗ trợ
- Các vấn đề về accessibility, bảo mật và quyền riêng tư (PII) vẫn đang được bàn thảo tiếp tục
Ví dụ demo
- Ví dụ complex-text: vẽ trực tiếp bằng drawElement các chữ có style HTML, hộp và layout phức tạp trên canvas
- Ví dụ webGL: dùng texElement2D tạo texture HTML và map lên cube 3D
- Ví dụ text-input: dùng setHitTestRegions, fireOnEveryPaint để hiển thị vùng tương tác cho form nhập liệu
Thử nghiệm dành cho nhà phát triển và lưu ý
- Kích hoạt trên Chrome Canary bằng flag
--enable-blink-features=CanvasDrawElement - Do nội dung canvas không bị tainted, cần chú ý tránh rò rỉ dữ liệu cá nhân
- API và hành vi vẫn tiếp tục thay đổi, và hiện vẫn thiếu các trường hợp thử nghiệm HTML quy mô lớn
- Khuyến khích gửi feedback: nên báo cáo qua GitHub Issue về tương thích, các trường hợp render thất bại, vấn đề accessibility
Giá trị ứng dụng và triển vọng
- Trong biểu đồ, trực quan hóa dữ liệu, UI trong canvas, HUD/menu game trong 3D, v.v., giúp nâng cao khả năng biểu đạt và năng suất của web
- Những chuyển đổi HTML→Canvas trước đây khó (style, layout, hỗ trợ đa ngôn ngữ, accessibility) giờ có thể xử lý trực tiếp bằng API chuẩn
- Được kỳ vọng là công nghệ mạnh mẽ cho lập trình viên web đồ họa, game và app
1 bình luận
Ý kiến trên Hacker News
Có rất nhiều lo ngại về khả năng truy cập và lạm dụng, nhưng cần nhìn một góc khác của cuộc thảo luận này Mình vừa thấy một thread thú vị trên Twitter, cho rằng việc không thể dùng font rendering và metrics trong JS khiến 90% các điều hay trên web không thể xảy ra Web vốn được thiết kế như nền tảng biểu diễn văn bản, nhưng lại không có nhiều khả năng thao tác chi tiết với text; có nhiều phản hồi nói về việc đáng tiếc phải tự chèn giải pháp đo text trong tldraw (thread trên Twitter: https://x.com/_chenglou/status/1951481453046538493) Web là nền tảng phát triển ứng dụng, nên việc khiến nó dễ dùng và mạnh mẽ sẽ tốt cho mọi người Theo mình web API nên đi sâu hơn nữa; nếu có API font/text metrics cho canvas thì sẽ tuyệt vời Nhưng đồng thời, layout text của web engine cực kỳ mạnh về hiệu năng, nên nếu chức năng này có thể dùng được trong canvas thì sẽ mở ra nhiều khả năng mới Ví dụ mà mình cứ nhắc đi nhắc lại mấy năm nay là rich-text editing có phân trang; contenteditable không thể triển khai ở mức product-grade nên Google Docs đã tự làm engine layout của riêng họ Nếu đề xuất này được áp dụng, có thể tận dụng cả sức mạnh của contenteditable lẫn khả năng kiểm soát layout trang/in Mình mong chức năng này được đưa vào trình duyệt
Khẳng định rằng “đa số điều hay trên web không thể xảy ra vì không tiếp cận được font rendering/metrics” mình thấy còn phải bàn thêm Mình muốn xem cụ thể những ví dụ đổi mới nào là “điển hình” Hiện tại JS cũng đã cung cấp khá nhiều thông tin về font và metrics; hầu hết nhu cầu cần thiết có thể vòng qua mà không gây trở ngại nghiêm trọng cho hiệu năng Hơn nữa, những gì HTML-in-Canvas cung cấp hiện tại cũng chưa thực sự mang tính thay đổi căn bản ở điểm đó
Mình cũng dùng Harfbuzz và engine layout riêng của Nutrient trên WASM, tương tự Google Docs Demo: https://document-authoring-demo.nutrient.io/ Nếu API này được nền tảng cung cấp chính thức thì sẽ dễ phát triển hơn nhiều Nhờ WASM nên đây không phải ngõ cụt Mình nghe tin đang có công việc đồng bộ trong ElectricSQL, nên gửi lời hỏi thăm Oleksii
Với câu “mong chức năng này vào trình duyệt”, mình không hiểu vì sao lại muốn mở rộng thêm engine layout/UI kém hiệu quả nhất thế giới sang canvas Như vậy chỉ làm đóng băng thêm vấn đề hiện tại là thiếu quyền truy cập API tốt Figma hoạt động theo hướng tạo một trình duyệt khác bên trong trình duyệt để vượt qua giới hạn DOM (xem: https://www.figma.com/blog/building-a-professional-design-tool-on-the-web/) Nếu contenteditable không đủ để làm product-level nên Google Docs tự làm engine riêng, thì đề xuất mới khó giúp ích thiết thực cho layout rich-text
Mình thấy tính năng này rất tiềm năng, nhưng cảm giác bản thân cấu trúc “đặt HTML vào trong canvas trong HTML” vẫn hơi chướng mắt và không khớp lắm Theo mình, canvas nên trở thành một định dạng bậc một độc lập trong trình duyệt để được xử lý tự nhiên hơn Khi đó, việc có một trang trung tâm HTML nhúng canvas, hoặc ngược lại canvas trung tâm có phần tử HTML bên trong, đều sẽ tự nhiên hơn Đây chỉ là suy nghĩ của riêng mình
Mình thấy ý của cậu rất hợp lý Chỉ lưu ý là HTML đã có thể nhúng vào SVG rồi, rồi lại nhúng vào HTML rồi Nếu canvas là phần tử cấp cao nhất của trang, vậy tiêu đề trang sẽ lưu ở đâu? Cuối cùng vẫn phải dùng tag , ví dụ như
Nếu canvas cho phép nội dung đi vào thẻ canvas, nó sẽ được giải quyết rất gọn
Website đặt nặng canvas rất bất tiện Không dùng hết hệ thống dịch vụ của nền tảng (autocomplete, accessibility, v.v.) và còn có vấn đề quyền riêng tư Ví dụ, dùng rất khó chức năng sửa lỗi chính tả của hệ thống, và không tận dụng được accessibility của hệ thống nên mỗi app phải tự triển khai accessibility UI khác nhau
Nếu làm theo cấu trúc mà cậu nói, thì đúng kiểu phát minh lại Flash Nhưng đưa Flash vào web trước đây cũng cực kỳ cực khổ
Có lúc cần để một canvas trong HTML page rồi thêm layout/style cho text trong đó Dù vậy không cần thiết phải làm thay đổi toàn bộ mô hình layout của cả trang
Trước đây đã có người phản đối truy cập DOM từ WASM Nên mình thấy hướng phát triển này là điều gần như tất yếu
Giờ chúng ta đã đi đến chỗ có thể biên dịch cả browser thành WASM, rồi chạy browser đó trong thẻ canvas của browser chính
Thậm chí còn chạy cả OS cùng với IE trên web được (ví dụ: https://copy.sh/v86/?profile=windows2000)
Ý tưởng khá giống premise trong bài The Birth & Death of JavaScript Tham khảo: https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript
Cũng có thử nghiệm kiểu này: https://www.chromium.org/blink/blink-in-js/
Vì “sandboxing và bảo mật”, tương lai có thể đến khi cấu trúc này trở thành bắt buộc
Thêm nữa, có thể xem môi trường tương tự trực tiếp tại https://trevorlinton.github.io
Trong https://github.com/WICG/html-in-canvas/blob/main/security-privacy-questionnaire.md có TODO rằng cần bổ sung thêm rủi ro fingerprinting
Mình nghĩ SVG
foreignObjectđã có thể đem lại kết quả tương đương HTML-in-Canvas rồi Chẳng hạn https://github.com/zumerlab/snapdom, có dự án copy DOM với inline style rồi đặt vàoforeignObjectcủa SVG và render sang canvasforeignObjectlên canvas dễ hơn Ngoài ra còn hỗ trợ những tính năng mới như update tự động canvas khi nội dung đổi, interactivity, v.v.Mình chưa chắc mình có đúng, nhưng có cảm giác là render HTML trên canvas chỉ với JS thuần vẫn đủ khả thi Canvas thường dùng cho thứ HTML không thể làm được, chứ không phải để thay thế DOM
Hiện tại, ví dụ như:
Nếu phần tử HTML luôn ở phía trước toàn bộ canvas thì ổn, nếu không phải thì sẽ phải phủ thêm canvas lên HTML và lặp lại cho từng lớp z Với thay đổi này, mình nghĩ có thể sửa được “stack layer render đảo ngược” của browser Mình cho rằng toàn bộ render của browser nên được xếp trên một Universal Canvas API
Sắp tới, có lẽ ngay cả trong HTML-in-Canvas ta sẽ cần render lại canvas nữa
Nhìn vào là nhớ meme Pimp My Ride luôn “Anh thích HTML à, nên tao cho HTML vào trong canvas, rồi đưa thêm một canvas vào trong HTML”
Có thể cứ thêm cờ trên body như sau
Như vậy cả trang sẽ là cùng một surface vẽ như canvas, vẫn có thể render DOM như bình thường Nội bộ mở quy trình rasterize cả DOM có vẻ còn tốt hơn Ở cấp kiến trúc, DOM rendering và canvas rendering sẽ thành hai layer hoàn toàn ngang hàng Ví dụ, hành động vẽ line trên page có thể kiểm soát được là có kích hoạt hay bỏ qua reflow của phần tử DOM hay không
Tưởng tượng cảnh tất cả screen reader cùng hét lên cùng lúc
Cách này còn giúp canvas tự co giãn kích thước; nếu đúng như vậy thì mở ra thời kỳ mới cho composition HTML Có thể vượt qua iframe
Mình thấy nó hơi lệch nhưng vẫn thiên về đồng ý với đề xuất này Có những lúc cần vẽ thứ gì đó mà vẫn muốn tái sử dụng lại element HTML sẵn có Trước đây người ta render offscreen ra bitmap rồi copy vào canvas, hoặc cố chỉnh bằng z-index và position absolute, nhưng canvas gần như vẫn luôn che chụp tất cả trực quan (dù gần đây đã khá khá cải thiện) Cách này có thể chưa phải giải pháp hoàn hảo, nhưng tớ nghĩ vẫn tốt hơn hẳn mấy mẹo tạm thời trước đây Giống html2canvas, khi có trường hợp phải như vậy thì đây là một giải pháp khả dụng