定价

ImageViewer 使用手册

v1.4.1 · @snowui-design-system/components

许可阅读:首次阅读组件文档、源码或 npm 使用流程,先读完整 LICENSE;同任务同版本去重。公开 API 配置与内部实现修改分别按许可处理;全部组件使用统一运行包,源码权益以许可为准。

组件介绍

在页面级预览中查看图片或视频,提供前后切换与关闭操作,适合媒体详情浏览。

使用选择

用于图片的放大查看与组间导航。页面内普通图片展示用 Image;不要重写组件已有的缩放、关闭与键盘操作。

  • open / items / index 一起省略时使用页面级自动模式;手动模式显式提供预览列表与当前项。

  • children 替换全部默认控制按钮;只调整内置按钮外观时使用 parts,避免误删关闭与前后导航。

快速开始

以下示例展示推荐的基本组合。更多场景与组合限制见后文。

<script lang="ts">	import { Button, Image, ImageGroup } from '@snowui-design-system/components';	import { ImageViewer } from '@snowui-design-system/components';	import cover01 from '@snowui-design-system/resource-base/assets/backgrounds/gradient-01-1024.jpg';	import cover02 from '@snowui-design-system/resource-base/assets/backgrounds/gradient-02-1024.jpg';	import cover03 from '@snowui-design-system/resource-base/assets/backgrounds/gradient-03-1024.jpg';</script> <ImageGroup size={80}><Image src={cover01} alt="SnowUI resource cover 1" /><Image src={cover02} alt="SnowUI resource cover 2" /><Image src={cover03} alt="SnowUI resource cover 3" /></ImageGroup><ImageViewer />

API

关键字 类型 默认值 描述
open boolean — 手动模式下控制 Viewer 显示;与 items/index 一起省略时进入页面级自动模式
items ImageViewerItem[] — 手动模式下提供预览项列表;省略时自动消费页面级预览状态
index number — 手动模式下提供当前显示项索引;省略时自动消费页面级预览状态
dismissMode "button" | "backdrop" "backdrop" 关闭策略:backdrop 隐藏关闭按钮并允许点击媒体外空白关闭;button 显示关闭按钮且禁用空白关闭
backdrop BackdropVariant "opaque" 与 Dialog 共用遮罩视觉:opaque 为 10% 黑色遮罩,blur 在其上模糊背景,transparent 透明;不改变关闭策略。
children Snippet — 显式组合控制按钮;提供后替换全部默认内置按钮
parts ComponentPartsOf<{ Button: typeof Button }> — 批量配置默认内置的 prev / next / close Button

未重新声明的继承能力请查所属基础组件文档;本表只列本组件源码具名公开属性。

使用边界与详细说明

控制按钮双模式

  • 默认内置模式:省略 children 时自动生成三个真实 Button,分别使用 ArrowLineLeft、ArrowLineRight、SnowUI Close;默认 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)

关键字 类型 默认值 描述
parts.Button Button Props undefined 配置全部内置控制按钮;默认 md / gray / backgroundBlur
as 'prev' | 'next' | 'close' - 控制按钮角色

使用示例

<ImageViewer open={viewerOpen} items={items} index={currentIndex} />
 <ImageGroup size={80}>    <Image src="https://example.com/a.png" alt="A" />    <Image src="https://example.com/b.png" alt="B" />    <Image src="https://example.com/c.mp4" alt="C" /></ImageGroup> <ImageViewer />
 <Image    src="https://example.com/single.png"    alt="Single"    previewMode="viewer"/> <ImageViewer />
 <Image    src="https://example.com/single.png"    alt="Single"    previewMode="window"/> <ImageViewer />
 <Image    src="https://example.com/single.png"    alt="Single"    previewMode="none"/> <ImageViewer />
 <img    data-snowui-image-viewer-mode="viewer"    src="https://example.com/a.png"    alt="A"/> <ImageViewer />
 <img    data-snowui-image-viewer-mode="window"    src="https://example.com/a.png"    alt="A"/> <ImageViewer />
 <div data-snowui-image-viewer-scope>    <img        data-snowui-image-viewer-mode="viewer"        src="https://example.com/a.png"        alt="A"    />    <img        data-snowui-image-viewer-mode="viewer"        src="https://example.com/b.png"        alt="B"    />    <Image01        data-snowui-image-viewer-mode="viewer"        alt="C"    /></div> <ImageViewer />

智能行为

  1. 独立组件: 预览态与展示态拆分,避免 Image 承担弹层、导航和组索引状态。

  2. 全页面共享服务: 页面根部声明一个自动模式 ImageViewer;所有未显式指定 previewMode 的 Image / ImageGroup 自动共享它。

  3. 自动模式与 owner: open/items/index 全部省略时注册到页面服务;首个注册实例成为 owner,只有 owner 响应请求并渲染 Portal,owner 卸载后自动顺位接管。

  4. 手动模式: 当显式传入 open/items/index 时,ImageViewer 不再接管页面级点击,但仍会基于传入值建立本地预览状态,支持默认上一张 / 下一张 / 关闭动作。

  5. 默认内置控制: 省略 children 时自动生成真实 Button 控制键;prev / next 根据边界显隐;close 仅在 dismissMode="button" 时生成。

  6. 显式组合控制: 外部 Button as="prev" / "next" / "close" 若未显式传入自己的 onclick,会自动调用 Viewer 的上一张、下一张、关闭动作;自动模式与手动模式都适用。

  7. 资源兼容: items 同时支持字符串图片地址、字符串视频地址与 resource-svelte 图片组件。

  8. 视频预览: 视频项在 Viewer 中自动播放;实现上使用 autoplay + muted + playsinline + loop,保证预览层打开后立即可见。

  9. 单图预览: 当 Image 不在 ImageGroup 中时,自动模式 Viewer 会按单项列表打开当前点击的媒体。

  10. 组内切换: 当 Image 位于 ImageGroup 中时,自动模式 Viewer 会消费整组媒体列表,因此上一张/下一张可以工作。

  11. 组件接入链路: 页面存在共享 Viewer 时,未显式声明 previewMode 的 Image / ImageGroup 自动进入 Viewer;显式 viewer / window / none 始终优先。

  12. 原生图片接入: 自动模式下,原生 HTML <img data-snowui-image-viewer-mode="viewer" /> 也会被 ImageViewer 捕获并打开。

  13. 资源图片接入: resource-svelte 图片组件会把 data-snowui-image-viewer-mode="viewer" 透传到根 <img>,因此也能直接接入页面级 Viewer。

  14. DOM 分组: 当原生 <img> 或 resource-svelte 图片位于 [data-snowui-image-viewer-scope] 容器内时,Viewer 会按该作用域内 DOM 顺序建立上一张 / 下一张列表;没有 scope 时按单图打开。

  15. Window DOM 链路: 自动模式下,原生 <img data-snowui-image-viewer-mode="window" /> 会直接在新窗口打开,不进入 Viewer。

  16. 遮罩淡入: opaque / blur 从透明渐显,复用 Dialog backdrop preset;减少动效时立即显示。媒体本身、切换与关闭仍立即完成。

  17. 前后项轻量预取: 打开 Viewer 后,会对上一张和下一张字符串图片做轻量预取,降低切换等待感;视频不参与这条预取链。

  18. 预取去重: 同一轮打开周期内,相同字符串图片 URL 只预取一次,避免切换时反复创建加载器。

  19. 轻量事件总线: 自动模式不依赖全局 Svelte store,而是通过浏览器自定义事件 + Viewer 本地状态接收打开请求,降低运行时耦合与崩溃风险。

  20. DOM 点击适配: 自动模式还会监听浏览器点击事件,识别原生 <img> / resource-svelte 根 <img> 上的 data-snowui-image-viewer-mode 属性。

  21. 全页浮层: Viewer 现在使用固定定位覆盖整个页面,并通过更高的 z-index 浮在页面元素之上。

  22. 媒体最大化: 媒体壳层不再铺满灰色背景,而是改为透明容器;图片和视频在可用显示区域内按最大尺寸 contain 展示。

  23. 边距规则: 媒体最大显示区域与屏幕上下左右边缘保持 10% 距离;控制按钮与屏幕边缘保持 3% 距离。

  24. 比例规则: 媒体始终保持原始比例,并保证完整显示在可视区域内;当高度先触边时宽度自适应,当宽度先触边时高度自适应。

  25. 点击空白关闭: 仅 dismissMode="backdrop" 时,点击媒体以外的预览层区域关闭 ImageViewer。

  26. 键盘控制: 自动模式下,Esc 关闭 Viewer,A / W / ArrowLeft / ArrowUp 切换上一张,S / D / ArrowRight / ArrowDown 切换下一张。

  27. 边界停止: 当当前项是第一张时,不显示上一张按钮,上一张键盘操作也无效;当当前项是最后一张时,不显示下一张按钮,下一张键盘操作也无效。

  28. Portal 挂载: ImageViewer 直接挂载到 document.body,避免祖先层的 transform / filter / contain 干扰固定定位。

  29. 三层 Overlay 结构: Viewer 现在拆成独立 viewport、独立 media-hitbox 和独立 controls 三块,媒体布局、空白关闭、按钮点击互不抢事件。

  30. 10% 硬边距: viewport 直接使用 inset: 10vh 10vw,媒体只能在剩余的 80vw / 80vh 可视框内居中显示。

  31. 根层点击委托关闭: backdrop 模式由根层判断点击目标是否落在媒体或按钮内;不在两者内时关闭 Viewer。button 模式不处理空白点击。

  32. 按钮单独层: 控制按钮放在独立 controls 层,层本身 pointer-events: none,只有按钮自身恢复 pointer-events: auto。

  33. 关闭策略: 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 事件,并在收到事件后写入本地状态:

    • autoItems

    • autoIndex

    • autoOpen

  • 自动模式下,组件还会在捕获阶段监听 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. 数据解析

  • 当前预览项统一由以下派生值计算:

    • resolvedItems

    • resolvedOpen

    • resolvedIndex

  • 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 个角色样式入口:

    • prevStyle

    • nextStyle

    • closeStyle

  • 对外提供 3 个 Pin 方位入口:

    • prevAs → pin-left

    • nextAs → pin-right

    • closeAs → pin-bottom

  • 对外提供 3 个默认动作入口:

    • showPrev

    • showNext

    • close

  • 因此 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: auto

    • object-fit: contain

    • max-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" + SnowUI Close。

  • dismissMode="backdrop" 不创建内置 close Button;显式组合的 Button as="close" 也由 Context 隐藏。

  • 内置关闭按钮沿用 ImageViewer 的 Button 默认配置:md / gray / backgroundBlur。

1. 推荐用法:页面级自动模式

  • 一个页面里只放一个 ImageViewer

  • 不传 open/items/index

  • Image 通过 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":始终新窗口打开,不进入 Viewer

  • previewMode="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":接入页面级 ImageViewer

  • previewMode="window":新窗口打开

  • previewMode="none":不参与预览

<Image src="https://example.com/a.png" alt="A" previewMode="viewer" /><Image src="https://example.com/b.png" alt="B" previewMode="window" /><Image src="https://example.com/c.png" alt="C" previewMode="none" /><ImageViewer />

6.2 ImageGroup 组件

  • ImageGroup previewMode="viewer":组内 Image 默认接入 ImageViewer

  • ImageGroup previewMode="window":组内 Image 默认新窗口打开

  • 组内某个 Image 也可以单独覆写自己的 previewMode

<ImageGroup size={80} previewMode="viewer">    <Image src="https://example.com/a.png" alt="A" />    <Image src="https://example.com/b.png" alt="B" />    <Image src="https://example.com/c.mp4" alt="C" /></ImageGroup> <ImageViewer />

6.3 原生 HTML 与 resource-svelte

  • data-snowui-image-viewer-mode="viewer":接入 ImageViewer

  • data-snowui-image-viewer-mode="window":新窗口打开

  • data-snowui-image-viewer-scope:把作用域内媒体按 DOM 顺序组成一组

7. DOM 分组示例

没有 scope:按单图打开

<img    data-snowui-image-viewer-mode="viewer"    src="https://example.com/a.png"    alt="A"/> <img    data-snowui-image-viewer-mode="viewer"    src="https://example.com/b.png"    alt="B"/>
  • 点第一张,只打开第一张。

  • 点第二张,只打开第二张。

  • 两张图不会互相形成上一张 / 下一张。

有 scope:同一作用域内按 DOM 顺序分组

<div data-snowui-image-viewer-scope>    <img        data-snowui-image-viewer-mode="viewer"        src="https://example.com/a.png"        alt="A"    />    <img        data-snowui-image-viewer-mode="viewer"        src="https://example.com/b.png"        alt="B"    />    <Image01 data-snowui-image-viewer-mode="viewer" alt="C" /></div>
  • 点击第一张,会打开这 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。

变量 用途与默认参考
--snowui-backdrop-background-override 共享遮罩背景;默认取 backdrop 变体
--snowui-image-viewer-background-override 背景(默认值见组件源码)
--snowui-image-viewer-z-index-override z-index(默认值见组件源码)

设计指南 · Token · 组合边界

ImageViewer 类型与状态

必填属性:无具名必填属性。

可双向绑定:本组件未声明具名双向绑定属性;状态更新通过公开回调或所属宿主处理。

继承声明:FrameProps。继承能力以基类公开文档为准;Omit 中的字段已排除,不能按基类原样使用。

共享 Frame API · 返回 API

以下为公开属性实际引用的类型声明,来自源码;Svelte Snippet 表示内容片段,ComponentProps 表示对应组件的 Props。

ImageViewerItem

来源:components/src/components/ImageViewer.svelte

interface ImageViewerItem {        src?: string | Component;        alt?: string;    }

BackdropVariant

来源:components/src/lib/backdrop.ts

export type BackdropVariant = "opaque" | "blur" | "transparent";

ComponentPartsOf

来源:components/src/logic/component-parts.svelte.ts

export type ComponentPartsOf<    T extends Record<string, Component<any>>,> = {    [K in keyof T]?: ConfigurableComponentPartProps<T[K]>;};

ConfigurableComponentPartProps

来源:components/src/logic/component-parts.svelte.ts

export type ConfigurableComponentPartProps<C extends Component<any>> = Partial<    Omit<ComponentProps<C>, ComponentPartControlledKey>>;

ComponentPartControlledKey

来源:components/src/logic/component-parts.svelte.ts

export type ComponentPartControlledKey =    | "children"    | "as"    | "value"    | "defaultValue"    | "open"    | "selected"    | "checked"    | "disabled"    | "readonly"    | "id"    | `on${string}`    | `aria-${string}`;