diff --git a/web-ui/arduino-emulator.html b/web-ui/arduino-emulator.html index e46344f..f1f1585 100644 --- a/web-ui/arduino-emulator.html +++ b/web-ui/arduino-emulator.html @@ -138,7 +138,7 @@ } #canvas-zoom-overlay.open { display:flex; } #canvas-zoom-img { - image-rendering:pixelated; max-width:90vw; max-height:90vh; + image-rendering:pixelated; border:2px solid #333; border-radius:4px; box-shadow:0 8px 40px rgba(0,0,0,.8); } @@ -7927,31 +7927,54 @@ function mobToggleBoard(){ (function() { const overlay = document.createElement('div'); overlay.id = 'canvas-zoom-overlay'; - overlay.innerHTML = '
클릭 또는 ESC로 닫기
'; + const zoomCanvas = document.createElement('canvas'); + zoomCanvas.id = 'canvas-zoom-img'; + overlay.appendChild(zoomCanvas); + const hint = document.createElement('div'); + hint.id = 'canvas-zoom-hint'; + hint.textContent = '클릭 또는 ESC로 닫기'; + overlay.appendChild(hint); document.body.appendChild(overlay); - function openZoom(canvas) { - const img = document.getElementById('canvas-zoom-img'); - img.src = canvas.toDataURL(); - // 자연 크기의 3배, 화면 90% 이내 - const scale = Math.min(3, window.innerWidth * 0.9 / canvas.width, window.innerHeight * 0.9 / canvas.height); - img.style.width = (canvas.width * scale) + 'px'; - img.style.height = (canvas.height * scale) + 'px'; - overlay.classList.add('open'); + let _src = null, _rafId = null; + + function _loop() { + if (!_src || !overlay.classList.contains('open')) return; + const ctx = zoomCanvas.getContext('2d'); + ctx.imageSmoothingEnabled = false; + ctx.drawImage(_src, 0, 0, zoomCanvas.width, zoomCanvas.height); + _rafId = requestAnimationFrame(_loop); } - function closeZoom() { overlay.classList.remove('open'); } + function openZoom(canvas) { + _src = canvas; + // 화면의 90% 이내에서 최대 정수 배율 + const maxScale = Math.min( + Math.floor(window.innerWidth * 0.9 / canvas.width), + Math.floor(window.innerHeight * 0.9 / canvas.height) + ); + const scale = Math.max(1, maxScale); + zoomCanvas.width = canvas.width * scale; + zoomCanvas.height = canvas.height * scale; + overlay.classList.add('open'); + cancelAnimationFrame(_rafId); + _loop(); + } + + function closeZoom() { + overlay.classList.remove('open'); + cancelAnimationFrame(_rafId); + _src = null; + } overlay.addEventListener('click', closeZoom); document.addEventListener('keydown', e => { if (e.key === 'Escape') closeZoom(); }); - // body 끝에서 실행되므로 DOM 이미 준비됨 — 직접 연결 ['tft-canvas', 'oled-canvas'].forEach(id => { const c = document.getElementById(id); - if (c) c.addEventListener('click', () => openZoom(c)); + if (c) c.addEventListener('click', e => { e.stopPropagation(); openZoom(c); }); }); - // canvas가 나중에 그려질 수 있으므로 전역 노출 window._canvasZoomOpen = openZoom; })();