3.3 KiB
3.3 KiB
媒体预览触控板缩放经验
更新日期: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)。
修复规则
- 媒体预览 wheel 缩放按归一化
deltaY计算。 - 单次 wheel 缩放增量必须有限幅。
- 首次 auto-fit 完成前隐藏媒体内容,避免先大后小的闪跳。
- 用户手动拖拽/缩放后,auto-fit 不再覆盖当前视图。
- 触控板缩放基准值使用 ref 保持最新。
- 父层同步缩放使用绝对值 setter,避免连续事件误差。
- 按钮和快捷键步进保持原有节奏,不和触控板手感绑定。
验证清单
- 图片预览中触控板轻推时缩放变化更细。
- 视频预览中触控板轻推时缩放变化更细。
- 工具栏放大/缩小按钮仍按原步进工作。
- 对比联动模式下缩放同步稳定,不出现过度叠加。
pnpm nx typecheck drawnix通过。
相关文件
packages/drawnix/src/components/shared/media-preview/MediaViewport.tsxpackages/drawnix/src/components/shared/media-preview/MediaViewport.scsspackages/drawnix/src/components/shared/media-preview/useViewerState.tspackages/drawnix/src/components/shared/media-preview/UnifiedMediaViewer.tsxpackages/drawnix/src/components/shared/media-preview/types.ts