Phoenix LiveView function component that mounts a Fresco viewer.
Renders a host <div> containing a stage <div> and an <img>. The
companion JS hook (FrescoViewer in priv/static/fresco.js) attaches
Pointer Events for unified mouse/touch/pen gestures, applies
transform: translate3d(tx, ty, 0) scale(s) on the stage element, and
publishes a handle to window.Fresco.viewerFor(id) so peer extensions
(Tessera, future Etcher) can attach.
The image is server-rendered inside the host so it appears immediately —
the hook reads naturalWidth/Height on mount and fits the image into
the viewport without any "blank box" flash.
Usage
<Fresco.viewer
id="photo"
src={~p"/uploads/photo.jpg"}
class="w-full h-[80vh] rounded"
/>Interactions
- Pan: click/touch drag, arrow keys (after focusing the viewer)
- Zoom: mouse wheel (centered on cursor), pinch (two-finger on touch
or trackpad), double-click (2× centered on cursor),
+/-keys - Reset: nav button,
0key — fits the image to the viewport - Fullscreen: nav button,
fkey — toggles native browser fullscreen
Parent app setup
Import the JS hook and spread FrescoHooks into your LiveSocket hooks:
import "../../deps/fresco/priv/static/fresco.js"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { ...window.FrescoHooks, ...colocatedHooks }
})
Summary
Functions
Renders a Fresco viewer for the given image source.
Functions
Renders a Fresco viewer for the given image source.
Companion JS hook attaches gesture handlers, fits the image to the viewport, and publishes the handle for peer extensions.
Attributes
id(:string) (required) - DOM id; must be unique on the page.src(:string) (required) - URL of the image to display.The default behavior treats
srcas a plain image URL. Extensions can register source providers viawindow.Fresco.registerSourceProvider/2to intercept specific URL patterns; the bundled engine handles{type: "image"}sources and throws a clear error for anything else (Tessera-style tile sources are planned for a later release).class(:string) - CSS classes for the viewer container. Defaults to"w-full h-96".infinite_canvas(:boolean) - Whentrue, drops the default "image must cover viewport" clamp so the user can pan freely beyond the image edges and zoom out until the image is a thumbnail in the middle of an empty canvas. The viewer's background dot-grid (always present) becomes visible in the void around the image so it reads as "canvas," not "broken layout." Defaultfalsekeeps the stock single-image viewer behavior — pan stays location-locked inside the image, zoom-out floor is fit-to-viewport.Pairs naturally with future layered overlays (e.g. Etcher annotations) that need to draw shapes, callouts, or labels in the white space around the image, Figma/Miro/Excalidraw style.
Defaults to
false.theme(:atom) - Color scheme for the viewer host background, dot grid, and nav buttons.:system(default) — follow the OS / browserprefers-color-scheme.:light— force light palette regardless of OS preference.:dark— force dark palette regardless of OS preference.:inherit— emit only the host structure; the parent app's CSS supplies the six--fresco-*custom properties. Use this to wire Fresco to a parent theme system (daisyUI, custom palettes, …).
Theming is implemented as CSS custom properties on
.fresco-viewer(--fresco-bg,--fresco-grid-dot,--fresco-nav-bg,--fresco-nav-bg-hover,--fresco-nav-fg,--fresco-nav-focus).Defaults to
:system. Must be one of:system,:light,:dark, or:inherit.Global attributes are accepted.