@nickpaustian/spektra
v0.1.0
Published
An image pan and zoom viewer: an inline picture that expands to full screen and springs back, with drag, wheel, pinch and keys through helm, double-tap zoom and IIIF deep-zoom tiles
Downloads
146
Maintainers
Readme
@nickpaustian/spektra
An image that pans and zooms. Inline, a picture sits in the page and a click springs it from its box to full screen, where a drag or two fingers pan it (and fling it on release), the wheel, a pinch, a double tap and the keys zoom it toward the pointer, and Escape, a click beside it or a zoom out past the minimum spring it home. Expanded, it fills its element from the start. Pan is held in a rubber band that lets go while zooming in and tightens again after. A IIIF image service can lay sharper tiles over the picture as it is zoomed. Input is through helm, so a mouse, a trackpad, a touch screen and a pen all feel the same.
<figure id="plate"><img src="plate.jpg" alt="A plate" /></figure>import { Spektra } from "@nickpaustian/spektra"
const viewer = new Spektra("#plate", {
mode: "inline",
padding: 40,
maxScale: 8,
iiif: "https://images.example/iiif/3/plate",
})The element is replaced in the page by an anchor holding the same image, and the viewer floats above, hidden until it is expanded. The anchor takes the element's place in layout, so size the element as you would the picture. dispose() puts everything back.
Options
| option | default | |
|---|---|---|
| mode | expanded | inline expands on click; expanded fills its element |
| expandOnClick | true | a click on the anchor expands |
| fit | contain | contain, cover or none at zoom 0 |
| padding | 0 | between the container's edge and the fitted image, a number or { top, right, bottom, left } |
| boundsPadding | 0 | how far past the edge the image may be panned before the band pulls it back |
| minScale, maxScale | 0.5 inline or 1 expanded, 5 | zoom limits as multiples of the fitted size; an in-place viewer does not shrink below its fit |
| initialScale | the fit | opening zoom |
| invertScroll | false | wheel down zooms in |
| collapseOnZoomOut, collapseThreshold | true, −0.5 | zooming this many doublings below minScale collapses an inline viewer |
| doubleTapZoom, doubleTapZoomLevel | true, 4 | a double tap springs to this scale, or back to the fit |
| keyboard | true | arrows pan; +, - and shift with up or down zoom |
| closeOnEscape | true | |
| overlayBlur | 0 | pixels of backdrop blur behind the expanded viewer |
| closeElement | a Close button | your own close control, used where it is |
| iiif, iiifOptions | | a IIIF image service or manifest URL for tiles |
| autoUpdate | true | step from its own frame loop; false and call update(dt) yourself |
| createSignal | | make the signals with your own signal class |
| feel | DEFAULT_FEEL | the springs and friction, all per second: panFriction, band, transitionZoom, transitionPan, flingWindow, overzoom, keyPanSpeed, keyZoomSpeed |
| onExpandStart, onExpandEnd, onCollapseStart, onCollapseEnd | | callbacks; the same arrive as signals |
The feel
Every spring and friction is per second, so the motion is the same at 60 and 120 Hz. panFriction is the fraction of a fling's speed left after a second (0.00005 by default: a flick coasts for a tenth of a second or so). band is the spring that pulls the image back inside its bounds after a drag past the edge, and the zoom back inside its limits after an over-zoom; the zoom keeps no velocity between frames, so that pull is a plain ease with no overshoot. overzoom is how many doublings past a zoom limit the wheel or a pinch can push before the push fades to nothing: a quarter in place, and inline enough to reach the zoom-out collapse. transitionZoom and transitionPan are the springs of expand, collapse and zoomTo. Pass any of them in feel to retune; DEFAULT_FEEL holds the rest.
Methods and signals
expand(),collapse(),toggle();Spektra.isOpenandSpektra.close()for whichever viewer is open;Spektra.get(el)andSpektra.destroy(el)by element or selector.zoomTo(scale, aboutPointer = true)springs to a scale;togglePresetZoom()is what a double tap does.setImageSource(src, iiif?)shows another picture; the anchor keeps the old one until the new has loaded.setTileSource(url, options?)andsetTiles(source | null)change the tiles.viewis{ x, y, zoom, scale }: the pan from centre in pixels, the zoom as a log2 exponent and as a multiple of the fit.modeandisTransitioningsay where it is.signals.onExpandStart,onExpandEnd,onCollapseStart,onCollapseEndcarry the viewer;onViewChangefires each frame the image moved.
Tiles
IIIFTileSource reads an Image API info.json (v2 or v3), or the first image service in a Presentation manifest, and once the image is zoomed past what its own pixels can show (tileThreshold, default 1.2 screen pixels per image pixel) lays tiles of the right level over it, keeping those already painted until the new level covers the view so a zoom never flashes blurry. TileSource is the interface: update(view) each frame and dispose(), for a pyramid of your own.
Styling
Styles are injected once per document. The classes to override: .spektra-anchor (the inline box), .spektra-expanded (the full-screen viewer, .spektra-dragging while a drag is on), .spektra-overlay (its background, --spektra-overlay for the colour, .spektra-active once faded in), .spektra-close (the default button) and .spektra-image (the moving box). The viewer, overlay and default button are children of body while expanded, so a focus trap takes those three.
