跳到主要内容

时间轴模型

时间轴是 BlinkLife 回放页的核心可视化组件,采用三层 CustomPainter 架构绘制剪辑片段、进度打点和拖拽手柄,支持磁吸吸附和触觉反馈。

组件树

TimelineHub (容器)
├── 模式切换 Chip (全部 ↔ 仅高光) + 打点数
└── TimelineProgressTrack (三层绘制)
├── Layer 1: _ClipSegmentsPainter ← 剪辑片段区间色块
├── Layer 2: _ProgressMarkerPainter ← 进度条 + 打点 marker
└── Layer 3: Thumb + 拖拽浮层 ← 白色圆形手柄

TimelineToolbar (筛选栏,独立组件)
└── 动作类型 FilterChip 列表 (仅 actionTypes > 1 时显示)

三层绘制架构

Layer 1: 剪辑片段层 (_ClipSegmentsPainter)

参数类型说明
clipsList<ClipRecord>剪辑片段列表
totalDurationMsint视频总时长(毫秒)
currentPositionMsint当前播放位置
selectedClipIdint?选中的片段 ID

绘制逻辑:将 clip 的 startTime/endTime 映射到轨道宽度,圆角矩形填充半透明色,选中片段加 1.5px 描边。

Layer 2: 进度打点层 (_ProgressMarkerPainter)

参数类型说明
progressdouble0.0-1.0 播放进度
markerTimesMsList<int>预计算的 marker 时间列表
markerDotIdsList<int>对应的 DotRecord id
selectedDotIdint?选中的打点 ID
barHeightdouble轨道高度(默认 6.0)

绘制顺序:

  1. 背景轨道:RRect + 灰色填充
  2. 已播放区域:LinearGradient (蓝→紫→粉) + clipRRect
  3. 打点 marker:按动作类型绘制不同形状(已通过 alpha 0.6 衰减,已选中白色描边)

Layer 3: Thumb

白色圆形(拖拽时 9px,普通 7px)+ 拖拽浮层显示"当前/总时长"。

Marker 形状映射

动作类型形状
射门/投篮/扣杀菱形 (diamond)
犯规/违例三角形 (triangle)
其他圆形 (circle)

磁吸算法

源文件:lib/utils/timeline_utils.dart

applySnap()

({int positionMs, int? snappedIndex}) applySnap({
required int rawMs, // 拖拽原始位置(毫秒)
required List<int> markerTimesMs,
int snapThresholdMs = 300, // 轻吸附阈值
bool strongSnap = false, // 强吸附(仅高光模式)
})
  • strongSnap=true(仅高光模式)→ 直接跳到最近 marker
  • strongSnap=false(全部模式)→ 距离 < 300ms 才吸附

findNearestMarkerByTap()

点击检测:在 ±20px 范围内命中最近的 marker。

拖拽交互流程

PointerDown → setScrubbing(true) + setGestureExclusionRects(true)

PointerMove (30ms 节流)
→ _seekToPosition()
→ applySnap() → 计算磁吸位置
→ 新 marker 命中 → HapticFeedback.selectionClick()
→ session.seek(source: scrubbing)

PointerUp → seek(source: seekExternal) + setScrubbing(false)

性能优化

优化项手段效果
高频 position 独立ValueNotifier + ValueListenableBuilder避免重绘整个时间轴
Marker 时间预计算_computeMarkerTimes() 缓存避免每帧重算
拖拽节流30ms 间隔限制减少不必要的 seek
竖版轨道瘦身height: 36px (vs 横版 48px)节省竖版空间

右滑返回冲突

时间轴的水平拖拽与页面右滑返回手势冲突。解决方案:_SwipeBackBlocker 组件(RawGestureDetector + HorizontalDragGestureRecognizer(touchSlop: 1.0))。touchSlop=1 < 返回手势的 touchSlop=10,抢先赢得手势竞技场。仅在时间轴和筛选标签区域使用。

相关文档