Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# 媒体预览触控板缩放经验
更新日期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`