Caldis/react-zmage
 Watch   
 Star   
 Fork   
9 days ago
react-zmage

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 的使用场景和边界说明。

致谢

  • 感谢 @motopods 提交 PR #216,为 react-zmage 增加自定义 Portal 挂载点能力。

New Features

  • Custom Portal target: Added portalTarget so 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. Use zIndex for 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} />
        </>
      )
    }

Documentation

  • 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 for portalTarget.

Thanks

  • Thanks to @motopods for contributing PR #216, which added the custom Portal mount target capability to react-zmage.
25 days ago
react-zmage

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

  • 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 canZoom now share the same viewport-fit calculation, avoiding mismatches between configured edge and the visible margin on certain image sizes or screen resolutions.
  • Change the desktop edge default to 16px: The desktop preset now defaults edge to 16 instead of 30, 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 onAbort to <img>.
25 days ago
react-zmage

1.8.3

优化

  • AI 集成指引更清楚: README、llms.txt 和官网 AI 文档补充最小接入、样式导入、SSR 子路径与 Wrapper 模式约束,减少生成集成代码时漏掉关键步骤的概率。

修复

  • 侧边翻页按钮脱离边缘后的形态更正确: 当 controller.layout 给 flip 控件设置 inset 时,左右翻页按钮现在会变成完整圆角与对称 padding,不再保留贴边半圆样式。
  • 脱离边缘的翻页按钮保持居中: 修正 detached flip 按钮在 show / hover / active 状态下的 transform,使按钮向内偏移后仍保持垂直居中。

Improvements

  • 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.

Fix

  • Correct detached side-flip button shape: When controller.layout adds 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.
25 days ago
react-zmage

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 Features

  • New blur flip effect: animate.flip now 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' }}
    />

Visual Update

  • Safer desktop viewer spacing by default: The default radius is now 8, desktop edge is now 30, and pagination / caption are inset by default to reduce crowding around edge-aligned images and overlay text.
  • More consistent controller contrast: controller.color / controller.backdrop now propagate from the toolbar to side flip buttons and pagination, making custom dark or tinted controls easier to read.
25 days ago
react-zmage

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 的安全区。


New Features

  • More complete controller.layout insets: Numeric controller.layout inset values now follow each overlay target's natural edge instead of always mapping to the bottom edge. The toolbar follows controller.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.

25 days ago
react-zmage

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 转场使用同一个视觉写入源,修复封面首帧偏移、圆角不一致和转场末段闪动。

New Features

  • 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.layout and 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 } },
        },
      }}
    />

Visual Update

  • 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.

Improvements

  • npm package size: The published package now ships only dist assets and the SSR stub path while preserving public subpath compatibility.

Fix

  • 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.
27 days ago
react-zmage

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-fitobject-position、clip 和 border radius,减少封面被裁剪或圆角不一致时的首帧跳变。

优化

  • 默认 preset 改为 auto: 省略 preset 时会根据 (pointer: coarse) and (hover: none) 自动选择 mobile 或 desktop 默认值。需要强制旧桌面行为时可以显式传 preset="desktop"
  • 滚轮缩放手感: desktop zoom 状态下支持平滑 wheel / trackpad 缩放,可配置滚轮方向反转,并在缩小退出 zoom 后用默认 1000ms 保护时间吸收残留滚轮事件。

New Features

  • Mobile zoom gestures: preset="mobile" and the default preset="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.placement moves the toolbar, and controller.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.cover now 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.

Improvements

  • Default preset is now auto: omitting preset now resolves mobile or desktop defaults via (pointer: coarse) and (hover: none). Pass preset="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 1000ms guard that absorbs residual wheel events after zooming back out.
28 days ago
react-zmage

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 test
  • pnpm -w run check
  • pnpm --filter llms-eval run test
  • npm publish --dry-run

New Features

  • Wrapper shared gallery: <Zmage.Wrapper> now accepts an explicit set. When a descendant <img> is clicked, Wrapper matches the image src against set[i].src and opens that page; defaultPage remains the fallback when no match is found.
  • Wrapper caption discovery: without set, wrapped images can provide viewer captions through data-zmage-caption or the nearest figcaption. Rich text, Markdown, and CMS HTML can keep their original image markup while still feeding captions to the viewer.

Docs And Demos

  • The Wrapper playground snippet now matches real usage: src / alt / caption live 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.

Release

  • npm: react-zmage@1.6.0
  • GitHub tag: v1.6.0
  • No breaking changes.

Validation

  • pnpm test
  • pnpm -w run check
  • pnpm --filter llms-eval run test
  • npm publish --dry-run
28 days ago
react-zmage

1.5.0

新功能

  • 快捷键扩展: 旋转 + 下载 + 自定义描述符: 桌面端默认开启 [ / ] 旋转图片; Mod+S 下载当前图片 (默认关闭, 启用即劫持浏览器"另存为网页"). 全部 HotKey entry 接受 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'] }} />
  • 新增 loadingDelay prop — 显示 Loading 指示器前的延迟 (默认 200ms, 经典抗闪烁阈值). 在此窗口内图片加载完成则永不显示 Loading, 解决快速切换缓存图时的指示器闪烁. 设为 0 恢复旧的"立即显示"行为.

破坏变更

  • closeOnDoubleClickhideOnDblClick 重命名: 与 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 双轨抖动.

New Features

  • HotKey extensions: rotate + download + custom descriptors: desktop preset now ships [ / ] to rotate the image and Mod+S to download the current image (download is opt-in — turning it on hijacks the browser's "Save Page As"). Every HotKey entry accepts boolean | string | string[], supporting custom key descriptors. Descriptors use e.code names (physical key positions, layout-independent); the Mod prefix is cross-platform — on macOS, Ctrl on Windows/Linux. Strict modifier matching — 'Space' is never matched by Cmd+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 loadingDelay prop — 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. Set 0 to restore the legacy instant-show behaviour.

Breaking

  • closeOnDoubleClickhideOnDblClick rename: aligns with hideOnScroll to form a coherent "auto-dismiss trigger" family. The old prop name fails at compile time; users must rename manually.

Fix

  • 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: canZoom state 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.
29 days ago
react-zmage

1.4.1

修复

  • ESC / 方向键 / Space 不再串扰外层 modal/dialog (#184): <Zmage> 处于浏览态时, 这些按键会被 zmage 在 capture phase 接住并 stopImmediatePropagation, 阻止挂在 window 上的外层 modal/dialog 监听器一起被关掉。仅在 zmage 真正消费时阻断 — 比如 hotKey.close=false 时 ESC 会正常冒泡给外层处理。

Fix

  • ESC / arrows / Space no longer leak to outer modal/dialog listeners (#184): while <Zmage> is in browsing mode, these keys are captured (capture-phase) and stopImmediatePropagation is 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=false lets ESC bubble through to the outer listener as before.