Patterns
Zoom
Images zoom with pinch (or Ctrl + wheel), double-tap, double-click and the +, - and 0 keys, and pan while zoomed. Each viewer can set how far a double-tap zooms (step) and how far zoom goes (max), or turn zoom off.
Loading the live example…
zoom.tsx
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox, type FluidboxRootProps } from "@ogdakke/fluidbox/react";
import { photos } from "./data";
function Gallery({ zoom }: Pick<FluidboxRootProps, "zoom">) {
return (
<Fluidbox.Root items={photos} zoom={zoom}>
<div className="pattern-grid">
{photos.map((photo) => (
<Fluidbox.Trigger
key={photo.src}
item={photo}
className="pattern-thumb"
>
<img src={photo.src} alt={itemLabel(photo)} />
</Fluidbox.Trigger>
))}
</div>
</Fluidbox.Root>
);
}
export function Zoom() {
return (
<>
{/* Double-click zooms to 3x; pinch and wheel stop at 6x. */}
<Gallery zoom={{ step: 3, max: 6 }} />
<Gallery zoom={false} />
</>
);
}Zoom.vue
<script setup lang="ts">
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/vue";
import { photos } from "./data";
</script>
<template>
<Fluidbox.Root :items="photos" :zoom="{ step: 3, max: 6 }">
<div class="pattern-grid">
<Fluidbox.Trigger
v-for="photo in photos"
:key="photo.id"
:item="photo"
class="pattern-thumb"
>
<img :src="photo.src" :alt="itemLabel(photo)" />
</Fluidbox.Trigger>
</div>
</Fluidbox.Root>
<Fluidbox.Root :items="photos" :zoom="false">
<div class="pattern-grid">
<Fluidbox.Trigger
v-for="photo in photos"
:key="photo.id"
:item="photo"
class="pattern-thumb"
>
<img :src="photo.src" :alt="itemLabel(photo)" />
</Fluidbox.Trigger>
</div>
</Fluidbox.Root>
</template>zoom.tsx
import { For } from "solid-js";
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox, type FluidboxRootProps } from "@ogdakke/fluidbox/solid";
import { photos } from "./data";
function Gallery(props: { zoom: FluidboxRootProps["zoom"] }) {
return (
<Fluidbox.Root items={photos} zoom={props.zoom}>
<div class="pattern-grid">
<For each={photos}>
{(photo) => (
<Fluidbox.Trigger item={photo} class="pattern-thumb">
<img src={photo.src} alt={itemLabel(photo)} />
</Fluidbox.Trigger>
)}
</For>
</div>
</Fluidbox.Root>
);
}
export function Zoom() {
return (
<>
<Gallery zoom={{ step: 3, max: 6 }} />
<Gallery zoom={false} />
</>
);
}zoom.ts
import { Component } from "@angular/core";
import { itemLabel } from "@ogdakke/fluidbox";
import { FluidboxModule } from "@ogdakke/fluidbox/angular";
import { photos } from "./data";
@Component({
selector: "pattern-zoom",
standalone: true,
imports: [FluidboxModule],
template: `
<div fluidboxRoot [items]="photos" [zoom]="{ step: 3, max: 6 }" class="pattern-grid">
@for (photo of photos; track photo.id) {
<button [fluidboxTrigger]="photo" class="pattern-thumb">
<img [src]="photo.src" [alt]="label(photo)" />
</button>
}
</div>
<div fluidboxRoot [items]="photos" [zoom]="false" class="pattern-grid">
@for (photo of photos; track photo.id) {
<button [fluidboxTrigger]="photo" class="pattern-thumb">
<img [src]="photo.src" [alt]="label(photo)" />
</button>
}
</div>
`,
})
export class Zoom {
readonly photos = photos;
readonly label = itemLabel;
}zoom.html
<fluidbox-gallery class="pattern-grid" zoom-step="3" zoom-max="6">
<fluidbox-item item-id="photo-aurora">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/aurora.svg" alt="Aurora over the tundra" />
</button>
<template slot="content">
<img src="/art/aurora.svg" alt="Aurora over the tundra" width="1100" height="1650" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-ridge">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/ridge.svg" alt="Night ridge" />
</button>
<template slot="content">
<img src="/art/ridge.svg" alt="Night ridge" width="1200" height="1500" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-fjord">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/fjord.svg" alt="Fjord in the blue hour" />
</button>
<template slot="content">
<img src="/art/fjord.svg" alt="Fjord in the blue hour" width="1200" height="1600" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-lagoon">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/lagoon.svg" alt="Lagoon after sunset" />
</button>
<template slot="content">
<img src="/art/lagoon.svg" alt="Lagoon after sunset" width="1800" height="1200" />
</template>
</fluidbox-item>
</fluidbox-gallery>
<fluidbox-gallery class="pattern-grid" nozoom>
<fluidbox-item item-id="photo-aurora">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/aurora.svg" alt="Aurora over the tundra" />
</button>
<template slot="content">
<img src="/art/aurora.svg" alt="Aurora over the tundra" width="1100" height="1650" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-ridge">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/ridge.svg" alt="Night ridge" />
</button>
<template slot="content">
<img src="/art/ridge.svg" alt="Night ridge" width="1200" height="1500" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-fjord">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/fjord.svg" alt="Fjord in the blue hour" />
</button>
<template slot="content">
<img src="/art/fjord.svg" alt="Fjord in the blue hour" width="1200" height="1600" />
</template>
</fluidbox-item>
<fluidbox-item item-id="photo-lagoon">
<button slot="trigger" type="button" class="pattern-thumb">
<img src="/art/lagoon.svg" alt="Lagoon after sunset" />
</button>
<template slot="content">
<img src="/art/lagoon.svg" alt="Lagoon after sunset" width="1800" height="1200" />
</template>
</fluidbox-item>
</fluidbox-gallery>zoom.ts
import { createFluidbox, itemLabel, type FluidboxOptions } from "@ogdakke/fluidbox";
import { photos } from "./data";
function gallery(root: HTMLElement, zoom: FluidboxOptions["zoom"]) {
const fluidbox = createFluidbox({ items: photos, zoom });
const grid = document.createElement("div");
grid.className = "pattern-grid";
photos.forEach((photo, index) => {
const button = document.createElement("button");
button.type = "button";
button.className = "pattern-thumb";
button.append(Object.assign(new Image(), { src: photo.src, alt: itemLabel(photo) }));
grid.append(button);
fluidbox.bind(button, index);
});
root.append(grid);
return () => {
fluidbox.destroy();
grid.remove();
};
}
export function mountZoom(root: HTMLElement) {
const disposers = [gallery(root, { step: 3, max: 6 }), gallery(root, false)];
return () => disposers.forEach((dispose) => dispose());
}zoom: { step, max }orzoom: falseon the viewer. The custom elements also read thezoom-step,zoom-maxandnozoomattributes.- Zoom options apply live, even while the viewer is open. Call
fluidbox.update({ zoom }), or set a newzoomprop or property. Turning zoom off zooms a zoomed photo back out. stepdefaults to2andmaxto4.- While zoomed, the dialog has
data-zoomed. Pans can reach under the browser's bars; set--fluidbox-zoom-bleed: 0pxto keep them inside the window, or--fluidbox-zoom-clipto keep them inside the stage insets.