01srcalt
# Basic usage
Point src at an image and the thumbnail becomes a full viewer: click it to open, scroll or double-click to zoom, pinch on touch, drag to pan. Escape or a backdrop click closes it again.

<script setup lang="ts">
import { HanaImgViewer } from 'hana-img-viewer'
</script>
<template>
<HanaImgViewer
src="https://pixiv-r2.caelum.moe/121909597.png"
alt="Artwork 121909597"
/>
</template>02previewSrc
# Progressive quality
Keep src light and pass the full-quality file as previewSrc. The overlay opens instantly with the thumbnail, then swaps in the high-res image the moment it finishes loading — no spinner, no layout shift.

<script setup lang="ts">
import { HanaImgViewer } from 'hana-img-viewer'
const thumb = '/covers/summer-320w.jpg'
const fullRes = '/covers/summer-2400w.png'
</script>
<template>
<HanaImgViewer :src="thumb" :preview-src="fullRes" alt="Summer cover" />
</template>03v-model:openupdate:open
# Controlled state
Bind v-model:open to drive visibility from outside. The viewer never flips state on its own — it emits update:open and waits for your state to change, so your ref stays the single source of truth.

<script setup lang="ts">
import { HanaImgViewer } from 'hana-img-viewer'
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button type="button" @click="open = true">Open from the outside</button>
<HanaImgViewer v-model:open="open" src="/photos/garden.png" alt="Garden" />
<p>open: {{ open }}</p>
</template>05container
# Custom container
Mount the overlay anywhere by passing container. Here it renders inside a scrollable panel in a dialog. While the ref is still null an open request simply waits — the viewer resolves it as soon as the container exists.
<script setup lang="ts">
import { HanaImgViewer } from 'hana-img-viewer'
import { ref } from 'vue'
const container = ref<HTMLElement | null>(null)
</script>
<template>
<section class="scrollable-panel">
<div ref="container" />
<HanaImgViewer
:container="container"
:close-on-escape="false"
src="/photos/artwork.png"
alt="Artwork"
/>
</section>
</template>06minZoommaxZoom
# Zoom control
Zoom is always on. Clamp the wheel, pinch, and double-click range with minZoom and maxZoom.

default: 0.5x · 10x
minZoom: 1 · maxZoom: 3<template>
<!-- Default 0.5x-10x zoom -->
<HanaImgViewer src="/covers/summer.jpg" alt="Cover" />
<!-- Wheel, pinch, and double-click clamped to 1x-3x -->
<HanaImgViewer :min-zoom="1" :max-zoom="3" src="/photos/garden.png" alt="Garden" />
</template>07closeOnBackdropClickcloseOnEscapeshowCloseButton
# Close behavior
All three escape hatches are opt-out: backdrop click, Escape, and the corner showCloseButton. The left viewer ignores backdrop clicks; the middle one ignores Escape; the right one hides the close button.

closeOnBackdropClick: false
closeOnEscape: false
showCloseButton: false<template>
<!-- Backdrop clicks are ignored; press Escape or the close button -->
<HanaImgViewer :close-on-backdrop-click="false" src="/a.png" alt="A" />
<!-- Escape is ignored; click the backdrop or the close button -->
<HanaImgViewer :close-on-escape="false" src="/b.png" alt="B" />
<!-- No close button; backdrop and Escape still close -->
<HanaImgViewer :show-close-button="false" src="/c.png" alt="C" />
</template>08transitionDuration
# Transition duration
The open and close FLIP runs for transitionDuration milliseconds. The left viewer keeps the 300 ms default; the right one stretches the animation to 600 ms.

default: 300ms
transitionDuration: 600<template>
<!-- Default 300ms open/close FLIP -->
<HanaImgViewer src="/covers/summer.jpg" alt="Cover" />
<!-- Longer, smoother open/close animation -->
<HanaImgViewer :transition-duration="600" src="/photos/garden.png" alt="Garden" />
</template>