Google анонсировала HTML-in-Canvas на Google I/O 2026 — новый API веб-платформы, позволяющий разработчикам рендерить реальные живые DOM-элементы прямо на HTML Canvas. Полное руководство с примерами кода и стратегиями внедрения.
Более десятилетия веб-разработчики сталкивались с неудобным выбором: строить в DOM
(богатая интерактивность, доступность, стилизация) или на Canvas (полный контроль
пикселей, высокая производительность, 3D-графика). Нельзя было получить и то, и другое — по крайней
мере, элегантно. Решения вроде html2canvas и rasterizeHTML делали
приблизительные скриншоты, но были медленными, неточными и не справлялись с динамическим контентом.
На Google I/O 2026 (19 мая) Google анонсировала HTML-in-Canvas — новый API веб-платформы, который наконец устраняет этот разрыв. Он позволяет рендерить реальные, живые DOM-элементы прямо на HTML Canvas, поддерживая 2D-, WebGL- и WebGPU-пайплайны. О других 15 обновлениях Chrome, анонсированных на I/O, читайте в нашем обзоре обновлений Chrome на Google I/O 2026.
API доступен как Origin Trial в Chrome 148-150 (стабильный канал, май 2026).
Разработчики могут присоединиться через токен Origin Trial или включить флаг
chrome://flags/#canvas-draw-element. Это руководство охватывает все примитивы API
с рабочими примерами кода, таймлайном поддержки браузеров, стратегиями оптимизации
производительности и реальными сценариями использования.
До HTML-in-Canvas, если вы хотели использовать богатый интерактивный HTML — стилизованные формы, сложные макеты, живые визуализации данных — внутри Canvas-приложения (игра, дизайн-инструмент, видео-редактор), у вас было три плохих варианта:
html2canvas или
SVG foreignObject. Медленно, даёт статичные изображения, не обновляется при изменении
DOM без полного перерендера.HTML-in-Canvas API решает всё это, предоставляя прямой канал от браузерного рендерера DOM в ваш canvas-контекст. Это не скриншот — это сам браузерный движок рендеринга, рисующий на вашем canvas на уровне пикселей.
API состоит из нескольких примитивов, работающих вместе. Все методы находятся на интерфейсах
HTMLCanvasElement, CanvasRenderingContext2D,
WebGLRenderingContext и GPUCanvasContext.
Перед тем как отрендерить DOM-элемент на canvas, браузер должен вычислить его макет.
layoutSubtree() выполняет синхронный layout поддерева элемента, гарантируя,
что все размеры, позиции и вычисленные стили готовы. Это обязательный первый шаг
— вызов drawElementImage() или texElementImage2D() без предварительного
layoutSubtree() даст пустой результат.
Рисует DOM-элемент на 2D canvas-контексте. Сигнатура повторяет drawImage() —
можно задать координаты назначения и опционально исходную область кадрирования. Элемент
рендерится в своём естественном DOM-размере.
Загружает DOM-элемент напрямую как WebGL- или WebGPU-текстуру. Это самый эффективный путь для 3D-рендеринга — данные идут напрямую от браузерного композитора в память GPU, минуя CPU.
Копирует отрендеренный DOM-элемент в существующий WebGL-текстурный объект. Полезно для фреймворков вроде Three.js, где вы управляете текстурными объектами напрямую.
Возвращает статичный ImageBitmap или HTMLImageElement отрендеренного
элемента. Полезно для одноразовых захватов, где не нужны живые обновления.
Возвращает DOMMatrix, представляющую текущее вычисленное преобразование элемента
относительно системы координат canvas. Критически важно для сопоставления координат Canvas
и DOM, а также для применения трансформаций в 3D-сценах.
Событие paint срабатывает на canvas, когда HTML-in-Canvas рендер завершён.
Слушайте его, чтобы узнать, когда элемент отрисован и готов к использованию:
canvas.addEventListener('paint', () => {
// Элемент отрисован — можно читать, композитировать и т.д.
});
Объект ElementImage представляет кешированный, готовый к рендеру снимок
DOM-элемента. Создаётся через canvas.createElementImage(element) для
эффективного повторного рендеринга без перевычисления layout на каждом кадре.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const form = document.getElementById('loginForm');
// Шаг 1: Вычисляем layout
canvas.layoutSubtree(form);
// Шаг 2: Рисуем элемент на canvas
drawElementImage(form, 50, 50);
// Ожидаем завершения отрисовки
canvas.addEventListener('paint', () => {
console.log('Форма отрисована на canvas!');
}, { once: true });
const gl = canvas.getContext('webgl');
const uiElement = document.getElementById('hud-panel');
// Вычисляем layout
canvas.layoutSubtree(uiElement);
// Загружаем напрямую как текстурный юнит 0
texElementImage2D(uiElement, 0);
// Теперь доступна как TEXTURE0 в шейдере
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, myTexture);
import * as THREE from 'three';
// Создаём внеэкранный HTML-контент
const div = document.createElement('div');
div.innerHTML = '<h2>Привет, 3D!</h2><p>Отрендерено через HTML-in-Canvas</p>';
div.style.cssText = 'width:300px;height:200px;background:#1a1a2e;color:white;padding:20px;';
document.body.appendChild(div);
// Вычисляем layout и захватываем
canvas.layoutSubtree(div);
const imageBitmap = captureElementImage(div);
// Создаём Three.js текстуру
const texture = new THREE.CanvasTexture(imageBitmap);
const material = new THREE.MeshStandardMaterial({ map: texture });
const mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), material);
scene.add(mesh);
const transform = canvas.getElementTransform(myElement);
// Возвращает DOMMatrix
// Применяем к Three.js объекту
const matrix = new THREE.Matrix4();
matrix.fromArray(transform.toFloat64Array());
threeObject.applyMatrix4(matrix);
// Или используем для hit-testing координат Canvas обратно в DOM:
canvas.addEventListener('click', (e) => {
const inverse = transform.inverse();
const domPoint = inverse.transformPoint(
new DOMPoint(e.offsetX, e.offsetY)
);
});
Анонсированные вместе с HTML-in-Canvas на Google I/O 2026, element-scoped view transitions позволяют делать плавные анимированные переходы между состояниями DOM, когда эти элементы рендерятся на Canvas. Без этого изменения состояния DOM-элемента приводили бы к визуальному разрыву на Canvas.
const transition = document.startViewTransition({
update: () => {
// Меняем DOM-контент
updateElementContent();
// Перерендериваем для canvas
canvas.layoutSubtree(myElement);
drawElementImage(myElement, 0, 0);
}
});
await transition.finished;
console.log('Плавный Canvas-переход завершён!');
chrome://flags/#canvas-draw-element. WICG-предложение, 3.3k+ звёзд.layoutSubtree, drawElementImage, texElementImage2D,
captureElementImage, getElementTransform.Важно: Ни один другой браузерный движок (Firefox, Safari) не обязался реализовать HTML-in-Canvas. Это Chrome-эксклюзив на обозримое будущее. О других AI-функциях браузера читайте в руководстве по Chrome Prompt API.
layoutSubtree() — самая дорогая операция. Вызывайте её только при реальном
изменении DOM. Для анимаций на 60fps, где DOM статичен, вызовите один раз и используйте
кешированный ElementImage для последующих кадров.
Не читайте пиксели canvas и не композитируйте сразу после drawElementImage().
Дождитесь события paint — оно сигнализирует о завершении рендеринга элемента
на canvas.
const dpr = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth * dpr;
canvas.height = canvas.clientHeight * dpr;
ctx.scale(dpr, dpr);
const supportsHtmlInCanvas =
'layoutSubtree' in HTMLCanvasElement.prototype &&
'drawElementImage' in CanvasRenderingContext2D.prototype;
if (supportsHtmlInCanvas) {
// Используем API
} else {
// Падаем на традиционный Canvas или наложенный DOM
}
const elementImage = canvas.createElementImage(hudElement);
canvas.layoutSubtree(hudElement);
function renderFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
elementImage.draw(ctx, 10, 10); // Быстрый путь — без DOM layout
requestAnimationFrame(renderFrame);
}
Игровые движки, встроенные в браузер (PlayCanvas, Unity WebGL, кастомные), нуждаются в богатых меню, инвентарях, чатах и HUD. Вместо перерисовки каждого виджета на Canvas разработчики могут строить UI на стандартном HTML/CSS и рендерить его в игровой мир. Интеграция с Three.js делает это особенно простым для WebGL-движков.
Инструменты визуализации данных часто смешивают Canvas-рендер графиков с DOM-тултипами, легендами и контролами. HTML-in-Canvas позволяет рендерить подсказки и аннотации как настоящие HTML-элементы прямо на Canvas, сохраняя их стилизацию и макет.
Приложения вроде Figma, Photopea и Excalidraw рендерят всё на Canvas для производительности, но нуждаются в богатом текстовом редактировании, стилизованных инпутах и сложных контролах форм. HTML-in-Canva позволяет им встраивать реальные DOM-элементы (текстовые редакторы, палитры цветов, выпадающие списки) без хаков с оверлейным позиционированием.
Canvas-видео-редакторы могут композитировать DOM-элементы (титры, нижние трети, бегущие строки) прямо в свой рендер-пайплайн, используя WebGL-текстуры для GPU-ускорения.
texElementImage2D() для загрузки напрямую как WebGL-текстура.
Работает во всех WebGL-совместимых браузерах. API синхронный.copyElementImageToTexture() с GPUTexture.layoutSubtree() + drawElementImage().getElementTransform().Если вы строите Canvas-приложение и нуждаетесь в богатых HTML-интерфейсах внутри него — да, но с оговорками. Origin Trial даёт возможность экспериментировать и предоставлять обратную связь. Для продакшн-приложений с широкой аудиторией дождитесь стабильного релиза. Всегда реализуйте graceful degradation.
Сочетание HTML-in-Canvas, element-scoped view transitions и других обновлений веб-платформы (см. наше руководство по WebMCP) делает это время захватывающим для веб-графики. Разрыв между «тем, что можно построить на DOM» и «тем, что можно на Canvas» наконец сокращается.
Я full-stack веб-разработчик, специализирующийся на Canvas, WebGL и высокопроизводительных веб-приложениях. Если вы планируете внедрять HTML-in-Canvas, давайте обсудим ваш проект.