入门篇跑通了基础功能,但实际监控回放场景还需要:全天录像概览(双进度条)、播放指针居中跟随、点击空白自动续播。这篇文章把这些进阶功能都实现了。
一、双进度条:主时间轴+全天概览
1.1 什么是双进度条?
┌─────────────────────────────────────────┐ ← 主时间轴(可缩放) │ ████ ████████████ ████████ │ ├─────────────────────────────────────────┤ ← 概览条(全天) │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ ↑可视窗口 ↑播放位置 │ └─────────────────────────────────────────┘- 主时间轴:可缩放、可拖动,显示当前视口的细节
- 概览条:显示全天的录像分布,可快速跳转
1.2 启用概览条
<VueTimeAxisPlus :start-time="startTime" :segments="segments" :overview="true" :overview-height="20" />1.3 运行时动态开关
<template> <VueTimeAxisPlus ref="timeline" :segments="segments" /> <button @click="$refs.timeline.updateOptions({ overview: true })">显示概览条</button> <button @click="$refs.timeline.updateOptions({ overview: false })">隐藏概览条</button> </template>1.4 概览条点击跳转
点击概览条会触发changeTime事件,可以实现快速跳转:
onChangeTime(data){// 点击概览条时,mode为'click'// timestamp为点击位置对应的时间this.seekTo(data.timestamp)}二、播放指针居中跟随
2.1 问题
播放时,指针移动到视口边缘就看不到了。
2.2 解决方案
启用centerPointer,播放时自动滚动视口使指针居中:
<VueTimeAxisPlus ref="timeline" :center-pointer="true" @playTime="onPlayTime" />2.3 播放逻辑实现
data(){return{playing:false,playTimer:null,playTime:0}},methods:{onPlayTime(data){// data.timestamp: 当前播放时间// data.valid: 是否在有效录像内if(!data.valid){// 进入空白区域,查下一段续播constnext=this.$refs.timeline.getNextSegment(data.timestamp)if(next){this.playTime=next.beginthis.$refs.timeline.updateTime(this.playTime)}}},startPlay(){this.playing=truethis.playTimer=setInterval(()=>{this.playTime+=1000// 每秒前进1秒if(this.playTime>=this.maxTime){this.stopPlay()return}this.$refs.timeline.updateTime(this.playTime)},1000)},stopPlay(){this.playing=falseclearInterval(this.playTimer)}}2.4 手势让位后自动恢复
用户拖动时间轴时,播放指针暂停跟随;松手后自动恢复:
<VueTimeAxisPlus :center-pointer="true" :center-resume-delay="3000" // 3秒后自动恢复居中 />三、跨空白续播
3.1 问题
录像有空白时段(如夜间无录像),用户点击空白区域时怎么处理?
3.2 解决方案
启用snapToNearest,点击空白自动吸附到最近的片段起点:
<VueTimeAxisPlus :snap-to-nearest="true" @changeTime="onChangeTime" />3.3 续播逻辑
onChangeTime(data){if(data.mode==='snap'){// 被吸附到了下一段起点console.log(`自动跳转到${newDate(data.nextBegin.begin).toLocaleTimeString()}`)// 开始播放下一段this.playTime=data.nextBegin.beginthis.startPlay()}}3.4 getNextSegment查询
不依赖snap,手动查询下一段:
// 从当前时间往后找最近的录像片段constnext=this.$refs.timeline.getNextSegment(this.playTime)if(next){console.log(`下一段:${newDate(next.begin).toLocaleTimeString()}-${newDate(next.end).toLocaleTimeString()}`)console.log(`业务字段:${next.id}`)}3.5 getNextSegments批量查询
获取从当前时间往后的所有片段(可做播放列表):
constplaylist=this.$refs.timeline.getNextSegments(this.playTime)console.log(`还有${playlist.length}段录像`)四、自定义样式
4.1 axisStyle配置
<VueTimeAxisPlus :axis-style="{ borderColor: '#333', graduationColor: '#666', tickTextColor: '#fff', hoverLineColor: '#00ff00', hoverTextColor: '#00ff00', overviewBg: '#1a1a1a', overviewWindowColor: 'rgba(64, 196, 255, 0.2)', overviewWindowBorder: '#40c4ff', overviewPlayColor: '#ff5c5c' }" />4.2 片段样式
每个片段可以独立设置样式:
segments:[{begin:t(6),end:t(7),style:{background:'rgba(24,208,217,0.5)'}// 青色},{begin:t(7),end:t(8),style:{background:'rgba(255,77,79,0.6)'}// 红色},{begin:t(8),end:t(9),style:{background:'rgba(255,165,0,0.5)'}// 橙色}]4.3 主题切换
constdarkTheme={tickTextColor:'#ccc',overviewBg:'#1a1a1a',overviewWindowColor:'rgba(64, 196, 255, 0.15)'}constlightTheme={tickTextColor:'#333',overviewBg:'#f5f5f5',overviewWindowColor:'rgba(64, 196, 255, 0.1)'}constswitchTheme=(theme)=>{this.$refs.timeline.updateOptions({axisStyle:theme})}五、踩坑记录
坑1:播放指针居中与手势冲突
现象:用户拖动时间轴时,播放指针还在居中跟随,导致视口跳动。
原因:centerPointer和用户手势同时生效。
解决:组件内部已处理,用户手势时自动暂停居中,松手后延迟恢复。
坑2:概览条动态开关后高度不对
现象:运行时切换overview后,时间轴高度没有变化。
原因:没有调用updateOptions。
解决:
this.$refs.timeline.updateOptions({overview:true})// 组件内部会自动重算画布高度坑3:getNextSegment返回null
现象:调用getNextSegment后返回null。
原因:当前时间已经是最后一段之后。
解决:判断返回值:
constnext=this.$refs.timeline.getNextSegment(time)if(next){// 有下一段}else{// 已经是最后一段之后}六、总结
| 功能 | 配置 | 关键点 |
|---|---|---|
| 概览条 | overview: true | 支持运行时动态开关 |
| 播放指针居中 | centerPointer: true | 手势时自动暂停 |
| 跨空白续播 | snapToNearest: true | 点击空白自动吸附 |
| 查询下一段 | getNextSegment(time) | 返回完整片段信息 |
| 主题切换 | updateOptions({axisStyle}) | 支持部分字段覆盖 |
更新日期:2026年9月
调试版本:vue-time-axis-plus 1.x / Vue 3.4+