Skip to content

wanglin2/VideoTimeLine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

22 Commits

Repository files navigation

一个基于Vue2的时间轴组件,一般用于监控视频的回放。

文章

手摸手带你实现一个时间轴组件

本地开发

git clone https://github.com/wanglin2/VideoTimeLine.git cd package npm i npm link cd .. cd demo npm i npm link @wanglin1994/video-timeline npm run serve

安装

npm i @wanglin1994/video-timeline

注意:源码未打包直接发布,有需要请自行配置打包文件。

引入

importVideoTimelinefrom'@wanglin1994/video-timeline'Vue.use(VideoTimeline)

基础用法

<template> <divclass="container"> <divclass="timeShow">当前时间:{{showTime}}</div> <divclass="timeLine"> <TimeLineref="Timeline"@timeChange="timeChange"></TimeLine> </div> <divclass="btns"> <el-buttontype="primary"@click="reRender">重新渲染</el-button> <el-buttontype="primary"@click="jump">跳转到2021-01-01零点</el-button> <el-selectv-model="zoom"placeholder="请选择"style="width: 100px;margin: 010px;"@change="zoomChange" > <el-optionv-for="item in zoomList":key="item.value":label="item.label":value="item.value" > </el-option> </el-select> </div> </div> </template> <script>importdayjsfrom'dayjs'exportdefault{data (){return{// 基础用法 time:Date.now(), zoom:5, zoomList: ['半小时', '1小时', '2小时', '6小时', '12小时', '1天', '3天', '15天', '30天'].map((item, index) =>{return{ label: item, value: index } }) } }, computed:{// 基础用法showTime (){returndayjs(this.time).format('YYYY-MM-DD HH:mm:ss') } },mounted (){// 基础用法setInterval(() =>{this.time+=1000this.$refs.Timeline.setTime(this.time) }, 1000) }, methods:{// 基础用法timeChange (t){this.time= t },reRender (){this.initZoomIndex=8this.$refs.Timeline.reRender() },jump (){this.$refs.Timeline.setTime('2021-01-01 00:00:00') },zoomChange (value){this.$refs.Timeline.setZoom(value) } }}</script> <style lang="less" scoped>.container{width: 100%;height: 100%;display: flex;position: relative;justify-content: center;flex-direction: column;.timeLine{height: 50px; }}.btns{margin: 10px0;display: flex;justify-content: center;}.timeShow{margin: 10px0;display: flex;justify-content: center;user-select: none;}</style>

显示时间段

<template> <divclass="container"> <divclass="timeShow">当前时间:{{showTime2}}</div> <divclass="timeLine"> <TimeLineref="Timeline2":initTime="time2"@timeChange="timeChange2":timeSegments="timeSegments"@click_timeSegments="click_timeSegments" ></TimeLine> </div> </div> </template> <script>importdayjsfrom'dayjs'exportdefault{data (){return{// 显示时间段 time2:'2021-01-15 00:00:00', timeSegments: [{ name:'时间段1', beginTime:newDate('2021-01-13 10:00:00').getTime(), endTime:newDate('2021-01-14 23:00:00').getTime(), color:'#FA3239', startRatio:0.65, endRatio:0.9 },{ name:'时间段2', beginTime:newDate('2021-01-15 02:00:00').getTime(), endTime:newDate('2021-01-15 18:00:00').getTime(), color:'#836ABB', startRatio:0.65, endRatio:0.9 } ] } }, computed:{// 显示时间段showTime2 (){returndayjs(this.time2).format('YYYY-MM-DD HH:mm:ss') } },mounted (){// 显示时间段setInterval(() =>{this.time2+=1000this.$refs.Timeline2.setTime(this.time2) }, 1000) }, methods:{// 显示时间段timeChange2 (t){this.time2= t },click_timeSegments (arr){alert('点击了:'+ arr[0].name) } }}</script> <style lang="less" scoped>.container{width: 100%;height: 100%;display: flex;position: relative;justify-content: center;flex-direction: column;.timeLine{height: 50px; }}.timeShow{margin: 10px0;display: flex;justify-content: center;user-select: none;}</style>

多个时间轴

当超过一个播放窗口时可以显示多个时间轴。

<template> <divclass="container"> <divclass="timeShow">当前时间:{{showTime3}}</div> <divclass="timeline3"> <TimeLineref="Timeline3":initTime="time3"@timeChange="timeChange3":timeSegments="timeSegments3"@click_timeSegments="click_timeSegments3":windowList="windowList"@click_window_timeSegments="click_window_timeSegments" ></TimeLine> </div> </div> </template> <script>importdayjsfrom'dayjs'exportdefault{data (){return{// 多个时间轴 time3:'2021-01-15 00:00:00', timeSegments3: [{ name:'时间段1', beginTime:newDate('2021-01-13 10:00:00').getTime(), endTime:newDate('2021-01-14 23:00:00').getTime(), color:'#FA3239', startRatio:0.65, endRatio:0.9 },{ name:'时间段2', beginTime:newDate('2021-01-15 02:00:00').getTime(), endTime:newDate('2021-01-15 18:00:00').getTime(), color:'#836ABB', startRatio:0.65, endRatio:0.9 } ], windowList: [{ name:'窗口1', timeSegments: [{ name:'窗口1的时间段1', beginTime:newDate('2021-01-13 10:00:00').getTime(), endTime:newDate('2021-01-14 23:00:00').getTime(), color:'#FA3239', startRatio:0.1, endRatio:0.9 },{ name:'窗口1的时间段2', beginTime:newDate('2021-01-12 18:00:00').getTime(), endTime:newDate('2021-01-13 00:00:00').getTime(), color:'#00AEFF', startRatio:0.1, endRatio:0.9 } ] },{ name:'窗口2', timeSegments: [{ name:'窗口2的时间段1', beginTime:newDate('2021-01-15 02:00:00').getTime(), endTime:newDate('2021-01-15 18:00:00').getTime(), color:'#FFCC00', startRatio:0.1, endRatio:0.9 } ] },{ name:'窗口3' },{ name:'窗口4' },{ name:'窗口5' },{ name:'窗口6' } ], } }, computed:{// 多个时间轴showTime3 (){returndayjs(this.time3).format('YYYY-MM-DD HH:mm:ss') } },mounted (){// 多个时间轴setInterval(() =>{this.time3+=1000this.$refs.Timeline3.setTime(this.time3) }, 1000) }, methods:{// 多个时间轴timeChange3 (t){this.time3= t },click_timeSegments3 (arr){alert('点击了:'+ arr[0].name) },click_window_timeSegments (data, index, item){alert('点击了窗口时间轴的时间段:'+ data[0].name) }, }}</script> <style lang="less" scoped>.container{width: 100%;height: 100%;display: flex;position: relative;justify-content: center;flex-direction: column;.timeline3{height: 200px; }}.timeShow{margin: 10px0;display: flex;justify-content: center;user-select: none;}</style>

显示自定义元素

有时候会想在时间轴上显示一些自定义的东西,比如某个时间段显示一张图片之类的,这可以通过监听某个时间点的位置来实现。

<template> <divclass="container"> <divclass="timeShow">当前时间:{{showTime4}}</div> <divclass="timeLine4"> <TimeLineref="Timeline4":initTime="time4":windowList="windowList4"@timeChange="timeChange4"></TimeLine> </div> <iclass="icon h-icon-mark_f"ref="flagIcon"style="color: #E72528"></i> <iclass="icon h-icon-car"ref="carIcon"style="color: #2196F3"></i> </div> </template> <script>importdayjsfrom'dayjs'exportdefault{data (){return{// 显示自定义元素 time4:'2021-01-02 00:00:00', windowList4: [{ name:'窗口1', },{ name:'窗口2', },{ name:'窗口3' },{ name:'窗口4' },{ name:'窗口5' },{ name:'窗口6' } ], } }, computed:{// 显示自定义元素showTime4 (){returndayjs(this.time4).format('YYYY-MM-DD HH:mm:ss') } },mounted (){// 显示自定义元素setInterval(() =>{this.time4+=1000this.$refs.Timeline4.setTime(this.time4) }, 1000)window.addEventListener('scroll', () =>{this.$refs.Timeline4.updateWatchTime() })this.$refs.Timeline4.watchTime('2021-01-01 23:30:00', (x, y) =>{if (x ===-1|| y ===-1){this.$refs.flagIcon.style.display='none' } else{this.$refs.flagIcon.style.display='block'this.$refs.flagIcon.style.left= x +'px'this.$refs.flagIcon.style.top= y +24+'px' } })this.$refs.Timeline4.watchTime('2021-01-02 02:30:00', (x, y) =>{if (x ===-1|| y ===-1){this.$refs.carIcon.style.display='none' } else{this.$refs.carIcon.style.display='block'this.$refs.carIcon.style.left= x +'px'this.$refs.carIcon.style.top= y +'px' } }, 2) }, methods:{// 显示自定义元素timeChange4 (t){this.time4= t } }}</script> <style lang="less" scoped>.container{width: 100%;height: 100%;display: flex;position: relative;justify-content: center;flex-direction: column;.timeLine4{height: 200px; }}.timeShow{margin: 10px0;display: flex;justify-content: center;user-select: none;}.icon{position: fixed;font-size: 30px;}</style>

API

属性

参数说明类型可选值默认值
initTime初始时间,中点所在的时间,默认为当天0点。可以传递数字类型的时间戳或字符串类型的时间,如:2020-12-19 18:30:00Number/String
timeRange中间刻度所允许显示的时间范围,即当前时间的限定范围,对象类型,字段见表1-1Object
initZoomIndex初始的时间分辨率,数字索引,时间分辨率数组为:['半小时', '1小时', '2小时', '6小时', '12小时', '1天', '3天', '15天', '30天', '365天', '3650天']Number5
showCenterLine是否显示中间的竖线Booleantrue
centerLineStyle中间竖线的样式,对象类型,字段见表1-2Object
textColor日期时间文字的颜色Stringrgba(151,158,167,1)
hoverTextColor鼠标滑过显示的时间文字颜色Stringrgb(194, 202, 215)
lineColor时间刻度的颜色Stringrgba(151,158,167,1)
lineHeightRatio时间刻度高度占时间轴高度的比例,对象格式,字段见表1-3Object
showHoverTime鼠标滑过时是否显示实时所在的时间Booleantrue
timeSegments要显示的时间颜色段,对象数组类型,对象字段见表1-4Array[]
backgroundColor时间轴背景颜色String#262626
enableZoom是否允许鼠标滚动切换时间分辨率Booleantrue
enableDrag是否允许拖动Booleantrue
windowList播放窗口列表,播放窗口数量大于1的话可以配置此项,会显示和窗口对应数量的时间轴,只有一个窗口的话请直接使用基本时间轴,对象数组类型,对象字段见表1-5Array[]
baseTimeLineHeight当显示windowList时的基础时间轴高度Number50
initSelectWindowTimeLineIndex初始选中的窗口时间轴索引Number-1
maxClickDistance(v0.1.2+)鼠标按下和松开的距离小于该值认为是点击事件Number3
roundWidthTimeSegments(v0.1.6+)绘制时间段时对计算出来的坐标进行四舍五入,可以防止相连的时间段绘制出来有间隔的问题Booleantrue
customShowTime(v0.1.7+)自定义显示哪些时间,详细请阅读下方【属性详解1-1】Function
hoverTimeFormat(v0.1.9+)格式化鼠标滑过时间,函数类型,接收一个time参数,代表当前鼠标所在的时间戳,函数需要返回一个格式化后的时间字符串,默认显示的时间格式为YYYY-MM-DD HH:mm:ssFunction
showDateAtZero(v0.1.9+)0点处是否显示日期,时间轴上默认0点处显示的日期,需要显示成时间,那么就需要设为false,然后通过formatTime属性自定义格式化Booleantrue
formatTime(v0.1.9+)格式化时间轴显示时间,默认规则是模式YYYY年月模式YYYY-MM0MM-DD、其他HH:mm,如果想自定义,比如0点还是显示时间,不显示日期,就可以通过该函数进行格式化。如果该函数返回值为空值,那么还会走内部规则。Function
extendZOOM(v0.1.9+)扩展ZOOM列表,详细请参考下方【属性详解1-2】Array[]

属性详解1-1 customShowTime的用法

该属性在v0.1.7+版本开始支持

当你在使用本组件的时候可能会遇到一个很常见的问题,比如容器的宽度很小,然后时间段展示的又是几天、甚至半个月的时间,那么很容易遇到时间段里面时间都挤在一起的问题,比如这个issue,其实组件内部是内置了一些判断方法,比如在3天的时间分辨率下,对应的initZoomIndex=6,对应关系可参考上方属性表格,那么就会使用3天的这个判断规则,如下:

date=>{// 每三小时显示returndate.getHours()%3===0&&date.getMinutes()===0}

意思是显示3的倍数的整点小时,那么当你容器宽度不够,且时间分辨率设置的比较大,那么时间就会挤在一起看不清,这时候你就可以通过customShowTime属性传入自定义的判断方法,这个方法接收两个参数:

  • date:是否要显示的时间,可以根据该时间进行判断是否要显示这个时间
  • currentZoomIndex:当前的时间分辨率,比如3天对应的就是6,对应关系可参考上方属性表格

那么如果内置的规则不满足的话,就可以自定义,比如3天的时间分辨率下我想只显示12倍数的小时,可以这么做:

<TimeLine:customShowTime="customShowTime""></TimeLine>
exportdefault{methods: {customShowTime(date,zoomIndex){if(zoomIndex===6){returndate.getHours()%12===0&&date.getMinutes()===0}}}}

函数返回值需要注意一下,如果要显示返回true,如果不显示返回false,如果不处理,仍旧交给内部规则,返回其他值。

属性详解1-2 extendZOOM的用法

该属性用于扩展ZOOM列表,即时间分辨率,内置了11个时间分辨率,可以参考上方表格initZoomIndex属性,如果内置的时间分辨率满足不了你,那么可以通过该属性进行扩展。

extendZOOM为数组类型,数组的每一项为:

{zoom: 26,// 时间分辨率,整个时间轴表示的时间范围,单位:小时zoomHourGrid: 0.5,//时间分辨率对应的每格小时数,即时间轴上最小格代表多少小时mobileZoomHourGrid: 2,// 手机模式下时间分辨率对应的每格小时数,如果不用适配手机端,可以不用设置}

这个数组的数据会追加到内部的ZOOM数组,对应的zoomIndex往后累加即可,内部一共有11zoom,那么你追加了一项,对应的zoomIndex为11,因为是从零开始计数。

同时你需要传递customShowTime属性来自定义控制时间显示,否则会报错,因为内置的规则只有11个。

接下来看一个案例。

只显示当天的时间, 从00:00:00到23:59:59,详细请看这个issue

首先默认的initZoomIndex5,即1天,刚好满足,不用修改,然后将enableZoom设为false,不允许修改时间分辨率;将enableDrag设为false,不允许拖拽; 然后再将initTime设为当天的12:00:00,那么刚好整个时间轴显示的就是当前的时间,到这里,似乎就可以了。但是实际上会存在一些问题,如前面的issue中所示。

问题1:0点处显示的是日期,需要改成时间

很简单,将showDateAtZero设为false0点就不会显示日期了

问题2:鼠标滑过显示的还是带日期的,这个好办,通过hoverTimeFormat属性自定义格式化规则即可:

hoverTimeFormat(time){// 小于今天,大于今天的时间不显示if(dayjs(time).isBefore(dayjs().format('YYYY-MM-DD 00:00:00'))||dayjs(time).isAfter(dayjs().format('YYYY-MM-DD 23:59:59'))){return''}returndayjs(time).format('HH:mm:ss')}

问题3:左右两侧的时间显示不出来

0点和24点的时间刚好是两端,因实现原理问题,无法显示,怎么办呢,其实很简单,假如时间轴表示的时间范围为25小时,那么左右两端不就会各多出半小时的时间吗,这个空间足够显示时间了,但是内部的时间分辨率没有25小时的,这时就需要扩展时间分辨率了:

extendZOOM: [{zoom: 25,zoomHourGrid: 0.5}]

扩展了extendZOOMcustomShowTime不能少,否则会报错:

customShowTime(date,zoomIndex){// 当zoomIndex等于11,也就是等于我们开展的zoom时才自己处理if(zoomIndex===11){// 时间是2的倍数时才会显示returndate.getHours()%2===0&&date.getMinutes()===0}}

到这里,你还会发现一个问题,24点实际上是下一天的0点,所以显示的是00:00,这样可能不符合我们的需求,这时我们可以通过formatTime来格式化时间轴上的时间显示,判断是下一天的0点,那么就改成24:00

formatTime(time){// 下一天的00:00显示24:00if(time.isAfter(dayjs().format('YYYY-MM-DD 23:59:59'))){return'24:00'}if(time.hour()===0&&time.minute()===0&&time.millisecond()===0){returntime.format('HH:mm')}}

完整代码请参考:CustomZoom.vue

表1-1 timeRange对象的字段格式

字段名说明类型可选值默认值
start允许显示的最小时间,可以传递数字类型的时间戳或字符串类型的时间,如:2020-12-19 18:30:00Number/String
end允许显示的最大时间,可以传递数字类型的时间戳或字符串类型的时间,如:2020-12-19 18:30:00Number/String

表1-2 centerLineStyle对象的字段格式

字段名说明类型可选值默认值
width线的宽度,单位pxNumber2
color线的颜色String#fff

表1-3 lineHeightRatio对象的字段格式

字段名说明类型可选值默认值
date0点时的日期线段高度Number0.3
time显示时间的线段高度Number0.2
none不显示时间的线段高度Number0.1
hover鼠标滑过时显示的时间段高度Number0.3

表1-4 timeSegments数组的对象元素的字段格式

字段名说明类型可选值默认值
beginTime起始时间戳,必填Number
endTime结束时间戳,必填Number
color颜色,必填String
startRatio高度的起始比例,即top=时间轴高度*startRatioNumber0.6
endRatio高度的结束比例,即bottom=时间轴高度*endRatioNumber0.9

从v0.1.8+版本开始,时间段可以只传一个beginTime,绘制一根宽度为1px的线段

表1-5 windowList数组的对象元素的字段格式

字段名说明类型可选值默认值
timeSegments要显示的时间段,对象数组,对象字段见表1-4Array[]
可以添加你需要的其他任意字段

事件

事件说明回调函数参数
timeChange当前时间切换事件currentTime(当前时间,时间戳格式)
dragTimeChange拖动时间条结束后的事件currentTime(当前时间,时间戳格式)
mousedown鼠标按下事件e(事件对象)
mouseup鼠标松开事件e(事件对象)
click_timeSegments点击到了基础时间轴里的时间段时触发timeSegments(点击到的时间段,数组类型)、time(v0.1.10+,点击位置对应的时间戳)、 date(v0.1.10+,点击位置对应的日期时间字符串)、 x(v0.1.10+,点击位置相对时间轴左侧的距离)
click_window_timeSegments点击到了窗口时间轴里的时间段时触发timeSegments(点击到的时间段,数组类型)、index(时间轴索引)、item(时间轴数据)
change_window_time_line点击窗口时间轴进行切换选中时触发index(时间轴索引)、item(时间轴数据)
click_timeline(v0.1.2+)时间轴的点击事件time(点击位置对应的时间戳)、 date(点击位置对应的日期时间字符串)、 x(点击位置相对时间轴左侧的距离)

方法

方法名说明参数
updateWatchTime手动更新观察的时间位置,比如页面滚动后时间轴的整体位置变化了需要调用,如果没有显示自定义元素时无需调用
reRender重新渲染
setTime设置当前时间t(数字类型的时间戳或字符串类型的时间,如:2020-12-19 18:30:00)
setZoom设置分辨率index(分辨率索引)
watchTime设置要观察的时间点,会返回该时间点的实时位置,可以根据该位置来设置一些自定义元素的位置,位置为相对于浏览器可视窗口的位置time(要观察的时间,数字类型的时间戳或字符串类型的时间,如:2020-12-19 18:30:00), callback(时间点位置变化时会调用,回调参数为x水平位置、y重置位置,单位px), windowTimeLineIndex(如果自定义元素是要显示到某个窗口时间轴里的话,可以通过该参数来指定第几个时间轴,数字索引,从1开始)
onResize如果时间轴所在的容器尺寸变化了需要调用该方法来适应

About

一个简单的Vue2视频时间轴组件

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published