Skip to content

Pan and zoom for images

Intorduction

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

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

intentionmousetrackpad/touchpadtouchscren
panclick + moveclick + movetwo finger drag
zoomCtrl + wheelpinchpinch
resetdouble clickdouble clickdouble tap
scrollwheeltwo finger dragone finger drag

Pay attention:

  • gestures intentionally selected to not interfere with the system’s default scroll gestures, to avoid “scroll traps”
  • Cmd + click - zoom in
  • Alt + click - zoom out
  • First double click (tap) - zoom in x2

Try to use gestures with this image 👇

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-10-5 0 5 10sin(x)

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

  • style pan-zoom buttons
  • create rehype plugin to wrap images into container (<figure class="beoe"></figure>), to avoid creating it on the client side