Chrome 150 및 155에서는 Canvas의 HTML API가 여러 가지로 변경되었습니다. 이러한 변경사항은 API의 인체공학을 개선하기 위해 커뮤니티 및 표준 의견에 따라 이루어졌습니다. 이 블로그 게시물에서는 변경사항과 구현이 최신 상태인지 확인하는 방법을 설명합니다.
업데이트에는 속성 및 메서드 이름 바꾸기, 명시적 텍스처 메모리 사전 할당 요구, DOM 동기화 모델 업데이트가 포함됩니다. 또한 HTML-in-Canvas 오리진 트라이얼이 이제 연장됩니다. 다음은 변경사항, 변경 이유, 코드베이스 업데이트 방법에 관한 설명입니다.
Chrome 150의 변경사항
Chrome 150에서는 중복을 없애고 최신 그래픽 규칙에 맞추기 위해 WebGL과 WebGPU 전반에서 인수 서명이 변경되었습니다.
WebGL
texElementImage2D에서 중복 인수 (level, srcFormat, destType)가 삭제되었습니다(자세한 내용은 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의 WebGL 변경사항에는 세 가지 측면이 있습니다.
- 메서드 이름이
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 속성 이름 지정 규칙에 따라 <canvas> 요소의 불리언 layoutsubtree 속성이 content="drawable"로 이름이 변경되었습니다 (자세한 내용은 WICG 문제 #169 참고).
하위 요소의 drawable 속성
이전에는 캔버스의 직접 하위 요소만 렌더링되었습니다. 마크업을 평면화하지 않고 복잡한 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도 지정하는 중첩된 하위 요소를 제외한 전체 하위 트리를 캡처합니다. 이를 통해 버튼과 같은 상위 컨테이너와 하위 요소를 별도의 그리기 호출에서 애니메이션 처리하고 그릴 수 있습니다.
히트 테스트 동기화
이전에는 개발자가 그리기 메서드에서 반환 값을 캡처하여 element.style.transform에 파이프해야 했습니다. 이제 drawElementImage() 메서드는 void를 반환하며 동기화는 컨텍스트에 따라 달라집니다.
2D 컨텍스트: 자동 동기화
2D 캔버스에서 브라우저는 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()
WebGL 또는 WebGPU 컨텍스트에서 DOM을 동기화하려면 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);
}
오리진 트라이얼 연장
HTML-in-Canvas 오리진 트라이얼이 Chrome 160까지 연장되었습니다. 사용자에게 HTML-in-Canvas 기능을 계속 제공하려면 오리진 트라이얼 토큰을 갱신해야 합니다. 자세한 내용은 오리진 트라이얼 도움말을 참고하세요.
의견 공유
WICG GitHub 저장소에 문제를 제출하여 애플리케이션에서 이러한 업데이트가 어떻게 작동하는지 알려주세요. 개발자 뉴스레터에 가입하여 HTML-in-Canvas 관련 최신 소식을 받아보세요.