Skip to content

Pan and zoom for images

Produced by GNUPLOT 5.4 patchlevel 0-1-0.8-0.6-0.4-0.2 0 0.2 0.4 0.6 0.8 1-20-15-10-5 0 5 10sin(x)

Try to use gestures with this image ๐Ÿ‘†

Introduction

@beoe/pan-zoom โ†— is a small client-side script that adds pan and zoom capabilities to SVG images (or any DOM node, actually).

There are many similar scripts, but the main difference is that this one supports gestures for all types of devices:

IntentionMouseTrackpad/TouchpadTouchscreen
PanClick + moveClick + moveTwo-finger drag
ZoomCtrl + wheelPinchPinch
ResetDouble clickDouble clickDouble tap
ScrollWheelTwo-finger dragOne-finger drag

Pay attention to the following:

  • Gestures are intentionally selected to avoid interfering with the systemโ€™s default scroll gestures, to prevent โ€œscroll traps.โ€
  • Cmd + click - zoom in.
  • Alt + click - zoom out.
  • The first double click (or tap) - zooms in by 2x.

Installation

  1. Install dependencies

    Terminal window
    pnpm add @beoe/pan-zoom
  2. Add svgpanzoom.ts

    src/components/svgpanzoom.ts
    import "@beoe/pan-zoom/css/PanZoomUi.css";
    import { PanZoomUi } from "@beoe/pan-zoom";
    // for BEOE diagrams
    document.querySelectorAll(".beoe").forEach((container) => {
    const element = container.firstElementChild;
    if (!element) return;
    // @ts-expect-error
    new PanZoomUi({ element, container }).on();
    });
    // for content images
    document
    .querySelectorAll(
    ".sl-markdown-content > img[src$='.svg' i]," +
    ".sl-markdown-content > p > img[src$='.svg' i]," +
    // for development environment
    ".sl-markdown-content > img[src$='f=svg' i]," +
    ".sl-markdown-content > img[src$='f=svg' i]"
    )
    .forEach((element) => {
    if (element.parentElement?.tagName === "PICTURE") {
    element = element.parentElement;
    }
    const container = document.createElement("figure");
    container.classList.add("beoe", "not-content");
    element.replaceWith(container);
    container.append(element);
    // @ts-expect-error
    new PanZoomUi({ element, container }).on();
    });
  3. Use svgpanzoom.ts in the base layout

Starlight specific code

  1. Use svgpanzoom.ts in the PageFrame

    src/components/PageFrame.astro
    ---
    import type { Props } from "@astrojs/starlight/props";
    import Default from "@astrojs/starlight/components/PageFrame.astro";
    ---
    <Default {...Astro.props}>
    <slot name="header" slot="header" />
    <slot name="sidebar" slot="sidebar" />
    <slot />
    </Default>
    <script>
    import "./svgpanzoom.ts";
    </script>
  2. Override PageFrame in Astro config

    astro.config.mjs
    export default defineConfig({
    integrations: [
    starlight({
    components: {
    PageFrame: "./src/components/PageFrame.astro",
    },
    }),
    ],
    });

Further Improvements

  • Create a Rehype plugin to wrap images in a container (<figure class="beoe"></figure>) to avoid creating it on the client side.
  • Do not stretch images if they are smaller than viewport
  • Prevent clicks on drag or pan
  • minimap and full-screen mode, like in reactflow โ†—