panera Svelte Themes

Panera

A Svelte library for panning and zooming in SVG, Canvas and HTML.

panera

A Svelte library for panning and zooming in SVG, Canvas and HTML.

Install

pnpm add panera

Peer requirement: svelte ^4 || ^5

Example

<script>
    import { Panera, Svg } from 'panera';
    let width = 800,
        height = 300;

    let pan;
    function reset() {
        pan.reset();
    }
    function to() {
        pan.to({ x: 80, y: 40, width: 200, height: 120 }, { duration: 600 });
    }
</script>

<button on:click={reset}>Reset</button> <button on:click={to}>Zoom to rect</button>

<Panera bind:this={pan} {width} {height}>
    <Svg>
        <rect fill="tomato" x="80" y="40" width="200" height="120" />
    </Svg>
</Panera>

See more examples here

API

  • Provider: <Panera />
  • Layers: <Svg />, <Canvas />, <Html />
  • Control via bind:this: to(), reset(), interpolate(), getView(), getBox()

<Panera /> props

  • width?: number | null — container width in pixels (required for fits)
  • height?: number | null - container height in pixels (required for fits)
  • duration?: number — default 1000 ms (used by to() & reset())
  • easing?: (t:number)=>number — default cubicInOut
  • bound?: boolean — clamp view inside container (default true)
  • debug?: boolean — overlay a non-scaling red debug rect (default false)

Methods (via bind:this)

  • reset(opts?)
    opts = { duration?: number, easing?: (t)=>number, debug?: boolean }
    Reset the view back to k=1, x=0, y=0.

  • to(rect, opts?)
    rect = {x, y, width, height}
    opts = { duration?, easing?, debug?, bound? }
    Tweens to the fitted view of the rectangle.

  • interpolate(a, b, opts?)
    a = { x, y, width, height }, b = { x, y, width, height }
    opts = { t?: number, easing?: (t)=>number, bound?: boolean, debug?: boolean }
    Instant scrub (no tween). Fits both a and b, then blends view using t (after easing).

  • getView(): Readable<{ k:number, x:number, y:number }>
    A Svelte store reflecting the current view.

  • getBox(): Readable<{ x:number, y:number, width:number, height: number }>
    A Svelte store reflecting the current bounding box.

Layers

  • <Svg>
    Renders a <svg> with viewBox and a <g> that applies:
    transform="scale(k) translate(x, y)".
    Debug rect uses vector-effect="non-scaling-stroke".

  • <Canvas>
    DPR-aware canvas; applies scale(k) then translate(x, y).
    Prop:
    render(ctx, {k, x, y}, {width, height, dpr}) => void

  • <Html>
    Absolutely-positioned HTML that follows the same transform via CSS:
    transform: scale(k) translate(xpx, ypx); transform-origin: 0 0;
    Place children with position:absolute; left/top in object coordinates.

Notes

  • SSR-safe: Canvas guards window/devicePixelRatio.
  • Transforms: All layers use the same order — scale(k) then translate(x, y) — so coordinates match.
  • Bounds: When bound: true, content is clamped so edges don’t show outside the container.

Development

pnpm format       # prettier
pnpm lint         # prettier check + eslint
pnpm test         # vitest unit tests
pnpm prepack      # build library to dist/ (svelte-package + publint)
pnpm pack         # output a tarball

Tests

Core math is tested in src/lib/core.spec.js:

  • fitRectToContainer (aspect fit + bounds)
  • lerp

License

MIT

Top categories

Loading Svelte Themes