Files
TrueGrowth/docs/MEDIA_PREVIEW_TRACKPAD_ZOOM_LESSONS.md

3.3 KiB
Raw Blame History

媒体预览触控板缩放经验

更新日期2026-04-30

背景

图片、视频预览里使用触控板放大缩小时,缩放速度太快,细微手势也会明显跳变,导致用户很难停在想要的比例。

这类问题不要只看“最大/最小缩放值”。真正影响手感的通常是 wheel 事件的粒度、单次缩放步进、连续事件同步方式。

根因

1. 高频 wheel 不能用固定大步进

触控板会连续发出很多小 wheel 事件。若每次都固定 +0.1-0.1,微小手势也会被放大成大幅缩放。

经验:

  • 触控板/滚轮缩放要按 deltaY 计算比例,而不是只看方向。
  • 要限制单个事件最大缩放量,防止鼠标滚轮或系统加速度造成跳变。
  • deltaMode 不是像素时要先归一化,避免不同设备手感差异过大。

2. 首次自适应不能制造视觉跳动

预览容器、图片/视频元信息和 CSS 尺寸可能不是同一帧就绪。如果先显示原始比例,再下一帧自适应,会让用户看到明显跳动。

经验:

  • 首次进入预览时可以等 auto-fit 计算完成后再显示媒体内容。
  • 容器尺寸变化要重新触发 auto-fit但用户手动拖拽/缩放后不要再自动覆盖视图。
  • auto-fit 相关隐藏样式要只作用于媒体内容,不影响工具栏和视频控件交互状态。

3. 连续缩放要读取最新值

React 事件回调在高频输入下容易遇到闭包值滞后。连续 wheel 中如果一直基于旧 localZoom 计算,会让缩放节奏不稳定。

经验:

  • 高频交互中的当前值可用 ref 保存。
  • 状态更新仍走 setState,但计算基准优先读 ref 中的最新缩放。
  • 只有缩放值实际变化超过极小阈值时才通知父层,减少无意义渲染。

4. 同步模式不要传相对增量

媒体预览内部已经算出了目标缩放值,父层若再用 z - zoomLevel 转为相对增量,高频事件下容易叠加闭包误差。

经验:

  • 子组件已经得到目标 zoom 时,父级 action 应支持设置绝对缩放值。
  • 快捷键、按钮这类明确步进仍适合保留相对 zoom(delta)
  • wheel/pinch 这类连续输入更适合 setZoomLevel(value)

修复规则

  1. 媒体预览 wheel 缩放按归一化 deltaY 计算。
  2. 单次 wheel 缩放增量必须有限幅。
  3. 首次 auto-fit 完成前隐藏媒体内容,避免先大后小的闪跳。
  4. 用户手动拖拽/缩放后auto-fit 不再覆盖当前视图。
  5. 触控板缩放基准值使用 ref 保持最新。
  6. 父层同步缩放使用绝对值 setter避免连续事件误差。
  7. 按钮和快捷键步进保持原有节奏,不和触控板手感绑定。

验证清单

  • 图片预览中触控板轻推时缩放变化更细。
  • 视频预览中触控板轻推时缩放变化更细。
  • 工具栏放大/缩小按钮仍按原步进工作。
  • 对比联动模式下缩放同步稳定,不出现过度叠加。
  • pnpm nx typecheck drawnix 通过。

相关文件

  • packages/drawnix/src/components/shared/media-preview/MediaViewport.tsx
  • packages/drawnix/src/components/shared/media-preview/MediaViewport.scss
  • packages/drawnix/src/components/shared/media-preview/useViewerState.ts
  • packages/drawnix/src/components/shared/media-preview/UnifiedMediaViewer.tsx
  • packages/drawnix/src/components/shared/media-preview/types.ts