@flow-player/timeline
v0.14.2
Published
Canvas timeline widget for Flow Player VOD playback: segment highlighting, event markers, zoom/pan interaction and playback cursor, designed to pair with @flow-player/browser-runtime VOD players (supports headless renderer for custom paint layers).
Maintainers
Readme
@flow-player/timeline
框架无关的 Canvas VOD 回放时间轴。默认采用 24 小时固定中心播放头,支持 Pointer Events、DPR、ResizeObserver、播放器外部时钟、区间异常、键盘/基础触控 和完整生命周期管理。
能力
- 默认 24 小时视图,可配置 1 分钟至 31 天缩放范围
none/viewport/playhead三种跨界策略- 顶部或底部三级刻度,支持 locale、timeZone、自定义 formatter(按级别/日边界)和 DST 日界线
- 刻度档位候选表与最小像素间距可配置(
tickCandidates/tickMinSpacingPx) - 固定中心播放头、hover 准星、点击选时、拖动预览/单次提交、滚轮锚点缩放
- 拖拽默认采用地图式平移(右拖 = 时间后退),可用
dragInvertsTime反转为右拖 = 时间前进 - 常规、事件、报警录像段,逐段样式覆写,自定义 marker 与 gap/overlap 区间
- 宿主 design token、内部工具栏、状态徽标和动态主题/几何更新
renderer: "none"headless 模式:只保留交互/键盘/aria/状态机,宿主自绘画布- 方向键、Home/End、
+/-、0、Escape,以及不劫持纵向滚动的基础触控 - 播放器
timeupdate外部驱动或startAutoAdvance()内部推进;两种时钟互斥 - DPR、显式/自动 resize、rAF 合帧和
destroy()完整清理
基本用法
import { FlowTimeline } from "@flow-player/timeline";
const dayStartMs = Date.parse("2026-08-27T00:00:00+08:00");
const timeline = new FlowTimeline({
container: document.getElementById("timeline")!,
centerMs: dayStartMs,
bounds: { startMs: dayStartMs, endMs: dayStartMs + 86_400_000 },
boundaryMode: "playhead",
locale: "zh-CN",
timeZone: "Asia/Shanghai",
segments: [
{ startMs: dayStartMs + 3_600_000, endMs: dayStartMs + 7_200_000, kind: "continuous" },
{ startMs: dayStartMs + 8_000_000, endMs: dayStartMs + 8_600_000, kind: "event" },
],
issues: [
{ startMs: dayStartMs + 7_200_000, endMs: dayStartMs + 8_000_000, kind: "gap" },
],
tokens: {
surface: "rgba(11, 23, 27, 0.96)",
track: "rgba(24, 49, 56, 0.72)",
playhead: "rgba(255, 107, 53, 0.95)",
continuous: "rgba(38, 201, 154, 0.9)",
event: "rgba(242, 191, 75, 0.9)",
},
toolbarVisibility: {
currentTime: true,
status: true,
legend: false,
dayReset: false,
},
onScrubPreview: ({ timestampMs }) => showPreview(timestampMs),
onSeekRequest: ({ timestampMs }) => player.seek(timestampMs),
});
player.addEventListener("timeupdate", (event) => {
timeline.setCursorTime(event.detail.positionMs, "playing");
});
timeline.destroy();所有颜色 token 都接受浏览器支持的 CSS color string,包括十六进制、rgb()、
rgba()、hsl() 和 oklch()。常用映射:事件类型使用 continuous / event /
alarm / gap / overlap,时间条底色使用 track,中心指针使用 playhead /
playheadPaused,组件与工具栏背景分别使用 surface / toolbarSurface。
工具栏自由组合
toolbarVisibility 可独立控制 currentTime、status、legend、zoomOut、
dayReset、zoomIn 和 recenter。showToolbar: false 仍是工具栏总开关;若所有
子项均隐藏,工具栏会折叠且不占高度。
timeline.setToolbarVisibility({
currentTime: false,
status: false,
legend: false,
dayReset: false,
});运行时可多次调用 setToolbarVisibility() 做部分更新,未传字段保持原值。
数据与交互
TimelineSegment用kind表达业务语义,并允许style覆写 fill、stroke、opacity、height。TimelineMarker支持独立线条、字体、文字偏移和 formatter。TimelineIssue用完整区间表达gap/overlap,而不是退化成单点标记。- 拖动期间只触发
onScrubPreview;松手触发一次onScrubCommit和onSeekRequest。点击事件提交实际点击时刻,不会跳到整段起点。 dragInvertsTime缺省为false:时间轴像地图一样跟随手指,右拖时中心时刻变早。 设为true后改为时间方向语义,右拖时中心时刻变晚。跨天是否切换业务日 仍由宿主根据onViewChange/onSeekRequest处理;boundaryMode只负责限制视图或中心播放头。onViewChange/onCenterChange携带source,可区分 player、drag、click、 wheel、keyboard、toolbar、auto 与 API。
source 语义矩阵
| source | 触发时机 |
| --- | --- |
| api | 外部调用 setCenterTime() / changeZoom() / setViewSpan() 等公开 API(未显式传 source 时) |
| player | 外部以 setCenterTime(ms, "player") 上报播放器时钟(如 timeupdate 驱动游标) |
| drag | 时间轴拖动(scrub)导致的中心/视图变化 |
| click | 单击时间轴跳转 |
| wheel | 滚轮缩放 |
| keyboard | 键盘(方向键/Home/End/缩放键) |
| toolbar | 工具栏按钮(zoom in/out、24h、回中) |
| auto | startAutoAdvance 内部自动推进 |
注意:setCenterTime() 缺省 source 即 api,也会触发 onCenterChange——外部
驱动游标时如需静默,请用 player source 并按 source 过滤。
Headless 渲染与自绘层
renderer: "none" 进入 headless 模式:不执行任何 Canvas 绘制、不显示 hover
气泡 DOM、不注入 focus boxShadow——只保留交互/键盘/aria/视图状态机与全部事件
回调。适合宿主自绘画布叠加透明交互层、自行还原行业视觉约定的场景。
const timeline = new FlowTimeline({
container,
renderer: "none", // 只保留交互与状态机
});
// 自绘层对齐尺寸:免旁路 ResizeObserver
const w = timeline.getWidth();
const h = timeline.getHeight();headless 模式下 onSegmentClick / onMarkerClick 的命中区仍按内置布局几何
(graduationPosition + tokens 高度)判定;自绘层布局不同时请自行处理命中。
刻度定制
tickCandidates?: number[]:自定义刻度步长候选表(ms 升序),缺省用内置 TICK_CANDIDATES(1s…31d)。tickMinSpacingPx?: number:刻度选档的最小像素间距,缺省 112。formatters.tick回调第 5/6 个参数为刻度级别(major | medium | minor)与 是否日边界(本地 0 点),可按级别区分格式;日边界文字可用 tokentextDayBoundary独立配色(缺省回落textMuted)。centerTimeLabel?: { color?, formatter? }:在中心指针线顶部绘制当前时间 文字(跟随showPlayhead;headless 下不绘制)。
跨天与缩放
| 配置 | 行为 |
| --- | --- |
| boundaryMode: "none" | 不限制跨天 |
| boundaryMode: "viewport" | 整个视窗保持在 bounds 内 |
| boundaryMode: "playhead" | 只限制中心播放头,视窗边缘可以跨界 |
zoomAnchorMs / setZoomAnchorTime() 可固定缩放基准;未设置时,滚轮与按钮、
API 一致以视口中心(当前时间)为锚——缩放只改档位,中心播放头与当前时间不动。
工具栏 24h 在存在全天 bounds 时恢复完整日历日,否则恢复当前播放头所在日历日。
自动推进
timeline.startAutoAdvance({
startMs,
endMs,
rate: 2, // 2ms 时间轴 / 1ms 墙钟,即 2x
intervalMs: 100,
});
timeline.stopAutoAdvance();调用 setCursorTime()、开始拖动或销毁组件会停止内部推进,避免与真实播放器
时钟竞争。
