cgeorgiaw HF Staff commited on
Commit
99194c3
·
verified ·
1 Parent(s): c977220

Tame zoom: shift+wheel, max 4x, clamp pan

Browse files
Files changed (1) hide show
  1. app.py +16 -7
app.py CHANGED
@@ -297,8 +297,8 @@ def choose(side: str, session: dict):
297
 
298
  # ---------- UI ----------------------------------------------------------------
299
 
300
- # Wheel-zoom + drag-pan + dbl-click reset on every <img> inside a `.zoomable`
301
- # Gradio image component. Resets zoom when the image src changes.
302
  ZOOM_HEAD = """
303
  <style>
304
  .zoomable [data-testid="image"], .zoomable .image-frame, .zoomable .image-container {
@@ -312,17 +312,26 @@ ZOOM_HEAD = """
312
  </style>
313
  <script>
314
  (function () {
 
315
  function bind(img) {
316
  if (img.__zoomBound) return;
317
  img.__zoomBound = true;
318
  let scale = 1, tx = 0, ty = 0;
319
  let dragging = false, lastX = 0, lastY = 0;
320
- const apply = () => { img.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`; };
321
- const reset = () => { scale = 1; tx = 0; ty = 0; apply(); };
 
 
 
 
 
 
 
322
  img.addEventListener('wheel', (e) => {
 
323
  e.preventDefault();
324
- const factor = e.deltaY < 0 ? 1.2 : 1 / 1.2;
325
- const ns = Math.min(12, Math.max(1, scale * factor));
326
  if (ns === scale) return;
327
  const rect = img.getBoundingClientRect();
328
  const ox = e.clientX - rect.left;
@@ -370,7 +379,7 @@ with gr.Blocks(title="Denoising A/B Judging", theme=gr.themes.Soft(), head=ZOOM_
370
  "denoised versions of it below, labelled **A** and **B**. The two "
371
  "versions come from different denoising approaches, presented in a "
372
  "random left/right order so the comparison stays blind. "
373
- "Scroll on an image to zoom, drag to pan, double-click to reset.\n\n"
374
  "**Which denoised image would you rather work with?**"
375
  )
376
 
 
297
 
298
  # ---------- UI ----------------------------------------------------------------
299
 
300
+ # Shift+wheel zoom, drag-pan when zoomed, dbl-click reset. Pan is clamped so
301
+ # the image always fills its container; max 4x. Resets on src change.
302
  ZOOM_HEAD = """
303
  <style>
304
  .zoomable [data-testid="image"], .zoomable .image-frame, .zoomable .image-container {
 
312
  </style>
313
  <script>
314
  (function () {
315
+ const MAX_SCALE = 4;
316
  function bind(img) {
317
  if (img.__zoomBound) return;
318
  img.__zoomBound = true;
319
  let scale = 1, tx = 0, ty = 0;
320
  let dragging = false, lastX = 0, lastY = 0;
321
+ function clamp() {
322
+ const w = img.clientWidth || img.naturalWidth || 1;
323
+ const h = img.clientHeight || img.naturalHeight || 1;
324
+ const minX = w - w * scale, minY = h - h * scale;
325
+ tx = Math.min(0, Math.max(minX, tx));
326
+ ty = Math.min(0, Math.max(minY, ty));
327
+ }
328
+ const apply = () => { clamp(); img.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`; };
329
+ const reset = () => { scale = 1; tx = 0; ty = 0; img.style.transform = ''; };
330
  img.addEventListener('wheel', (e) => {
331
+ if (!e.shiftKey) return; // plain scroll = page scroll
332
  e.preventDefault();
333
+ const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15;
334
+ const ns = Math.min(MAX_SCALE, Math.max(1, scale * factor));
335
  if (ns === scale) return;
336
  const rect = img.getBoundingClientRect();
337
  const ox = e.clientX - rect.left;
 
379
  "denoised versions of it below, labelled **A** and **B**. The two "
380
  "versions come from different denoising approaches, presented in a "
381
  "random left/right order so the comparison stays blind. "
382
+ "Hold **Shift** and scroll over an image to zoom (up to 4×), drag to pan, double-click to reset.\n\n"
383
  "**Which denoised image would you rather work with?**"
384
  )
385