ImageViewer 使用手册
v1.4.1 · @snowui-design-system/components
许可阅读:首次阅读组件文档、源码或 npm 使用流程,先读完整 LICENSE;同任务同版本去重。公开 API 配置与内部实现修改分别按许可处理;全部组件使用统一运行包,源码权益以许可为准。
组件介绍
在页面级预览中查看图片或视频,提供前后切换与关闭操作,适合媒体详情浏览。
使用选择
用于图片的放大查看与组间导航。页面内普通图片展示用 Image;不要重写组件已有的缩放、关闭与键盘操作。
open / items / index 一起省略时使用页面级自动模式;手动模式显式提供预览列表与当前项。
children 替换全部默认控制按钮;只调整内置按钮外观时使用 parts,避免误删关闭与前后导航。
快速开始
以下示例展示推荐的基本组合。更多场景与组合限制见后文。
API
未重新声明的继承能力请查所属基础组件文档;本表只列本组件源码具名公开属性。
使用边界与详细说明
控制按钮双模式
默认内置模式:省略 children 时自动生成三个真实
Button,分别使用ArrowLineLeft、ArrowLineRight、SnowUIClose;默认size="md"、variant="gray"、backgroundBlur={true}。parts 批量配置:
parts.Button配置全部内置控制按钮的可配置 Props;as、ARIA、点击动作和图标由 ImageViewer 接管。显式组合模式:传入 children 后不生成内置按钮;调用方使用
Button as="prev|next|close"并直接配置 Button Props。
当前主路径是:在页面根布局声明一个自动模式 ImageViewer 作为共享服务。页面中的 Image / ImageGroup 未显式设置 previewMode 时会自动接入;若误声明多个自动 Viewer,仅注册顺序中的第一个 owner 响应和渲染。
布局模型
主布局模型:复合管理布局。
ImageViewer 的区域结构为 portal overlay / viewport / media stage / media visual / controls。
媒体子项由 Image 或原生媒体请求提供;默认控制键由 ImageViewer 内置,显式组合时使用 Button / Icon 并通过
as="prev" / "next" / "close"声明角色。Viewer 只管理预览层、焦点恢复、键盘切换和媒体显示,不把内部控制区抽象为三槽位宿主。
ImageGroup 负责集合注册,ImageViewer 负责打开后的单一预览层。
关键字 (Keyword)
使用示例
智能行为
独立组件: 预览态与展示态拆分,避免
Image承担弹层、导航和组索引状态。全页面共享服务: 页面根部声明一个自动模式 ImageViewer;所有未显式指定 previewMode 的 Image / ImageGroup 自动共享它。
自动模式与 owner:
open/items/index全部省略时注册到页面服务;首个注册实例成为 owner,只有 owner 响应请求并渲染 Portal,owner 卸载后自动顺位接管。手动模式: 当显式传入
open/items/index时,ImageViewer不再接管页面级点击,但仍会基于传入值建立本地预览状态,支持默认上一张 / 下一张 / 关闭动作。默认内置控制: 省略 children 时自动生成真实 Button 控制键;prev / next 根据边界显隐;close 仅在
dismissMode="button"时生成。显式组合控制: 外部
Button as="prev" / "next" / "close"若未显式传入自己的onclick,会自动调用 Viewer 的上一张、下一张、关闭动作;自动模式与手动模式都适用。资源兼容:
items同时支持字符串图片地址、字符串视频地址与resource-svelte图片组件。视频预览: 视频项在 Viewer 中自动播放;实现上使用
autoplay + muted + playsinline + loop,保证预览层打开后立即可见。单图预览: 当
Image不在ImageGroup中时,自动模式 Viewer 会按单项列表打开当前点击的媒体。组内切换: 当
Image位于ImageGroup中时,自动模式 Viewer 会消费整组媒体列表,因此上一张/下一张可以工作。组件接入链路: 页面存在共享 Viewer 时,未显式声明 previewMode 的 Image / ImageGroup 自动进入 Viewer;显式
viewer / window / none始终优先。原生图片接入: 自动模式下,原生 HTML
<img data-snowui-image-viewer-mode="viewer" />也会被ImageViewer捕获并打开。资源图片接入:
resource-svelte图片组件会把data-snowui-image-viewer-mode="viewer"透传到根<img>,因此也能直接接入页面级 Viewer。DOM 分组: 当原生
<img>或resource-svelte图片位于[data-snowui-image-viewer-scope]容器内时,Viewer 会按该作用域内 DOM 顺序建立上一张 / 下一张列表;没有 scope 时按单图打开。Window DOM 链路: 自动模式下,原生
<img data-snowui-image-viewer-mode="window" />会直接在新窗口打开,不进入 Viewer。遮罩淡入: opaque / blur 从透明渐显,复用 Dialog backdrop preset;减少动效时立即显示。媒体本身、切换与关闭仍立即完成。
前后项轻量预取: 打开 Viewer 后,会对上一张和下一张字符串图片做轻量预取,降低切换等待感;视频不参与这条预取链。
预取去重: 同一轮打开周期内,相同字符串图片 URL 只预取一次,避免切换时反复创建加载器。
轻量事件总线: 自动模式不依赖全局 Svelte store,而是通过浏览器自定义事件 + Viewer 本地状态接收打开请求,降低运行时耦合与崩溃风险。
DOM 点击适配: 自动模式还会监听浏览器点击事件,识别原生
<img>/resource-svelte根<img>上的data-snowui-image-viewer-mode属性。全页浮层: Viewer 现在使用固定定位覆盖整个页面,并通过更高的
z-index浮在页面元素之上。媒体最大化: 媒体壳层不再铺满灰色背景,而是改为透明容器;图片和视频在可用显示区域内按最大尺寸
contain展示。边距规则: 媒体最大显示区域与屏幕上下左右边缘保持
10%距离;控制按钮与屏幕边缘保持3%距离。比例规则: 媒体始终保持原始比例,并保证完整显示在可视区域内;当高度先触边时宽度自适应,当宽度先触边时高度自适应。
点击空白关闭: 仅
dismissMode="backdrop"时,点击媒体以外的预览层区域关闭ImageViewer。键盘控制: 自动模式下,
Esc关闭 Viewer,A / W / ArrowLeft / ArrowUp切换上一张,S / D / ArrowRight / ArrowDown切换下一张。边界停止: 当当前项是第一张时,不显示上一张按钮,上一张键盘操作也无效;当当前项是最后一张时,不显示下一张按钮,下一张键盘操作也无效。
Portal 挂载:
ImageViewer直接挂载到document.body,避免祖先层的transform / filter / contain干扰固定定位。三层 Overlay 结构: Viewer 现在拆成独立
viewport、独立media-hitbox和独立controls三块,媒体布局、空白关闭、按钮点击互不抢事件。10% 硬边距:
viewport直接使用inset: 10vh 10vw,媒体只能在剩余的80vw / 80vh可视框内居中显示。根层点击委托关闭: backdrop 模式由根层判断点击目标是否落在媒体或按钮内;不在两者内时关闭 Viewer。button 模式不处理空白点击。
按钮单独层: 控制按钮放在独立
controls层,层本身pointer-events: none,只有按钮自身恢复pointer-events: auto。关闭策略:
dismissMode="button"显示内置/显式 close Button 并禁止点击空白关闭;dismissMode="backdrop"隐藏 close Button,并允许点击媒体与控制按钮之外的空白区域关闭。Escape 键仍作为自动模式的无障碍退出入口。
1. 运行模式
ImageViewer有两种运行模式:自动模式:
open/items/index全部省略,由页面级事件总线驱动。手动模式:显式传入
open/items/index,不接管页面级事件,但内部仍会镜像这些值用于默认导航与关闭。
自动模式下,组件会调用 image-preview.ts(源码参考:
components/src/logic/image-preview.ts) 中的registerImageViewer()注册当前页面存在可用 Viewer。自动模式下,组件会监听
snowui:image-preview:open事件,并在收到事件后写入本地状态:autoItemsautoIndexautoOpen
自动模式下,组件还会在捕获阶段监听
window.click:命中
.snowui-image时跳过,由Image自己处理命中原生
<img data-snowui-image-viewer-mode="viewer">或resource-svelte根<img data-snowui-image-viewer-mode="viewer">时,直接构建 DOM 预览请求命中
<img data-snowui-image-viewer-mode="window">时,直接在新窗口打开字符串图片
2. 数据解析
当前预览项统一由以下派生值计算:
resolvedItemsresolvedOpenresolvedIndex
clampedIndex会把索引约束在合法范围内,避免上一张/下一张时越界。currentItem是当前真正渲染的媒体项。媒体类型通过字符串后缀判断:
图片:普通字符串 URL 或
resource-svelte组件视频:
mp4 / webm / ogg / mov / m4v
DOM 图片适配时:
作用域优先读取最近的
[data-snowui-image-viewer-scope]scope 内所有
img[data-snowui-image-viewer-mode="viewer"]会按 DOM 顺序组成items没有 scope 时按单图打开
3. 交互逻辑
showPrev()/showNext():自动模式与手动模式共用
要求
itemCount > 1基于当前
clampedIndex做边界停止切换第一张不再向前循环到最后一张,最后一张也不再向后循环到第一张
close():自动模式下会先调用 image-preview.ts(源码参考:
components/src/logic/image-preview.ts) 的markImageViewerClosed(),再将autoOpen = false手动模式下仅关闭内部镜像的
manualOpen
根层
onclick统一交给handleRootClick():如果命中真实媒体节点(
img / video / .snowui-image-viewer-media),不关闭如果命中控制按钮层
controlsEl,不关闭其余区域点击统一关闭
键盘控制仅自动模式生效:
Escape->close()A / W / ArrowLeft / ArrowUp->showPrev()S / D / ArrowRight / ArrowDown->showNext()
4. 预取逻辑
当 Viewer 打开且媒体数量大于 1 时,会对上一项和下一项做轻量预取。
预取只针对字符串图片 URL:
视频不参与预取
resource-svelte组件不参与预取
prefetchedUrls用于同一轮打开周期内去重,避免重复创建window.Image()。
5. 宿主上下文
组件会通过
IMAGE_VIEWER_CONTEXT_KEY向子组件暴露控制协议。对外提供 3 个角色样式入口:
prevStylenextStylecloseStyle
对外提供 3 个 Pin 方位入口:
prevAs→pin-leftnextAs→pin-rightcloseAs→pin-bottom
对外提供 3 个默认动作入口:
showPrevshowNextclose
因此 Button.svelte(源码参考:
components/src/components/Button.svelte) / Icon.svelte(源码参考:components/src/components/Icon.svelte) 可以通过as="prev" / "next" / "close"自动接入 Viewer。
6. Portal 与根节点
Viewer 根层仍然使用 Frame.svelte(源码参考:
components/src/components/Frame.svelte) 承接:普通 Frame
enableResponsive / enableContext / enableScaleObserver显式透传
浮层并不在原始页面树中就地渲染,而是通过
use={[portalToBody]}挂到document.body。同时保留
bind:element,让 portal host 仍然维持在Frame体系内,避免脱离现有 portal 组件约束。
1. 根层
根类名:
.snowui-image-viewer职责:
固定全屏浮层
设置
z-index提供排版基准
font-size / line-height声明内部变量:
--snowui-image-viewer-viewport-padding--snowui-image-viewer-control-margin--snowui-image-viewer-background
2. 视口层
.snowui-image-viewer-viewport采用
position: fixed通过
inset: var(--snowui-image-viewer-viewport-padding)提供四边10%安全边距pointer-events: none只负责提供媒体可用显示区域,不负责接收点击
3. 媒体层
.snowui-image-viewer-media-shell占满可用视口
负责媒体居中
不接收指针事件
.snowui-image-viewer-media-hitbox与媒体布局同层
当前只作为媒体命中判断的包裹层
不主动接收指针事件
.snowui-image-viewer-media才是实际媒体节点
pointer-events: autoobject-fit: containmax-width / max-height: 100%因此图片和视频都会在
10%安全边距内按原始比例尽可能放大
4. 控制层
.snowui-image-viewer-controls全屏覆盖
pointer-events: none只作为角色按钮的定位容器
角色按钮代理选择器:
[class*="-role-prev"][class*="-role-next"][class*="-role-close"]
这些按钮会恢复
pointer-events: auto定位规则:
prev: 左侧垂直居中next: 右侧垂直居中close: 底部水平居中
三者都使用
--snowui-image-viewer-control-margin,对应屏幕边缘3%
5. 默认关闭按钮
未传入自定义 children 且
dismissMode="button"时,渲染内置Button as="close"+ SnowUIClose。dismissMode="backdrop"不创建内置 close Button;显式组合的Button as="close"也由 Context 隐藏。内置关闭按钮沿用 ImageViewer 的 Button 默认配置:md / gray / backgroundBlur。
1. 推荐用法:页面级自动模式
一个页面里只放一个
ImageViewer不传
open/items/indexImage通过previewMode="viewer"接入它原生 HTML
<img data-snowui-image-viewer-mode="viewer" />与resource-svelte图片也可直接接入ImageGroup负责提供组列表,ImageViewer负责显示
这是当前对话里最终确定的主用法。
2. 手动受控模式
当页面不想使用全局自动接管时,可以显式传
open/items/index这时
ImageViewer只是普通的受控预览组件适合 demo、特殊业务态或外部已有状态机的页面
3. 和 Image 的关系
Image上的src定义媒体来源ImageViewer不要求把媒体链接再写一遍到组件内部自动模式下,只有
previewMode="viewer"的Image才会把点击请求送入 Viewer如果该
Image不在组内,则按单图打开previewMode="window":始终新窗口打开,不进入 ViewerpreviewMode="none":完全不参与预览
4. 和 ImageGroup 的关系
ImageGroup统一控制组内Image尺寸与布局同时维护组内媒体列表快照
点击组内
Image后,ImageViewer会收到整组媒体列表和当前索引因此
prev / next只在组场景下真正有完整意义
5. 回退行为
Image组件是否进入 Viewer,由它自己的previewMode决定。原生 HTML
<img>和resource-svelte图片是否进入 Viewer,由data-snowui-image-viewer-mode决定。previewMode="window":即使页面里存在 Viewer,也强制改走新窗口。previewMode="none":当前图片完全不参与预览。data-snowui-image-viewer-mode="window":原生 HTML /resource-svelte图片直接新窗口打开。
6.1 Image 组件
previewMode="viewer":接入页面级ImageViewerpreviewMode="window":新窗口打开previewMode="none":不参与预览
6.2 ImageGroup 组件
ImageGroup previewMode="viewer":组内Image默认接入ImageViewerImageGroup previewMode="window":组内Image默认新窗口打开组内某个
Image也可以单独覆写自己的previewMode
6.3 原生 HTML 与 resource-svelte
data-snowui-image-viewer-mode="viewer":接入ImageViewerdata-snowui-image-viewer-mode="window":新窗口打开data-snowui-image-viewer-scope:把作用域内媒体按 DOM 顺序组成一组
7. DOM 分组示例
没有 scope:按单图打开
点第一张,只打开第一张。
点第二张,只打开第二张。
两张图不会互相形成上一张 / 下一张。
有 scope:同一作用域内按 DOM 顺序分组
点击第一张,会打开这 3 张里的第 1 张。
点击第二张,会打开这 3 张里的第 2 张。
点击
Image01,会打开这 3 张里的第 3 张。上一张 / 下一张只在这个
scope内切换,不会跳到页面其他图片。
8. 视频定义
ImageViewer支持图片和视频混合预览视频在 Viewer 中默认自动播放
缩略图阶段的视频行为不由
ImageViewer负责,而由Image负责 hover 播放
ImageViewer 样式覆盖
先阅读 完整 LICENSE;同任务、同版本、同范围去重。
优先用组件公开 Props 匹配合适样式,尽可能保留原有视觉。确需改写时按 样式选择与改写顺序 使用自己的 class(含项目已有 Tailwind)或实例 style。以下名称来自源码实际消费的公开 override;先核对目标作用域,不修改私有 DOM。采用 SnowUI Token 时选择正确类别,外部应用不被要求安装一方 utilities。
ImageViewer 类型与状态
必填属性:无具名必填属性。
可双向绑定:本组件未声明具名双向绑定属性;状态更新通过公开回调或所属宿主处理。
继承声明:FrameProps。继承能力以基类公开文档为准;Omit 中的字段已排除,不能按基类原样使用。
以下为公开属性实际引用的类型声明,来自源码;Svelte Snippet 表示内容片段,ComponentProps 表示对应组件的 Props。
ImageViewerItem
来源:components/src/components/ImageViewer.svelte
BackdropVariant
来源:components/src/lib/backdrop.ts
ComponentPartsOf
来源:components/src/logic/component-parts.svelte.ts
ConfigurableComponentPartProps
来源:components/src/logic/component-parts.svelte.ts
ComponentPartControlledKey
来源:components/src/logic/component-parts.svelte.ts