Обновления HTML-in-Canvas: работа над улучшением веб-API.

В Chrome 150 и 155 были внесены несколько изменений в API HTML-in-Canvas. Эти изменения были внесены в ответ на отзывы сообщества и стандартов с целью улучшения эргономики API. В этой статье описываются изменения и способы обеспечения их актуальности для вашей реализации.

Обновления включают переименование атрибутов и методов, требование явного предварительного выделения памяти для текстур и обновление моделей синхронизации DOM. Кроме того, расширен пробный период HTML-in-Canvas Origin Trial . Ниже приведено объяснение изменений, причин и способов обновления вашего кода.

Изменения в Chrome 150

В Chrome 150 мы изменили сигнатуры аргументов для WebGL и WebGPU, чтобы устранить избыточность и привести код в соответствие с современными графическими стандартами.

Вебгл

Мы удалили избыточные аргументы ( level , srcFormat , destType ) из texElementImage2D (подробнее см. обсуждение WebGL ):

// Old implementation (Chrome < 150)
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const destType = gl.UNSIGNED_BYTE;

gl.texElementImage2D(
  gl.TEXTURE_2D,
  level,
  internalFormat,
  srcFormat,
  destType,
  element
);

// New implementation (from Chrome 150)
const internalFormat = gl.RGBA8;
gl.texElementImage2D(gl.TEXTURE_2D, internalFormat, element);

WebGPU

Мы стандартизировали параметры copyElementImageToTexture объединив их в объекты sourceDict и destDict (см. GPUWeb PR#6250 ):

// Old implementation (Chrome < 150)
GPUQueue.copyElementImageToTexture(
  element,
  width,
  height,
  { texture: texture }
);

// New implementation (from Chrome 150)
const sourceDict = { source: element };
const destDict = {
  destination: { texture: texture },
  width: width,
  height: height
};

GPUQueue.copyElementImageToTexture(sourceDict, destDict);

Изменения в Chrome 155

В Chrome 155 внесены дальнейшие улучшения, основанные на отзывах разработчиков. Мы обновляем названия методов, требуем явного предварительного выделения памяти для текстур, поддерживаем сложные поддеревья DOM и разделяем синхронизацию DOM.

WebGPU

Метод WebGPU был переименован из copyElementImageToTexture в drawElementImageToTexture . Вместе с переименованием были расширены координаты параметров ( sourceX , sourceWidth ), а границы назначения сгруппированы в объект стандартного size ( GPUExtent3D ):

// BEFORE (Chrome 150)
const sourceDict = {
  source: myElement,
  sx: 0,
  sy: 0,
  swidth: 100,
  sheight: 100
};
const destDict = {
  destination: { texture: myTexture },
  width: 300,
  height: 200
};

queue.copyElementImageToTexture(sourceDict, destDict);

// AFTER (Chrome 155+)
const sourceDict = {
  source: myElement,
  sourceX: 0,
  sourceY: 0,
  sourceWidth: 100,
  sourceHeight: 100
};
const destDict = {
  texture: myTexture,
  size: { width: 300, height: 200 }
};

queue.drawElementImageToTexture(sourceDict, destDict);

Вебгл

Изменения в WebGL для HTML-in-Canvas затрагивают три аспекта:

  • Название метода меняется с texElementImage2D на texElementSubImage2D .
  • Изменяется сигнатура метода; наиболее существенным изменением является добавление аргументов xoffset и yoffset , позволяющих опционально указывать смещение в целевой текстуре, в котором будет отображаться элемент.
  • Метод больше не выделяет буферное хранилище для целевой текстуры автоматически; хранилище необходимо явно выделить до использования API. Это аналогично поведению texSubImage2D .
// 1. Capture natural layout size of the element
const elementImage = canvas.captureElementImage(element);

// 2. Pre-allocate texture backing once (or when element resizes)
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
  gl.TEXTURE_2D,
  0,
  gl.RGBA8,
  Math.ceil(elementImage.width),
  Math.ceil(elementImage.height),
  0,
  gl.RGBA,
  gl.UNSIGNED_BYTE,
  null // null reserves VRAM without uploading pixels
);

// 3. Upload DOM into pre-allocated VRAM
gl.texElementSubImage2D(
  gl.TEXTURE_2D,
  0,                  // level
  0,                  // xoffset
  0,                  // yoffset
  element             // pass DOM element
);

Атрибуты холста и дочерние элементы, доступные для рисования.

Мы обновляем способ подготовки холста для отображения HTML.

Переименование layoutsubtree в content="drawable"

В соответствии с соглашениями об именовании атрибутов HTML, логический атрибут layoutsubtree элемента <canvas> был переименован в content="drawable" (подробное обсуждение см. в выпуске WICG #169 ).

Атрибут drawable для потомков

Ранее отображались только непосредственные дочерние элементы canvas. Для поддержки сложных иерархий DOM без сглаживания разметки теперь можно явно добавить атрибут drawable к любому дочернему элементу, который вы собираетесь отображать независимо:

<!-- BEFORE -->
<canvas layoutsubtree>
  <div id="badge">
    <h2>Player Stats</h2>
    <button id="actionBtn">Equip</button>
  </div>
</canvas>

<!-- AFTER (Chrome 155+) -->
<canvas content="drawable">
  <!-- Subtree A (Body and heading) -->
  <div id="badge" drawable>
    <h2>Player Stats</h2>
    <!-- Subtree B (Excluded from Subtree A; drawn and animated independently) -->
    <button id="actionBtn" drawable>Equip</button>
  </div>
</canvas>

Элемент с drawable захватывает все свое поддерево, за исключением вложенных потомков, которые также указывают drawable . Это позволяет анимировать и отрисовывать родительские контейнеры и дочерние элементы, такие как кнопки, в отдельных вызовах функции drawable.

Синхронизация проверки попаданий

Ранее разработчикам приходилось перехватывать возвращаемое значение методов отрисовки и передавать его в element.style.transform . Теперь метод drawElementImage() возвращает void, а синхронизация зависит от контекста.

2D-контексты: автоматическая синхронизация

В режиме 2D canvas браузер автоматически синхронизирует границы проверки попадания DOM и кольца фокусировки экранного чтения с координатами, отображаемыми на экране. Вы можете удалить все конвейеры преобразования, используемые вручную:

// BEFORE
const transform = ctx.drawElementImage(element, x, y);
element.style.transform = transform.toString();

// AFTER (Chrome 155+)
ctx.drawElementImage(element, x, y);
// No style update needed: position & hit-testing sync automatically

Для ручного управления геометрией в 2D передайте { preserveElementGeometry: true } в drawElementImage() и примените преобразования с помощью canvas.updateElementGeometry() .

3D-контексты: Явное updateElementGeometry()

Для синхронизации DOM в контекстах WebGL или WebGPU создайте преобразование DOMMatrix и передайте его в canvas.updateElementGeometry() :

canvas.addEventListener('paint', () => {
  // 1. Upload element snapshot to GPU texture
  device.queue.drawElementImageToTexture({ source: element }, { texture });

  // 2. Render 3D scene...
  drawScene();

  // 3. Construct transform matrix as needed
  const canvasTransform = new DOMMatrix().translate(x, y);

  // 4. Update DOM hit-testing and accessibility bounding boxes
  canvas.updateElementGeometry(element, { canvasTransform });
});

Совместимость между версиями

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

Убедитесь, что вы задали правильные атрибуты элемента <canvas> для каждой версии браузера:

// ============================================================================
//  Cross-version canvas markup
// ============================================================================
if (!canvas.hasAttribute('content') && !canvas.hasAttribute('layoutsubtree')) {
  if ('content' in HTMLCanvasElement.prototype) {
    // Chrome 155+ (New standardized attribute)
    canvas.setAttribute('content', 'drawable');
  } else {
    // Chrome < 155 (Legacy boolean attribute)
    canvas.setAttribute('layoutsubtree', '');
  }
}

Для 2D-контекста проверьте возвращаемое значение функции drawElementImage() и атрибуты прототипа холста, чтобы убедиться в поддержке обеих версий:

// ============================================================================
// Cross-version 2D drawing & synchronization
// ============================================================================
function draw2DElement(ctx, element, x, y) {
  // Execute the 2D draw call
  const result = ctx.drawElementImage(element, x, y);

  if (result !== undefined && typeof result?.toString === 'function') {
    // Chrome < 155: drawElementImage returned a DOMMatrix
    // Requires manual transform piping to align DOM hit-test bounds:
    element.style.transform = result.toString();
  } else {
    // Chrome 155+: drawElementImage returns void / undefined
    // The browser automatically aligns DOM positioning, clicks, and a11y bounds!
    // If you previously had element.style.transform logic here, clear it:
    if (element.style.transform) {
      element.style.transform = "";
    }
  }
}

Для WebGPU используйте совместимую сигнатуру и имя метода:

// ============================================================================
// WebGPU cross-version support
// ============================================================================
if ('drawElementImageToTexture' in GPUQueue.prototype) {
  // Chrome 155+ (Latest implementation)
  queue.drawElementImageToTexture(
    { source: element, sourceX: 0, sourceY: 0, sourceWidth: w, sourceHeight: h },
    { texture: myTexture, size: { width: w, height: h } }
  );
} else if (typeof queue.copyElementImageToTexture === "function") {
  // Chrome 150 - 154: Try the Chrome 150+ dictionary signature first,
  // then fall back to the Chrome < 150 signature
  try {
    queue.copyElementImageToTexture(
      { source: element },
      { destination: { texture: myTexture }, width: w, height: h }
    );
  } catch (err) {
    // Chrome < 150
    queue.copyElementImageToTexture(element, { texture: myTexture });
  }
}

Обязательно проверьте совместимость WebGL с браузерами:

// ============================================================================
// WebGL cross-version support
// ============================================================================
if ('texElementSubImage2D' in WebGL2RenderingContext.prototype) {
  // Chrome 155+ (Explicit pre-allocation + sub-image)
  gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, element);
} else if (gl.texElementImage2D && gl.texElementImage2D.length === 3) {
  // Chrome 150-154 (3-argument signature)
  gl.texElementImage2D(gl.TEXTURE_2D, gl.RGBA8, element);
} else if (gl.texElementImage2D) {
  // Chrome < 150 (6-argument signature)
  gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, element);
}

Продление пробного периода Origin

Срок действия пробной версии HTML-in-Canvas в Origin продлен до Chrome 160. Обязательно обновите свой токен пробной версии Origin, чтобы продолжать предоставлять пользователям функцию HTML-in-Canvas. Подробнее см. в статье о пробной версии Origin .

Поделитесь своим мнением.

Сообщите нам, как эти обновления работают в ваших приложениях , создав заявку в репозитории WICG на GitHub . Подпишитесь на рассылку для разработчиков , чтобы быть в курсе всех новостей HTML-in-Canvas.