Skip to content

Previewer

浮窗式图片预览,遮罩、抢焦点、阻塞背景交互——形态类似 picture-in-picture。支持滚轮缩放、平移、多图切换、缩略图栏。

全局单例:再次 preview.show 自动关闭上一个浮窗(触发其 onClose),同一时间只能存在一个预览实例。

演示页已挂载 <Previewer />

本页脚本顶部已渲染 <Previewer />,点击下方按钮即可打开浮窗。自己接入时必须在应用 root 显式挂一次

Anatomy

  • Previewer浮窗容器,在应用 root 挂一次。订阅全局单例 state 渲染当前活动预览
  • preview命令式 helper,show/close/只读响应式 state

单图预览

preview.show(单值)

多图预览

数组 + 默认 index

多图时浮窗显示左右翻页按钮 + 底部缩略图栏。

通过点击项定位

替代手写 arr.indexOf(item):target 选项让组件在数组里查 index。

preview.show(images, { target })

点击下方按钮,浮窗将打开第 3 张图。

持有 close 句柄 + onClose

show 返回 close 闭包;旧 close 在新 show 触发后自动 noop。

保留 close 句柄

点击「打开」后,可通过「程序化关闭」主动关闭。

联动响应式 state

preview.isOpen / currentIndex / currentImageReadonly<Ref<...>>模板里使用前先在 setup 顶层赋值,Vue 3 的自动解包仅对 setup 顶层 ref 生效。

读取 isOpen / currentIndex

先打开图组,再观察下方实时变化。

isOpen: false
currentIndex: -1

不要写 v-if="preview.isOpen"

对象属性访问绕过 Vue 模板的 ref 自动解包,会拿到 ref 对象本身(永远 truthy)。务必先 const isOpen = preview.isOpen 再用。

路由切换自动关闭

Previewer 不绑定路由,业务侧自行 watch:

vue
<script setup lang="ts">
import { preview } from '@tripo3d/design';

const route = useRoute();
watch(() => route.fullPath, () => preview.close());
</script>

交互行为

动作结果
拖动标题栏移动浮窗(按钮、图片区由 .cancel-drag class 排除,不参与窗口拖动)
滚轮阶跃缩放 ±0.25,范围 [0.5, 2]
右下角 +/-同滚轮,按钮触发
拖图缩放 > 1 时启用,自动 clamp 不出 viewport
左右翻页多图时显示,自动重置缩放/位移
缩略图多图时底部显示,点击切换;当前项自动 scrollIntoView 居中
关闭按钮触发 onClose,关闭浮窗

不响应:ESC 全局键盘、点击外部、route/store 联动。需要这些联动请在应用层自行 watch + preview.close()

API

<Previewer /> Props

名称 类型 默认值 说明
default-position'center' | { x?: number; y?: number }'center'浮窗每次打开的初始位置;x/y 缺失轴自动居中。preview.show 传 initialPosition 可覆盖
widthnumber448浮窗宽度(px,含 padding)
image-sizenumber424主图区正方形边长(px)
classstring透传到外层容器

preview 命令式 API

方法签名说明
preview.show(src, options?) => () => void打开浮窗,返回 close 闭包;再次 show 自动关闭上一个并触发其 onClose
preview.close() => void关闭当前活动浮窗(不持有 close 句柄时使用)

preview.show 选项

名称 类型 默认值 说明
initialIndexnumber0默认打开的图片索引,越界 / 负数自动 clamp
targetstring | File替代手动 indexOf:传任一图,组件在数组里查 index
onClose() => void关闭时触发一次(含被新 show 替代的场景)
initialPosition{ x?: number; y?: number }本次打开的初始位置,缺值用容器 default-position

preview 只读响应式

字段类型说明
preview.isOpenReadonly<Ref<boolean>>当前是否有活动浮窗
preview.currentIndexReadonly<Ref<number>>当前展示的图片索引,未打开时 -1
preview.currentImageReadonly<Ref<string | null>>当前图片 URL(File 已 resolve 为 ObjectURL)

单例语义

ts
const close1 = preview.show(a, { onClose: () => console.log('a closed') });
const close2 = preview.show(b);  // 输出 'a closed',浮窗内容变 b
close1();                         // noop(已被 close2 替代)
close2();                         // 真正关闭 b

close 闭包内部带 token 校验,被新 show 替代后调用变 noop——不会误关后续打开的浮窗。

SSR

show / close 在 SSR 环境(globalThis.window === undefined)下是 noop:show 返回空 close,close 直接返回。<Previewer /><Teleport to="body">,仅在客户端 hydrate 后挂载,不影响 SSR 渲染。

基于 MIT 协议发布(内部使用)