1.9.0
-
自定义 Portal 挂载点: 新增
portalTarget,可以把查看器 Portal 挂到宿主应用的 overlay root、modal root、shadow host 或微前端容器。它只改变 DOM 父节点,查看器仍保持 fixed 全屏布局;需要调整层级时继续使用zIndex。import { useState } from 'react' import Zmage from 'react-zmage' import 'react-zmage/style.css' export function GalleryImage() { const [viewerRoot, setViewerRoot] = useState<HTMLElement | null>(null) return ( <> <div id="app-overlay-root" ref={setViewerRoot} /> <Zmage src="/photo.jpg" alt="Photo" portalTarget={viewerRoot} /> </> ) }
- 公开集成面同步: README、中文 README、AGENTS、
docs/llms.txt、官网参数表、playground 示例和 llms-eval 已同步portalTarget的使用场景和边界说明。
-
Custom Portal target: Added
portalTargetso the viewer Portal can mount into a host overlay root, modal root, shadow host, or micro-frontend container. It changes only the DOM parent; the viewer still uses fullscreen fixed positioning. UsezIndexfor stacking.import { useState } from 'react' import Zmage from 'react-zmage' import 'react-zmage/style.css' export function GalleryImage() { const [viewerRoot, setViewerRoot] = useState<HTMLElement | null>(null) return ( <> <div id="app-overlay-root" ref={setViewerRoot} /> <Zmage src="/photo.jpg" alt="Photo" portalTarget={viewerRoot} /> </> ) }
- Public integration surface sync: README, Chinese README, AGENTS,
docs/llms.txt, home prop tables, playground examples, and llms-eval now document the scenario and boundary forportalTarget.
1.8.4
- 修复进入浏览态末尾的图片闪烁: 首次 browse-in 期间不再立即挂载和加载相邻 side images,而是等浏览层进入稳态后再开始预加载。这个问题不是单张图片资源本身导致的;在大图、多图页面里,中心图还在执行 cover 几何动画(
object-fit、clip、radius、transform)时,相邻图的解码、布局和合成也同时加入,容易在进入动画最后几帧或窗口 resize 时放大为 Chromium repaint/composite 抖动,表现为短促闪烁。现在 flip 按钮会在相邻图加载完成前保持禁用态,图片 ready 后再恢复可用,保留翻页功能的同时降低首次打开的渲染压力。 - edge 几何计算和实际展示保持一致: 打开、旋转、缩放边界和
canZoom现在共用同一套 viewport-fit 计算,避免特定图片尺寸或屏幕分辨率下配置的edge与实际留白不一致。 - desktop 默认 edge 调整为 16px: desktop preset 的
edge默认值从30调整为16,调试台默认值、README、AGENTS、llms.txt和官网构建产物已同步。 - 图片 abort 处理不再触发 JSX lint 规则: viewer 内部仍保留图片加载中断后的终止处理,但改为通过 ref 注册原生事件,避免在
<img>上透传onAbort。
- Fix late browsing-in image flicker: Adjacent side images are no longer mounted and loaded immediately during the first browse-in transition. They now start preloading only after the browsing layer has settled. The issue was not caused by one image asset alone: on pages with large images or multiple gallery images, side-image decoding, layout, and compositor work could overlap with the center image's cover-geometry animation (
object-fit, clip, radius, and transform). That extra work was most visible in the last frames of browsing-in or during viewport resize, where Chromium could repaint or composite briefly and appear to flicker. Flip controls now stay disabled until the required adjacent image has loaded, preserving flip behavior while reducing first-open rendering pressure. - Keep edge geometry aligned with what is rendered: Opening, rotation, zoom bounds, and
canZoomnow share the same viewport-fit calculation, avoiding mismatches between configurededgeand the visible margin on certain image sizes or screen resolutions. - Change the desktop edge default to 16px: The desktop preset now defaults
edgeto16instead of30, with the playground defaults, README, AGENTS,llms.txt, and rebuilt website bundle kept in sync. - Avoid JSX lint failures for image abort handling: The viewer still handles aborted image loads, but attaches native abort listeners through refs instead of passing
onAbortto<img>.
1.8.3
- AI 集成指引更清楚: README、
llms.txt和官网 AI 文档补充最小接入、样式导入、SSR 子路径与 Wrapper 模式约束,减少生成集成代码时漏掉关键步骤的概率。
- 侧边翻页按钮脱离边缘后的形态更正确: 当
controller.layout给 flip 控件设置 inset 时,左右翻页按钮现在会变成完整圆角与对称 padding,不再保留贴边半圆样式。 - 脱离边缘的翻页按钮保持居中: 修正 detached flip 按钮在 show / hover / active 状态下的 transform,使按钮向内偏移后仍保持垂直居中。
- Clearer AI integration guidance: README,
llms.txt, and the AI docs now spell out minimal setup, style imports, SSR subpath usage, and Wrapper-mode constraints so generated integrations are less likely to miss required steps.
- Correct detached side-flip button shape: When
controller.layoutadds inset spacing for flip controls, the left and right flip buttons now use full rounding and symmetric padding instead of keeping the edge-attached pill shape. - Detached flip buttons stay centered: Fixed the show / hover / active transforms for detached flip controls so inset side buttons remain vertically centered.
1.8.2
-
新增 blur 翻页效果:
animate.flip现在支持'blur'。多图浏览切换时,当前图会轻微失焦并淡出,下一张图同步淡入;caption 也会跟随同一段切换节奏更新。<Zmage src="cover.jpg" set={gallery} animate={{ flip: 'blur' }} />
- 桌面浏览层默认间距更稳: 默认
radius调整为8,桌面edge调整为30,pagination 和 caption 默认向内收,减少靠边图片、页码和说明文字互相挤压的问题。 - 控制器对比度更一致: toolbar 的
controller.color/controller.backdrop现在会同步影响侧边翻页按钮和 pagination,让深色或自定义背景下的控制区更容易看清。
-
New blur flip effect:
animate.flipnow supports'blur'. During multi-image browsing, the outgoing image softly blurs and fades while the next image fades in; captions switch on the same transition rhythm.<Zmage src="cover.jpg" set={gallery} animate={{ flip: 'blur' }} />
- Safer desktop viewer spacing by default: The default
radiusis now8, desktopedgeis now30, and pagination / caption are inset by default to reduce crowding around edge-aligned images and overlay text. - More consistent controller contrast:
controller.color/controller.backdropnow propagate from the toolbar to side flip buttons and pagination, making custom dark or tinted controls easier to read.
1.8.1
-
controller.layout inset 更完整:
controller.layout的数字型inset现在会按目标组件自己的位置解释,不再一律当作底部偏移。toolbar 会跟随controller.placement判断安全边距,pagination 和 caption 继续按底部区域处理,左右 flip 按钮也可以单独向内收。<Zmage src="hero.jpg" controller={{ layout: { toolbar: { inset: 24 }, flip: { inset: 32 }, pagination: { inset: 20 }, caption: { inset: 64 }, }, }} />
-
文档与调试台同步: 参数调试台、README、
docs/llms.txt和公开类型说明已经同步到新的 layout 行为,方便直接调试 toolbar、flip、pagination 和 caption 的安全区。
-
More complete
controller.layoutinsets: Numericcontroller.layoutinsetvalues now follow each overlay target's natural edge instead of always mapping to the bottom edge. The toolbar followscontroller.placement, pagination and caption keep their bottom-area behavior, and left / right flip buttons can now be inset from the side edges.<Zmage src="hero.jpg" controller={{ layout: { toolbar: { inset: 24 }, flip: { inset: 32 }, pagination: { inset: 20 }, caption: { inset: 64 }, }, }} />
-
Docs and playground sync: The playground controls, README,
docs/llms.txt, and public type docs now describe the updated layout behavior for toolbar, flip, pagination, and caption safe-area tuning.
1.8.0
-
慢动作转场: 新增
animate.slowMotion,默认关闭。开启后,按住 Shift 打开或关闭图片会把完整 browsing 转场放慢到 10x,便于演示和检查动画细节。<Zmage src="photo.jpg" animate={{ slowMotion: true }} />
-
覆盖层布局: 新增
controller.layout和对应导出类型,可单独调整 toolbar、pagination、caption 的安全偏移,不改变图片首帧、缩放或关闭动画的几何计算。<Zmage src="photo.jpg" controller={{ placement: 'bottom-center', layout: { pagination: { inset: 32 }, caption: { inset: { bottom: 72 } }, }, }} />
- 首页示例更新: 官网首页示例改成更复杂的图文混排,用 Child's Dream 图片故事展示原位展开、封面裁剪、圆角匹配、caption、分页和慢动作转场。
- npm 包体积: 发布包只保留 dist 产物和 SSR stub 路径,继续保留公开子路径兼容,同时减少安装包内容。
- 快速开关浏览层: 隔离
Zmage.browsing()实例,取消旧 open / close timer,快速连续 in / out / reopen 时不再把封面卡在visibility:hidden或留下空 portal。 - 首帧和末帧稳定性: browsing-in 等待视口几何稳定,并让 RAF 转场使用同一个视觉写入源,修复封面首帧偏移、圆角不一致和转场末段闪动。
-
Slow-motion transitions: Added
animate.slowMotion, off by default. When enabled, holding Shift while opening or closing slows the full browsing transition to 10x for demos and animation inspection.<Zmage src="photo.jpg" animate={{ slowMotion: true }} />
-
Overlay layout: Added
controller.layoutand exported layout types so consumers can inset the toolbar, pagination, and caption overlays without changing first-frame, zoom, or close-animation geometry.<Zmage src="photo.jpg" controller={{ placement: 'bottom-center', layout: { pagination: { inset: 32 }, caption: { inset: { bottom: 72 } }, }, }} />
- Homepage examples: Rebuilt the homepage demo into a richer editorial layout using the Child's Dream image story to show origin expansion, cover crop matching, radius matching, captions, pagination, and slow-motion transitions.
- npm package size: The published package now ships only dist assets and the SSR stub path while preserving public subpath compatibility.
- Rapid viewer toggles: Isolated
Zmage.browsing()instances and canceled stale open / close timers, preventing rapid in / out / reopen sequences from leaving the cover hidden or an empty portal mounted. - First-frame and late-transition stability: browsing-in now waits for stable viewport geometry and uses a single visual write source during RAF transitions, fixing first-frame cover offsets, radius mismatch, and late-transition flashing.
1.7.0
-
移动端缩放手势:
preset="mobile"和默认的preset="auto"现在支持双指 pinch zoom、单指双击 zoom、缩放后的单指拖曳平移,以及接近边缘时带阻尼的回弹手感。<Zmage src="photo.jpg" gesture={{ pinchZoom: { resetBelowFit: true }, doubleTapZoom: { scale: 2.5 }, }} />
-
移动端拖曳切换与退出: mobile preset 支持横向拖曳切换图片、纵向拖曳退出浏览层,并和现有 browsing close 动画保持一致。
-
控制器自定义:
controller.placement可以移动工具栏位置,controller.render({ state, actions, slots })可以完全接管控制器 UI,同时复用内置按钮 slot。<Zmage src="photo.jpg" controller={{ placement: 'bottom-center', render: ({ state, actions, slots }) => ( <div> {slots.zoom} <span>{state.page + 1} / {state.total}</span> <button onClick={actions.close}>Close</button> </div> ), }} />
-
封面几何匹配:
animate.cover现在会在打开 / 关闭时匹配 cover<img>的object-fit、object-position、clip 和 border radius,减少封面被裁剪或圆角不一致时的首帧跳变。
- 默认 preset 改为 auto: 省略
preset时会根据(pointer: coarse) and (hover: none)自动选择 mobile 或 desktop 默认值。需要强制旧桌面行为时可以显式传preset="desktop"。 - 滚轮缩放手感: desktop zoom 状态下支持平滑 wheel / trackpad 缩放,可配置滚轮方向反转,并在缩小退出 zoom 后用默认
1000ms保护时间吸收残留滚轮事件。
-
Mobile zoom gestures:
preset="mobile"and the defaultpreset="auto"now support pinch zoom, double-tap zoom, one-finger panning while zoomed, and damped edge resistance.<Zmage src="photo.jpg" gesture={{ pinchZoom: { resetBelowFit: true }, doubleTapZoom: { scale: 2.5 }, }} />
-
Mobile drag paging and drag-to-exit: the mobile preset supports horizontal drag paging and vertical drag-to-exit, using the same browsing close animation path as the rest of the viewer.
-
Controller customization:
controller.placementmoves the toolbar, andcontroller.render({ state, actions, slots })lets consumers replace the whole controller UI while reusing built-in button slots.<Zmage src="photo.jpg" controller={{ placement: 'bottom-center', render: ({ state, actions, slots }) => ( <div> {slots.zoom} <span>{state.page + 1} / {state.total}</span> <button onClick={actions.close}>Close</button> </div> ), }} />
-
Cover geometry matching:
animate.covernow matches the cover<img>object-fit,object-position, clip, and border radius during open / close transitions, reducing first-frame jumps for cropped or rounded covers.
- Default preset is now auto: omitting
presetnow resolves mobile or desktop defaults via(pointer: coarse) and (hover: none). Passpreset="desktop"to force the previous desktop behavior on touch devices. - Wheel zoom feel: desktop zoom mode supports smooth wheel / trackpad zoom, configurable direction reversal, and a default
1000msguard that absorbs residual wheel events after zooming back out.
1.6.0
- Wrapper 共享图库:
<Zmage.Wrapper>现在可以接收显式set。点击包裹区内的子级<img>时, Wrapper 会用该图片的src匹配set[i].src, 并直接打开匹配页;defaultPage只在没有匹配项时作为兜底。 - Wrapper caption 读取: 不传
set时, 包裹区内图片可以通过data-zmage-caption或最近的figcaption向查看器提供 caption。富文本、Markdown、CMS HTML 不需要改写成<Zmage>组件也能保留图片说明。
- 包裹器模式的参数调试台示例已修正:
src/alt/caption不再错误地写在<Zmage.Wrapper>顶层, 而是来自真实子级<img>。 - 包裹器模式展示已扩展为复杂图文混排稿件, 覆盖 hero 图、浮动图、侧栏图组、重复裁切、caption 和正文混排。
- README、README.zh-CN、AGENTS、llms.txt、官网 Props / Three Modes 文档和 7 份 i18n 文案已同步说明 Wrapper 模式下哪些参数生效, 哪些参数属于组件模式。
- npm:
react-zmage@1.6.0 - GitHub tag:
v1.6.0 - 无破坏变更。
pnpm testpnpm -w run checkpnpm --filter llms-eval run testnpm publish --dry-run
- Wrapper shared gallery:
<Zmage.Wrapper>now accepts an explicitset. When a descendant<img>is clicked, Wrapper matches the imagesrcagainstset[i].srcand opens that page;defaultPageremains the fallback when no match is found. - Wrapper caption discovery: without
set, wrapped images can provide viewer captions throughdata-zmage-captionor the nearestfigcaption. Rich text, Markdown, and CMS HTML can keep their original image markup while still feeding captions to the viewer.
- The Wrapper playground snippet now matches real usage:
src/alt/captionlive on descendant<img>nodes, not on<Zmage.Wrapper>. - The Wrapper playground preview now uses a complex editorial layout with hero imagery, floated figures, side figures, repeated crops, captions, and prose.
- README, README.zh-CN, AGENTS, llms.txt, home Props / Three Modes docs, and all seven i18n files now explain which props apply in Wrapper mode and which props belong to component mode.
- npm:
react-zmage@1.6.0 - GitHub tag:
v1.6.0 - No breaking changes.
pnpm testpnpm -w run checkpnpm --filter llms-eval run testnpm publish --dry-run
1.5.0
-
快捷键扩展: 旋转 + 下载 + 自定义描述符: 桌面端默认开启
[/]旋转图片;Mod+S下载当前图片 (默认关闭, 启用即劫持浏览器"另存为网页"). 全部HotKeyentry 接受boolean | string | string[], 支持自定义按键描述符. 描述符基于e.code(按键物理位置, 与键盘布局无关),Mod前缀跨平台代表⌘(macOS) 或Ctrl(Windows/Linux). 严格修饰键匹配 —'Space'不会被Cmd+Space(输入法切换) 误触发.// 启用 Cmd/Ctrl+S 下载 <Zmage src="..." hotKey={{ download: true }} /> // 把旋转重绑到 A / D <Zmage src="..." hotKey={{ rotate: false, rotateLeft: 'KeyA', rotateRight: 'KeyD' }} /> // 多绑: Q 作为第二个关闭键 <Zmage src="..." hotKey={{ close: ['Escape', 'KeyQ'] }} />
-
新增
loadingDelayprop — 显示 Loading 指示器前的延迟 (默认 200ms, 经典抗闪烁阈值). 在此窗口内图片加载完成则永不显示 Loading, 解决快速切换缓存图时的指示器闪烁. 设为0恢复旧的"立即显示"行为.
closeOnDoubleClick→hideOnDblClick重命名: 与hideOnScroll形成"自动关闭触发器"语义家族, 命名更一致. 旧 prop 名编译时报错, 用户需手动替换.
- 跳页动画: 多页 set 中跨度大于 2 页的"跳页"操作现在走 fade 降级而非 swipe, 避免出现"画面瞬移"的视觉抖动 (Issue 1 / Issue 2).
- 关闭动画期间 cover 实时追踪: 关闭过程中 cover 元素的视口位置每帧追踪, 避免滚动/resize 引起的 cover-to-modal 收尾位置错位.
- 页面切换缩放比例同步:
canZoom状态在 key-reuse 切页路径上正确收敛, 切到新图后 Space/放大按钮的可用态准确反映当前图原生尺寸. - scale 校准 transition 中断: 当图片自然尺寸在切页 transition 已开始后才到达, 校准 transition 会被正确取消而不是叠加, 避免出现 transform 双轨抖动.
-
HotKey extensions: rotate + download + custom descriptors: desktop preset now ships
[/]to rotate the image andMod+Sto download the current image (download is opt-in — turning it on hijacks the browser's "Save Page As"). EveryHotKeyentry acceptsboolean | string | string[], supporting custom key descriptors. Descriptors usee.codenames (physical key positions, layout-independent); theModprefix is cross-platform —⌘on macOS,Ctrlon Windows/Linux. Strict modifier matching —'Space'is never matched byCmd+Space(macOS input-method switch).// Enable Cmd/Ctrl+S to download <Zmage src="..." hotKey={{ download: true }} /> // Rebind rotate to A / D <Zmage src="..." hotKey={{ rotate: false, rotateLeft: 'KeyA', rotateRight: 'KeyD' }} /> // Multi-binding: Q as a second close key <Zmage src="..." hotKey={{ close: ['Escape', 'KeyQ'] }} />
-
New
loadingDelayprop — delay (ms) before the loading indicator appears (default 200, classic anti-flicker threshold). If the image loads within this window the indicator never shows, eliminating the flash on cached page changes. Set0to restore the legacy instant-show behaviour.
closeOnDoubleClick→hideOnDblClickrename: aligns withhideOnScrollto form a coherent "auto-dismiss trigger" family. The old prop name fails at compile time; users must rename manually.
- Jump-page animation: jumping across more than two pages now fades instead of swiping, removing the "frame teleport" jitter (Issue 1 / Issue 2).
- Live cover tracking during close: the cover element's viewport position is tracked per-frame throughout the close animation, preventing cover-to-modal landing offsets caused by scroll/resize mid-flight.
- canZoom convergence on page change:
canZoomstate correctly converges on the key-reuse paging path, so the Space key / zoom button availability accurately reflects the current image's natural size after switching. - scale-calibration transition cancellation: when the natural image dimensions arrive after a paging transition has already started, the calibration transition is cancelled instead of stacking, avoiding the double-track transform jitter.
1.4.1
- ESC / 方向键 / Space 不再串扰外层 modal/dialog (#184):
<Zmage>处于浏览态时, 这些按键会被 zmage 在 capture phase 接住并stopImmediatePropagation, 阻止挂在 window 上的外层 modal/dialog 监听器一起被关掉。仅在 zmage 真正消费时阻断 — 比如hotKey.close=false时 ESC 会正常冒泡给外层处理。
- ESC / arrows / Space no longer leak to outer modal/dialog listeners (#184): while
<Zmage>is in browsing mode, these keys are captured (capture-phase) andstopImmediatePropagationis called on consume paths, so an outer modal hosting<Zmage>no longer closes alongside the viewer. Only consumed events are blocked — e.g.hotKey.close=falselets ESC bubble through to the outer listener as before.