HTML-in-Canvas API를 사용하면 상호작용이 가능하고 액세스 가능한 DOM 하위 트리를 2D, WebGL 또는 WebGPU 캔버스 컨텍스트로 직접 렌더링할 수 있습니다.
이 가이드에서는 모든 캔버스 컨텍스트에 공통적인 HTML-in-Canvas 기술 개념을 간략하게 설명합니다.
- HTML을 렌더링할 캔버스 준비하기
drawable하위 트리 이해하기paint이벤트 이해하기- 요소의 도형 업데이트
- HTML-in-Canvas의 접근성 이해하기
- 대체 전략 사용
HTML을 렌더링할 캔버스 준비
<canvas> 내에서 HTML 요소를 렌더링하려면 다음 두 속성을 구성합니다.
<canvas>에서content="drawable"설정: 이렇게 하면 캔버스 요소가 하위 DOM 노드를 숨겨진 콘텐츠로만 취급하는 대신 배치하도록 선택합니다.- 타겟 요소에
drawable설정: 요소를 캔버스 렌더링에 적합한 독립적인 페인트 루트로 표시합니다.
<canvas id="mainCanvas" width="600" height="400" content="drawable">
<div id="parentElement" class="card" drawable>
<h2>Container Title</h2>
<p>Descriptive text inside the parent element.</p>
<!-- Nested drawable: excluded from parentElement and drawn independently -->
<button id="childElement" class="btn" drawable>Action Button</button>
</div>
</canvas>
드로어블 하위 트리 이해
캔버스에 렌더링할 HTML을 준비할 때는 어떤 요소를 독립적으로 그려야 하는지 결정해야 합니다. 각 drawable 요소는 독립적인 페인트 루트와 드로어블 하위 트리를 나타냅니다.
드로어블 하위 트리는 drawable 속성이 있는 요소와 모든 하위 요소로 구성되며, drawable도 포함하는 중첩된 하위 요소는 제외됩니다. 예를 들면 다음과 같습니다.
<canvas content="drawable">
<!-- Subtree A (Renders parent container and text) -->
<div id="parentElement" drawable>
<h2>Parent Card Heading</h2>
<p>Card body text content.</p>
<!-- Subtree B (Excluded from Subtree A and drawn separately) -->
<button id="nestedChildElement" drawable>
Clickable Button
</button>
</div>
</canvas>
| 요소 | 경계 페인트 | 동작 |
|---|---|---|
#parentElement (하위 트리 A) |
배경 패널, 제목, 단락 | drawElementImage(parentElement, ...) 호출 시 렌더링됨 |
#nestedChildElement (하위 트리 B) |
버튼 표면 및 텍스트 | #parentElement 하위 트리에서 제외되고 별도의 drawElementImage(nestedChildElement, ...) 호출에서 렌더링됨 |
drawable 버튼은 drawable 카드 내에 중첩되어 있으며 각각 자체 drawElementImage() 호출에 의해 그려집니다.드로어블 요소를 중첩하면 시맨틱 HTML 관계를 유지하면서 캔버스 장면에서 하위 요소를 독립적으로 레이어링, 배치 또는 애니메이션화할 수 있습니다.
paint 이벤트 이해하기
HTML-in-Canvas API는 paint 이벤트를 도입합니다. 브라우저는 drawable 하위 요소에서 레이아웃, 텍스트 또는 CSS 스타일이 변경될 때마다 <canvas>에서 paint 이벤트를 자동으로 전달합니다.
canvas.addEventListener("paint", (event) => {
// Clear the viewport
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw elements individually
ctx.drawElementImage(parentElement, 20, 20);
ctx.drawElementImage(childElement, 40, 90);
});
paint 이벤트는 CanvasPaintEvent의 인스턴스를 전달합니다. 이 인스턴스는 표준 DOM Event를 확장하며, 마지막 페인트 이후 시각적으로 변경된 (예: 스타일 변경, 레이아웃 변경, 텍스트 수정 또는 포커스 상태 변경) drawable 하위 요소만 포함하는 changedElements 속성을 갖습니다.
canvas.addEventListener("paint", (event) => {
// Array of changed elements
for (const el of event.changedElements) {
console.log(`Element #${el.id} visually changed and needs repainting`);
}
});
프레임 수명 주기
paint 이벤트 내에서 실행된 캔버스 그리기 명령어는 현재 렌더링 업데이트에 표시되지만 paint 이벤트 내에서 이루어진 DOM 수정은 다음 프레임까지 표시되지 않습니다.
canvas.addEventListener("paint", () => {
// Renders in the CURRENT rendering update:
ctx.drawElementImage(parentElement, 20, 20);
// Modifying DOM styles inside paint schedules a new layout cycle;
// changes will appear in the NEXT frame:
parentElement.style.backgroundColor = "#e0f2fe";
});
중첩된 캔버스
페이지에 중첩된 <canvas content="drawable"> 요소가 포함된 경우 브라우저는 DOM 트리 역순으로 paint 이벤트를 전달합니다 (하위 요소가 상위 요소보다 먼저 실행됨). 이렇게 하면 상위 캔버스 요소가 합성하기 전에 하위 캔버스 요소가 렌더링됩니다.
다음 예에서 내부 nestedElement 요소가 시각적으로 변경되어야 한다고 가정해 보겠습니다 (예: 사용자가 포인터를 가져가면 CSS 배경 색상이 변경됨). 먼저 paint 이벤트가 nestedCanvas 캔버스에서 실행됩니다. 리스너는 하위 버퍼를 지우고 ctx.drawElementImage(nestedElement, ...)를 호출하여 업데이트된 상태를 렌더링합니다. 그러면 parentCanvas에서 paint 이벤트가 실행됩니다. 리스너는 이미 업데이트된 nestedCanvas 캔버스가 포함된 wrapperElement를 장면에 그립니다.
<!-- Outer parent canvas -->
<canvas id="parentCanvas" width="800" height="600" content="drawable">
<!-- Wrapper container for nested content -->
<div id="wrapperElement" drawable>
<!-- Nested child canvas -->
<canvas id="nestedCanvas" width="300" height="200" content="drawable">
<div id="nestedElement" class="badge" drawable>
Inner Child Content
</div>
</canvas>
</div>
</canvas>
<script>
const parentCanvas = document.getElementById("parentCanvas");
const parentCtx = parentCanvas.getContext("2d");
const nestedCanvas = document.getElementById("nestedCanvas");
const nestedCtx = nestedCanvas.getContext("2d");
const nestedElement = document.getElementById("nestedElement");
const wrapperElement = document.getElementById("wrapperElement");
// ============================================================================
// 1. Child Canvas Listener (Fires FIRST)
// ============================================================================
nestedCanvas.addEventListener("paint", (event) => {
// Clear and redraw the child canvas with the updated inner element
nestedCtx.clearRect(0, 0, nestedCanvas.width, nestedCanvas.height);
nestedCtx.drawElementImage(nestedElement, 10, 10);
console.log("1. Child nested canvas rendered fresh pixels.");
});
// ============================================================================
// 2. Parent Canvas Listener (Fires SECOND)
// ============================================================================
parentCanvas.addEventListener("paint", (event) => {
// Clear and redraw the parent canvas scene
parentCtx.clearRect(0, 0, parentCanvas.width, parentCanvas.height);
// Draws the wrapper container (including the freshly painted nestedCanvas)
parentCtx.drawElementImage(wrapperElement, 50, 50);
console.log("2. Parent canvas composited the complete scene.");
});
</script>
<canvas content="drawable"> 요소가 중첩되면 페인트 이벤트가 역 트리 순서로 실행됩니다. 중첩된 캔버스가 먼저 페인트되므로 상위 캔버스가 최신 픽셀을 그릴 수 있습니다.paint 이벤트 트리거
DOM 텍스트나 스타일이 변경되지 않은 경우에도 requestPaint()을 호출하여 다음 프레임에서 단일 paint 이벤트를 예약합니다. 이는 움직이는 요소가 있는 대화형 장면을 렌더링하는 데 사용할 수 있습니다. 예를 들면 다음과 같습니다.
let animatedX = 0;
canvas.addEventListener("paint", () => {
animatedX += 1.5; // Move the element horizontally
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawElementImage(staticElement, 20, 20);
ctx.drawElementImage(movingElement, animatedX, 200);
// Request canvas paint on each rendering update for smooth animation
canvas.requestPaint();
});
canvas.requestPaint();
요소의 도형 업데이트
캔버스에 HTML 요소를 그릴 때 그리기 코드는 요소가 표시되는 위치와 크기를 결정합니다. 일반 HTML 콘텐츠와 달리 요소의 화면 위치와 크기, 즉 기하학적 구조는 계산된 스타일만으로는 결정할 수 없습니다. 브라우저에서 다음을 수행하려면 요소의 도형이 필요합니다.
- 클릭, 탭, 포인터 이동과 같은 입력 이벤트를 올바른 요소로 전송합니다. 이를 Hit Test라고 합니다.
getBoundingClientRect()와 같은 레이아웃 쿼리에서 정확한 결과를 반환합니다.- 화면 돋보기와 같은 보조 기술에 요소의 경계를 보고합니다.
2D에서 drawElementImage()는 기본적으로 도형을 자동으로 업데이트합니다. 전달하는 인수와 요소의 계산된 스타일, 컨텍스트의 현재 변환만으로 요소가 표시되는 위치를 결정할 수 있습니다.
WebGL 및 WebGPU 컨텍스트에서는 브라우저가 도형을 확인할 수 없습니다. HTML 요소가 텍스처에 복사되고 셰이더 프로그램은 이 텍스처를 임의의 방식으로 장면에 매핑할 수 있습니다. 요소가 표시되는 위치를 브라우저에 알리려면 변환을 updateElementGeometry()에 전달합니다.
updateElementGeometry() 호출을 중지하면 브라우저는 더 이상 3D 카드의 HTML 요소가 어디에 있는지 알 수 없습니다. 카드는 계속 회전하지만 클릭하면 원래 위치로 이동합니다.지오메트리를 업데이트하려면 공간 변환을 메서드에 전달합니다. 다양한 컨텍스트에 맞게 변환을 구성하는 방법을 알아보세요.
Hit Test 스택 순서 관리
기본적으로 요소의 도형이 업데이트될 때마다 브라우저는 이를 상호작용 스택의 맨 위로 가져옵니다 (마지막으로 그려진 요소 = 맨 위 클릭 타겟). preserveHitTestOrder를 사용하여 이 동작을 제어할 수 있습니다.
// Updates the backdrop without stealing clicks from foreground elements
canvas.updateElementGeometry(backgroundElement, {
canvasTransform: customTransform,
preserveHitTestOrder: true
});
| 옵션 | 동작 | 사용 사례 |
|---|---|---|
preserveHitTestOrder: false(기본) |
요소를 히트 테스트 스택의 상단으로 이동합니다. | 클릭을 먼저 수신해야 하는 포그라운드 요소입니다. |
preserveHitTestOrder: true |
요소를 현재 스택 우선순위로 유지합니다. | 포그라운드 요소 다음에 업데이트해야 하는 백그라운드 요소입니다. |
도형 재설정
DOM에서 요소를 삭제하지 않고 그리기를 취소하려면 canvas.clearElementGeometry()를 호출합니다. 이렇게 하면 요소의 캔버스 변환이 재설정되고 접근성 트리에서 화면 밖으로 표시됩니다.
// Remove from canvas hit-testing and mark as offscreen
canvas.clearElementGeometry(childElement);
크기 및 단위
HTML을 캔버스에 그릴 때 브라우저는 CSS 픽셀, 기기 픽셀, 캔버스 그리드 픽셀이라는 세 가지 좌표계에서 동시에 작동합니다.
CSS 픽셀은 레이아웃 중에 콘텐츠를 측정하고 배치하는 데 사용됩니다. 그런 다음 브라우저는 devicePixelRatio로 배율을 조정하여 페이지를 실제 기기 픽셀로 래스터화합니다. CSS 픽셀은 논리적 단위이므로 위치와 크기가 기기 픽셀의 분수로 표시되는 경우가 많습니다 (예: 10.5 CSS px × DPR 1.5 = 15.75 기기 px). 화면은 전체 픽셀만 색칠할 수 있으므로 브라우저는 그 사이에 있는 모든 것을 해결해야 합니다. 이를 위해 다음 두 가지 방법을 사용합니다.
- 스냅: 상자 경계, 테두리, 레이어 오프셋은 가장 가까운 정수 기기 픽셀로 반올림되어 선명하게 유지됩니다.
- 앤티앨리어싱: 텍스트 글리프, 곡선, 변환된 가장자리의 경우 부분 적용 범위가 인접한 픽셀과 혼합되어 여전히 부드럽고 올바른 위치에 표시됩니다.
<canvas>에는 width 및 height 속성으로 설정된 자체 픽셀 그리드가 있으며, 이는 CSS 픽셀과 기기 픽셀 모두와 별개입니다. 이를 표시하기 위해 브라우저는 캔버스 비트맵을 물리적 픽셀 그리드에 리샘플링하여 인접한 캔버스 픽셀을 혼합하여 각 기기 픽셀을 채웁니다. 브라우저가 CSS, 캔버스, 기기 픽셀 간에 매핑하는 방법을 알아보려면 devicePixelContentBox를 사용한 픽셀 단위 렌더링을 참고하세요.
모든 drawable HTML 요소는 CSS 픽셀로 배치됩니다. 캔버스에 그려진 콘텐츠는 CSS 픽셀에서 캔버스 그리드로, 그런 다음 캔버스 그리드에서 물리적 픽셀로 두 번의 변환을 거칠 수 있으므로 정확하게 정렬되지 않는 각 단계에서 또 다른 혼합이 추가되고 결과가 흐릿하게 보일 수 있습니다. 이를 방지하려면 캔버스 그리드를 실제 픽셀 그리드와 일치시키세요. 여기에는 두 가지 목적이 있습니다.
- 일관된 렌더링: 캔버스가 물리적 픽셀에 1:1로 매핑되는 경우 CSS 픽셀을 캔버스 픽셀로 변환하면 브라우저가 일반 HTML의 CSS 픽셀을 물리적 픽셀로 변환하는 것과 동일한 결과가 생성됩니다.
- 추가 블렌딩 없음: 캔버스가 실제 픽셀에 1:1로 매핑되는 경우 기기 픽셀로 변환해도 흐림 효과가 추가되지 않습니다.
다음 이미지는 콘텐츠가 흐리게 표시되는 픽셀 변환을 보여줍니다. 세 그리드가 정렬되지 않습니다. 캔버스 요소는 360×180 CSS 픽셀이고 캔버스 크기는 300×150 캔버스 그리드 픽셀입니다. 기기 픽셀 비율이 1.5인 경우 캔버스는 540x270 기기 픽셀을 커버합니다. CSS 픽셀 1개는 기기 픽셀 1.5개에 걸쳐 있고 캔버스 픽셀 1개는 CSS 픽셀 1.2개 또는 기기 픽셀 1.8개에 걸쳐 있습니다 (패널 1). 원이 그려질 때 원의 위치와 반지름은 CSS 픽셀이 아닌 캔버스 픽셀로 표시되므로 브라우저는 이를 300x150 캔버스 비트맵에 그리고 가장자리를 앤티앨리어싱합니다 (패널 2). 캔버스를 표시하기 위해 브라우저는 이 비트맵을 540x270 기기 픽셀로 늘리므로 브라우저는 보간해야 합니다. 각 기기 픽셀은 이미 앤티앨리어싱된 가장자리를 포함하는 가장 가까운 캔버스 픽셀의 가중치 혼합을 사용합니다. 가장자리가 두 번째로 혼합되고 여러 기기 픽셀에 걸쳐 분산되므로 원이 흐리게 표시됩니다 (패널 3). 캔버스 그리드가 기기 픽셀과 1:1로 일치하면 이 문제를 방지할 수 있습니다. 그러면 브라우저는 기기 그리드에서 직접 가장자리를 한 번만 앤티앨리어싱하고 리샘플링 없이 비트맵을 표시하므로 가장자리가 선명하게 유지됩니다 (패널 4).
선명한 렌더링을 위해 캔버스 그리드는 캔버스가 포함하는 기기 픽셀 수와 일치해야 합니다(위 예시에서는 540x270).
크기 조절 관찰자로 캔버스 그리드 크기 조절하기
적절한 그리드 크기는 기기에 따라 다르며, 레이아웃이 변경되거나 사용자가 페이지를 확대/축소하거나 사용자가 창을 다른 디스플레이로 이동할 때 변경될 수 있습니다. 따라서 그리드 크기를 하드코딩하는 대신 브라우저에서 크기를 알려주도록 합니다.
캔버스 그리드를 화면의 캔버스와 동기화된 상태로 유지하려면 항상 ResizeObserver로 크기를 지정하고 캔버스 그리드를 devicePixelContentBoxSize로 조정합니다.
const observer = new ResizeObserver(([entry]) => {
const dpc = entry.devicePixelContentBoxSize;
canvas.width = dpc ? dpc[0].inlineSize : Math.round(entry.contentRect.width * window.devicePixelRatio);
canvas.height = dpc ? dpc[0].blockSize : Math.round(entry.contentRect.height * window.devicePixelRatio);
});
const supportsDevicePixelContentBox =
typeof ResizeObserverEntry !== 'undefined' &&
'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);
CSS 픽셀을 캔버스 그리드 좌표로 변환
paint 핸들러는 DOM 측정과 그리기 호출을 결합하는 경우가 많습니다(예: 크기에 따라 요소 배치). 두 가지는 서로 다른 단위를 사용합니다.
- 캔버스 픽셀은
canvas.width및canvas.height로 크기가 지정된 캔버스 그리드를 설명합니다. 예를 들어drawElementImage()그리기 메서드의dx,dy,dwidth,dheight매개변수 또는fillRect(),translate()와 같은 기타 캔버스 API 매개변수가 있습니다. - CSS 픽셀은 페이지를 설명합니다.
offsetWidth및clientWidth와 같은 DOM 측정값과offsetX와 같은 포인터 이벤트 좌표는 CSS 픽셀 단위입니다.
다음은 다양한 API 매개변수에서 사용하는 단위의 개요입니다.
| 값 | 단위 |
|---|---|
canvas.width 및 canvas.height |
캔버스 그리드 픽셀 |
drawElementImage()의 dx, dy, dwidth, dheight를 포함한 2D 그리기 매개변수 |
캔버스 그리드 픽셀 |
canvas.getElementImageDefaultSize()에서 width 및 height 반환 |
캔버스 그리드 픽셀 |
소스 직사각형: drawElementImage() 및 texElementSubImage2D()의 sx, sy, swidth, sheight, drawElementImageToTexture()의 sourceX, sourceY, sourceWidth, sourceHeight |
CSS 픽셀 |
텍스처 크기: texElementSubImage2D()의 width 및 height 옵션, drawElementImageToTexture()의 size |
텍스처 픽셀입니다. 기본적으로 캔버스 그리드 픽셀 단위의 엘리먼트 크기(반올림됨) |
요소 도형: getElementTransform(), updateElementGeometry()의 canvasTransform 및 canvasClip 옵션 |
CSS 픽셀 |
offsetWidth, clientWidth, getBoundingClientRect(), 포인터 이벤트 좌표, 크기 조절 관찰자 항목의 contentBoxSize와 같은 DOM 측정값 |
CSS 픽셀 |
크기 조절 관찰자 항목의 devicePixelContentBoxSize |
기기 픽셀 |
잘못된 단위를 사용하면 캔버스에 그려진 HTML 콘텐츠의 크기가 예상과 다를 수 있습니다. HTML을 캔버스에 그릴 때 CSS 픽셀 값을 캔버스 그리드 픽셀로 변환해야 합니다. 이렇게 하려면 캔버스 그리드 크기와 캔버스 CSS 크기 간의 비율을 계산합니다(가로 canvas.width / cssContentWidth, 세로 canvas.height / cssContentHeight). 크기 조절 관찰자는 다음과 같이 비율을 계산하기에 편리한 위치입니다.
// The number of canvas grid pixels per CSS pixel
let scaleX = 1;
let scaleY = 1;
const observer = new ResizeObserver(([entry]) => {
const dpc = entry.devicePixelContentBoxSize;
const cssContentWidth = entry.contentRect.width;
const cssContentHeight = entry.contentRect.height;
// Best practice: make the canvas grid match the physical pixel grid
if (dpc) {
canvas.width = dpc[0].inlineSize;
canvas.height = dpc[0].blockSize;
} else {
canvas.width = Math.round(cssContentWidth * window.devicePixelRatio);
canvas.height = Math.round(cssContentHeight * window.devicePixelRatio);
}
scaleX = canvas.width / cssContentWidth;
scaleY = canvas.height / cssContentHeight;
});
const supportsDevicePixelContentBox =
typeof ResizeObserverEntry !== 'undefined' &&
'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);
그런 다음 CSS 픽셀 값에 scaleX 및 scaleY를 곱한 후 그리기 메서드에 전달합니다. 예를 들어 캔버스에서 드로어블 요소를 중앙에 배치하려면 다음 단계를 따르세요.
const ctx = canvas.getContext('2d');
const card = document.getElementById('card');
canvas.onpaint = () => {
ctx.reset();
// offsetWidth and offsetHeight are in CSS pixels.
// Convert them to canvas grid coordinates.
const width = card.offsetWidth * scaleX;
const height = card.offsetHeight * scaleY;
const x = (canvas.width - width) / 2;
const y = (canvas.height - height) / 2;
// drawElementImage() takes canvas grid coordinates.
ctx.drawElementImage(card, x, y);
};
마지막으로 drawElementImage()는 요소의 테두리 상자를 그리므로 box-sizing: border-box를 사용하여 카드의 width 및 height에 패딩과 테두리를 포함하여 카드가 설정한 크기로 그려지도록 합니다.
#card {
/* Make width and height include padding and border */
box-sizing: border-box;
width: 200px;
padding: 16px;
}
HTML-in-Canvas의 접근성 이해하기
일반적으로 <canvas content="drawable">의 HTML 콘텐츠는 일반 DOM과 동일한 방식으로 접근성에 노출됩니다. 즉, aria-hidden="true" 또는 inert와 같은 속성을 사용하여 명시적으로 숨기지 않는 한 접근성 트리에 표시됩니다. 하지만 한 가지 중요한 예외가 있습니다. 렌더링된 도형이 아직 설정되거나 업데이트되지 않은 drawable 요소입니다.
다음과 같은 경우 <canvas content="drawable"> 내의 drawable 요소에 렌더링된 도형이 없습니다.
- 기본적으로 도형을 자동으로 업데이트하는
drawElementImage()로 아직 렌더링되지 않았습니다. updateElementGeometry()로 도형이 명시적으로 업데이트되지 않았습니다.
drawable 요소에 렌더링된 도형이 없는 경우 이 drawable 요소와 drawable 하위 트리는 도형 정보 없이 접근성 트리에 보고됩니다. 이는 예를 들어 스크린 리더가 시맨틱 콘텐츠에 액세스할 수 있도록 하면서 시각 보조 도구 (예: 돋보기)에 그려지지 않은 요소를 필터링하도록 신호를 보내는 데 유용합니다.
다음 예시에서는 이미지와 그림을 별도로 그리면서 그림, 이미지, 캡션의 의미를 유지합니다.
<canvas id="canvas" width="400" height="300" content="drawable">
<figure id="figure">
<img drawable id="image" src="..." alt="image alt text" />
<figcaption drawable id="caption">A caption</figcaption>
</figure>
</canvas>
<script>
const ctx = canvas.getContext('2d');
canvas.onpaint = () => {
ctx.reset();
// Note: Because `preserveElementGeometry` defaults to false, the
// `drawElementImage` call automatically updates geometry, ensuring
// the image and caption have geometry information for accessibility.
ctx.drawElementImage(image, 0, 0);
ctx.drawElementImage(caption, 0, 200);
};
</script>
대체 전략
HTML-in-Canvas API는 Chrome 157에서 제공되며 일부 브라우저에서는 지원되지 않을 수 있습니다. 모든 사용자가 솔루션을 원활하게 사용할 수 있도록 브라우저가 API를 지원하는지 확인하고 필요한 경우 대체 솔루션으로 대체합니다.
export function supportsHtmlInCanvas() {
if (typeof document === 'undefined') return false;
const canvas = document.createElement('canvas');
canvas.setAttribute('content', 'drawable');
return canvas.content === 'drawable';
}
API가 지원되지 않는 경우 구현 목표에 따라 다음 대안 중 하나를 선택할 수 있습니다.
- 대체 UI
- 플로팅 HTML 오버레이
- three-html-render 라이브러리
- html2canvas
- dom-to-image
다음 단계
- 2D 컨텍스트에서 HTML을 렌더링하는 방법을 알아보세요.
- WebGL 캔버스에서 HTML을 렌더링하는 방법을 알아보세요.
- WebGPU 캔버스에서 HTML을 렌더링하는 방법을 알아보세요.
- 대체 전략 알아보기
- GitHub에서 문제를 제출하여 의견을 공유하세요.
- 커뮤니티의 HTML-in-Canvas 데모 모음을 확인하세요.