javascript

HTML-in-Canvas в Chrome 157: интерактивный DOM в 2D, WebGL и WebGPU

  • суббота, 31 октября 2026 г. в 00:00:06
https://habr.com/ru/articles/1101228/

Эта статья перевод оригинальной статьи «HTML-in-Canvas in Chrome 157: Interactive DOM inside 2D, WebGL, and WebGPU».

Также я веду телеграм канал «Frontend по‑флотски», где рассказываю про интересные вещи из мира разработки интерфейсов и AI.

Вступление

После успешного origin trial API HTML-in-Canvas официально вышел в стабильной версии Chrome 157. Он объединяет интерактивность и доступность DOM с производительностью и попиксельным управлением <canvas>. Так возможности браузера можно использовать вместе с графикой canvas, ускоренной на GPU.

Выбор между canvas и DOM

Раньше при создании веб-приложений со сложной графикой приходилось выбирать между стандартными HTML-элементами и непосредственной работой с контекстами canvas.

Технология

Сильные стороны

Ограничения

<canvas>

Низкоуровневое управление пикселями с доступом к GPU для конвейеров 2D- и 3D-графики, собственных шейдеров и воркеров для внеэкранного рендеринга. Высокая производительность в тяжёлых интерфейсах с большим объёмом данных.

Нет встроенных возможностей веб-платформы: выделения, копирования и вставки текста, действий по правому клику и переноса строк. Непрозрачная для браузера сетка пикселей по умолчанию не поддерживает скринридеры, поиск по странице, автоматический перевод и автозаполнение. Для них нужна отдельная реализация.

HTML

Семантический интерфейс со встроенными элементами форм, адаптивной типографикой и CSS-стилями. Интеграция со скринридерами, поиском по странице и DevTools.

Ограниченные возможности управления производительностью сложных интерфейсов. Визуальные эффекты ограничены стандартными CSS и SVG, без прямого управления пикселями или шейдеров.

До HTML-in-Canvas

До появления этого API для интерфейсов, активно использующих canvas, было два подхода. У каждого были свои недостатки.

  1. Отрисовка текста непосредственно в пикселях canvas с помощью fillText(). Приложение императивно рисовало текст и элементы управления как наборы пикселей. Это создавало несколько проблем:

    • Скринридеры и вспомогательные инструменты не могут прочитать пиксели canvas. Для доступности приходилось вручную создавать и синхронизировать невидимый резервный DOM.

    • Пользователь не мог искать текст через поиск по странице (Ctrl/Cmd+F), привычным образом копировать и вставлять его, вызывать контекстные меню, пользоваться переводом и проверкой правописания в браузере.

    • Для интернационализации приходилось включать в сборку большие JavaScript-движки компоновки текста.

  2. HTML-элементы поверх canvas. Например, библиотеки размещали стандартные HTML-элементы над холстом, вычисляя ограничивающие прямоугольники и матричные преобразования в JavaScript. Здесь возникали другие проблемы:

    • Задержки синхронизации и визуальные разрывы. При прокрутке, масштабировании или движении камеры в 3D DOM-элементы могли смещаться или дрожать относительно пикселей canvas из-за разницы во времени подготовки кадров.

    • Ограничения обрезки и порядка наложения. HTML-слои всегда располагались поверх всего буфера canvas. Поместить HTML внутрь 3D-сцены, например за трёхмерный объект, внутрь стеклянного материала или в отражение зеркала, без сложных обходных решений с преобразованием iframe в изображение было нельзя.

    • Дополнительная нагрузка. Чтобы HTML-слои оставались на своих местах относительно canvas, на каждом кадре нужно пересчитывать преобразование каждого слоя в JavaScript и записывать его в style.transform. Перед отрисовкой браузер разбирает каждую строку и пересчитывает стиль элемента, поэтому каждый новый слой увеличивает работу основного потока. В 2D-контексте drawElementImage() обновляет положение элемента автоматически. В WebGL и WebGPU вычисленное преобразование передаётся в updateElementGeometry() как матрица. В обоих случаях браузеру не приходится разбирать CSS и пересчитывать стили.

Что даёт HTML-in-Canvas

HTML-in-Canvas устраняет необходимость в этих компромиссах. HTML внутри элемента <canvas> остаётся интерактивным, а интеграции с браузером работают автоматически.

HTML-содержимое внутри <canvas> сохраняет те же возможности, что и снаружи:

  • Компоновка и форматирование текста, включая многострочный и двунаправленный текст с CSS-стилями.

  • Элементы форм с широкими возможностями оформления через CSS и шейдеры.

  • Выделение, копирование и вставка текста, а также правый клик. Пользователи могут выделять текст внутри 3D-сцен и вызывать контекстные меню.

  • Доступность: HTML-содержимое canvas попадает в дерево доступности для скринридеров.

  • Функции браузера, включая перевод, автозаполнение и поиск по странице, работают без дополнительной настройки. Можно переводить и искать текст внутри текстур WebGL и WebGPU.

  • Расширения браузера тоже работают с этим содержимым. Например, расширение для замены текста автоматически обновляет текст на трёхмерных мешах.

  • В Chrome DevTools можно просматривать и менять CSS-стили элементов интерфейса внутри canvas.

  • Поисковые роботы и AI-агенты могут индексировать DOM-содержимое, отрисованное в 2D- и 3D-сценах.

  • Готовую композицию canvas с живыми DOM-элементами можно экспортировать в изображение (toDataURL(), toBlob()) или видеопоток (captureStream()).

Сравнение возможностей браузера при отрисовке текста непосредственно в canvas и через HTML-in-Canvas. В области HTML-in-Canvas текст можно выделять и переводить, а автозаполнение и другие функции браузера работают без дополнительной настройки. В области обычного canvas содержимое отрисовано методом fillText и не интерактивно.

Основные сценарии использования

API подходит для следующих задач:

  • Крупные приложения на canvas могут отрисовывать компоненты интерфейса прямо в рабочих областях холста, улучшая доступность и уменьшая размер сборки.

  • В 3D-сценах, играх и иммерсивных приложениях WebXR можно размещать интерактивные DOM-элементы прямо внутри сцены: например, внутриигровые терминалы или трёхмерные книги, которые можно читать.

  • Инструменты для создания и редактирования медиа могут сочетать HTML-типографику с шейдерными эффектами и экспортировать составные сцены непосредственно в изображения или видеопотоки.

  • Для выразительных интерфейсов можно обрабатывать HTML-содержимое как пиксели и создавать визуальные эффекты с помощью шейдеров.

Как начать работу с HTML-in-Canvas

Подробности есть в технической документации HTML-in-Canvas.

Для отрисовки HTML в canvas выполните следующие шаги.

1. Подготовьте canvas

Декларативно задайте атрибуты, которые сообщают браузеру, что HTML нужно отрисовывать в canvas. Эта настройка одинакова для контекстов 2D, WebGL и WebGPU. В документации описаны подготовка canvas и объявление HTML-элементов для независимой отрисовки.

<!-- Prepare your canvas by setting the `content` attribute -->
<canvas id="canvas" width="600" height="400" content="drawable">
  <!-- Mark child as `drawable` to render to canvas -->
  <div id="myElement" drawable>
    <!-- Your HTML here -->
  </div>
</canvas>

2. Нарисуйте изображение элемента в canvas

Конкретная реализация зависит от контекста: 2D, WebGL или WebGPU.

const ctx = canvas.getContext("2d");
// Draw HTML element into canvas in 2D
ctx.drawElementImage(myElement, myElementX, myElementY);

3. При необходимости синхронизируйте DOM и пиксели

Синхронизация геометрии тоже зависит от контекста. В 2D браузер по умолчанию выполняет её автоматически. В 3D-контекстах нужно сформировать преобразование и передать его методу updateElementGeometry.

// In 3D, construct a transform as needed, for example:
const transform = new DOMMatrix().translate(x, y);
// Synchronize the element's DOM and pixels
canvas.updateElementGeometry(myElement, { canvasTransform: transform });

Поддержка библиотек и экосистемы

HTML-in-Canvas поддерживают основные библиотеки для WebGL и трёхмерной графики.

Перечисленные инструменты являются сторонними проектами сообщества. Google и команда Chrome не разрабатывают, не сопровождают и официально не поддерживают их.

Three.js предоставляет экспериментальную поддержку через THREE.HTMLTexture.

const material = new THREE.MeshBasicMaterial();
material.map = new THREE.HTMLTexture(uiElement);
const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material);
scene.add(mesh);

PlayCanvas поддерживает потоковую передачу HTML-текстур через setSource().

canvas.addEventListener('paint', () => {
  htmlTexture.setSource(htmlElement);
}, { once: true });

Babylon.js предоставляет встроенную привязку через BABYLON.HtmlTexture.

const htmlTexture = new BABYLON.HtmlTexture("htmlTexture", uiElement, scene);
material.diffuseTexture = htmlTexture;

В PixiJS встроенная поддержка появилась в версии v8.19.0 благодаря HTMLSource.

import { HTMLSource } from 'pixi.js/html-source';
const sprite = Sprite.from(new HTMLSource({ resource: form, autoUpdate: true }));
app.stage.addChild(sprite);

Библиотека компонентов CanvasUI с открытым исходным кодом создана специально для HTML-in-Canvas.

Подробности реализации смотрите в документации соответствующей библиотеки.

Проверка поддержки и запасные варианты

Для совместимости с разными браузерами проверяйте поддержку API перед выполнением кода:

export function supportsHtmlInCanvas() {
  if (typeof document === "undefined") return false;
  const canvas = document.createElement("canvas");
  canvas.setAttribute("content", "drawable");
  return canvas.content === "drawable";
}

Если API недоступен, можно использовать альтернативный интерфейс, HTML-слои поверх canvas или одну из готовых библиотек, например three-html-render, html2canvas или dom-to-image.

Эти библиотеки являются сторонними проектами сообщества. Google и команда Chrome не разрабатывают, не сопровождают и официально не поддерживают их.

Что дальше