From 0e553a49d634744607e5a957dca701a70eb183ea Mon Sep 17 00:00:00 2001 From: jwinterm Date: Sat, 11 Jul 2026 08:02:40 -0400 Subject: [PATCH] feat(render): wheel + trackpad-pinch zoom in every mode Reusable zoom control (zoomControl.js): RK.attachZoom(host) adds wheel/pinch zoom to a render host; RK.applyZoom(host) scales whatever canvas is mounted (2D or WebGL) via CSS transform (pixelated so pixel art stays crisp), re-applied on mode switch. Trackpad pinch = ctrl+wheel, handled the same way; double-click resets. Wired into the tavern stage, so it works across Tiled/ASCII/Fancy/Iso/3D. Client-only. --- html/js/render/zoomControl.js | 40 +++++++++++++++++++++++++++++++++++ html/tavern.html | 2 ++ 2 files changed, 42 insertions(+) create mode 100644 html/js/render/zoomControl.js diff --git a/html/js/render/zoomControl.js b/html/js/render/zoomControl.js new file mode 100644 index 0000000..771b548 --- /dev/null +++ b/html/js/render/zoomControl.js @@ -0,0 +1,40 @@ +(function (root) { + 'use strict'; + var RK = root.RK = root.RK || {}; + + var MIN = 0.4, MAX = 4.0; + + // Apply the host's current zoom to whatever canvas is mounted in it (2D or WebGL). Re-callable + // after a renderer/mode switch so the new canvas inherits the zoom. + RK.applyZoom = function (host) { + if (!host) return; + var z = host._rkZoom || 1; + var c = host.querySelector('canvas'); + if (!c) return; + c.style.transformOrigin = 'center center'; + c.style.transform = z === 1 ? '' : 'scale(' + z + ')'; + c.style.imageRendering = 'pixelated'; // keep pixel art crisp when zoomed + }; + + // Wheel + trackpad-pinch zoom on a render host, shared by every mode. Trackpad pinch arrives as + // a wheel event with ctrlKey set; a normal wheel over the canvas zooms instead of scrolling. + RK.attachZoom = function (host) { + if (!host || host._rkZoomAttached) return; + host._rkZoomAttached = true; + host._rkZoom = host._rkZoom || 1; + host.style.overflow = 'hidden'; + host.title = 'Scroll / pinch to zoom ยท double-click to reset'; + + host.addEventListener('wheel', function (e) { + e.preventDefault(); + var factor = Math.exp(-e.deltaY * 0.0015); // smooth, direction-correct + host._rkZoom = Math.max(MIN, Math.min(MAX, (host._rkZoom || 1) * factor)); + RK.applyZoom(host); + }, { passive: false }); + + host.addEventListener('dblclick', function () { + host._rkZoom = 1; + RK.applyZoom(host); + }); + }; +})(window); diff --git a/html/tavern.html b/html/tavern.html index 538e121..95bc17c 100644 --- a/html/tavern.html +++ b/html/tavern.html @@ -14,6 +14,7 @@ + @@ -216,6 +217,7 @@ if (currentMode !== mode) { r.destroy(); return; } // switched again while loading currentRenderer = r; draw(); + if (RK.attachZoom) { RK.attachZoom(stageEl); RK.applyZoom(stageEl); } // wheel/pinch zoom, all modes var active = r.name || mode; setStatus(active === mode ? 'Render mode: ' + active