Skip to content

Pan and zoom for images

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",
    },
    }),
    ],
    });