视频时间轴编辑需实现时间映射、状态同步与用户反馈的实时配合;纯前端剪辑须用canvas/webgl绘制,currenttime同步需检查readystate、节流ui更新、捕获play异常,并依buffered判断加载状态。

视频时间轴编辑不是靠拖一个 <input type="range"> 就完事的——它本质是「时间映射 + 状态同步 + 用户反馈」三件事的实时配合。纯前端做剪辑级编辑(如分段、裁切、轨道叠加)必须用 Canvas 或 WebGL 手动绘制,<video></video> 自带的控件只负责播放控制。
currentTime 和 duration 怎么同步才不跳帧
直接读写 video.currentTime 在低性能设备或高码率视频上容易卡顿甚至跳秒,尤其当用户快速拖拽时。关键不是“设值快”,而是“设值准”:
- 监听
timeupdate事件前先检查video.readyState >= 2,否则currentTime可能返回NaN或旧值 - 拖拽过程中用
requestAnimationFrame节流更新进度条 UI,避免每帧都触发重绘 - 设置新时间后立即调用
video.play().catch(() => {}),防止因暂停状态导致后续timeupdate不触发 - 若视频未加载完成就拖到未缓冲区域,
video.buffered.end(0)会小于目标时间,此时应显示 loading 状态而非强行跳转
怎么画可交互的轨道时间轴(非简单进度条)
真正的编辑时间轴要支持多轨道、片段拖拽、入出点标记,不能只靠 <input>。核心是用 Canvas 绘制,并把像素坐标实时换算为时间:
- 设时间轴总宽为
canvas.width,则每毫秒对应像素 =canvas.width / (video.duration * 1000) - 入点(in-point)和出点(out-point)用两个可拖拽的
<div class="marker"> 浮在 Canvas 上,通过 <code>getBoundingClientRect()换算回时间值 - 拖拽 marker 时禁用
video.controls,避免原生控件干扰;释放后才重新启用 - Canvas 内部绘制轨道线、片段色块、时间刻度(如每 5 秒一个竖线),刻度文字用
ctx.fillText(),别用 DOM 插入——否则缩放/滚动时错位 -
seeking在每次currentTime被赋值后立即触发(哪怕值没变),且可能连续触发多次;不适合做 loading 显示 -
seeked只在跳转**实际完成且帧已解码**后触发,但不保证画面已渲染——video.videoWidth仍可能为 0 - 真正可靠的“就绪”信号是监听
loadeddata或canplay,再结合video.seeking === false判断 - 如果拖拽后
seeked没触发,大概率是视频未缓冲到目标位置,需检查video.buffered.length > 0且video.buffered.end(0) > targetTime
seeking 和 seeked 事件的实际作用边界
很多人以为 seeking 是“开始跳转”,seeked 是“跳转完成”,但真实行为更细:
Canvas 时间轴的像素-时间换算一旦写死就很难适配不同分辨率视频,建议把缩放因子(px/ms)封装成函数,每次 video.duration 变化时重算——比如用户切换清晰度后 duration 可能微调。











