fix(study): 확대 회귀 진짜 root cause — inline style 의 reactive cursor 가 imperative width 덮어씀

진단 도구로 확정: 펜 클릭 시 canvas:1512×677 정상 → 지우개 클릭 시 canvas:3024×1354
정확히 2배 (= cssWidth × dpr). canvas.style.width 가 사라져 internal pixel 그대로
displayed → 화면상 2배 확대.

원인: <canvas style="...; cursor: {tool === 'eraser' ? ...}"> 가 reactive variable
(tool) 포함한 inline style. tool 변경 시 Svelte 가 inline style attribute *전체*
재설정 → resizeCanvas() 의 imperative `canvas.style.width = ...px` 가 덮어써져 사라짐.
새로고침 / 창 이동 시 resizeCanvas 다시 호출되며 복구되던 이유.

Fix:
- style:cursor / style:width / style:height directive 로 분리. Svelte 의 style:property
  는 해당 property 만 set 하고 다른 inline style 안 건드림.
- 정적 inline style="..." 에서 cursor 제거.
- resizeCanvas 의 imperative style.width/height 라인 제거 (svelte directive 가 처리).

내부 pixel 은 그대로 imperative set 유지 (canvas.width = cssWidth × dpr — DOM
attribute 라 inline style 과 별개).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-27 14:48:50 +09:00
parent 5b2580d96c
commit c8360cd58a
@@ -159,8 +159,10 @@
const dpr = window.devicePixelRatio || 1;
canvas.width = cssWidth * dpr;
canvas.height = cssHeight * dpr;
canvas.style.width = `${cssWidth}px`;
canvas.style.height = `${cssHeight}px`;
// canvas.style.width/height 는 svelte 의 style:width / style:height directive 가
// reactive 로 처리. imperative set 은 inline style="..." 의 reactive 변수
// (예: style="cursor: {tool}") 갱신 시 함께 덮어써서 사라지는 회귀 (확대 = canvas
// 가 internal pixel 그대로 displayed) 의 원인이었음.
const ctx = canvas.getContext('2d');
if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
scheduleRedraw();
@@ -802,7 +804,10 @@
oncontextmenu={(e) => e.preventDefault()}
onselectstart={(e) => e.preventDefault()}
class="block"
style="touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; cursor: {tool === 'eraser' ? 'cell' : 'crosshair'};"
style:cursor={tool === 'eraser' ? 'cell' : 'crosshair'}
style:width={cssWidth ? `${cssWidth}px` : undefined}
style:height={cssHeight ? `${cssHeight}px` : undefined}
style="touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;"
></canvas>
{#if DBG}