一个基于 vue 框架使用 canvas 绘制的时间轴组件,可用于视频、录像播放或实时数据展示等业务。
组件特点
1. 高性能
组件使用纯原生的 canvas 绘图未使用第三库,所以组件体积很小;
同时相比于使用 div 方案需要频繁操作 Dom 性能上更高、体验更好。
2. 全端自适配
组件可以在电脑、移动端自适应,无需额外的配置。
- 宽度自适应,由于canvas 必须设置固定宽度,根据父容器的宽度动态计算 canvas 宽度,让组件宽度支持百分比设置;
//自适应父容器宽度 this.width参数支持百分比设置
let parentWidth = canvas.parentElement.clientWidth;
if (/^(\d|[1-9]\d|100)%$/.test(this.width)) {
width = Math.floor((this.width.replace("%", "") / 100) * parentWidth);
}
- 画布与图像比例自适应,通过 devicePixelRatio 像素比计算 canvas 画布与图像的比例,解决移动模糊的问题;
//移动端像素模糊问题是由于dpr像素比造成的,扩大canvas画布的像素,使1个canvas像素和1个物理像素相等
this.dpr = window.devicePixelRatio;
// // 设置图像大小
canvas.style.width = `${width}px`;
canvas.style.height = `${this.height}px`;
// 设置画布大小
canvas.width = Math.round(width * this.dpr);
canvas.height = Math.round(this.height * this.dpr);
// 由于画布扩大,canvas的坐标系也跟着扩大,
// 按照原先的坐标系绘图内容会缩小, 所以需要将绘制比例放大
this.ctx.scale(this.dpr, this.dpr);
- 刻度线自适应,不管 canvas 缩小到什么宽度,都能保证刻度线不会挤压在一起;
// 每个像素点对应多少秒(像素转时间和刻度)
px_second() {
let second = (this.whole_hour * 60 * 60) / this.canvasWidth;
// 保证不管缩小到什么宽度,刻度线都不会挤压在一起
if (second > this.minPxSecond) second = this.minPxSecond;
return second;
}
- 事件自动适配,电脑端和移动端分别自动注册事件;
//是否为手机端
this.isMobile = /Mobi/i.test(navigator.userAgent);
//移动端如不禁止,在滑动时会触发鼠标事件与滑动事件冲突
if (!this.isMobile) {
canvas.addEventListener("mousewheel", this.mousewheel);
canvas.addEventListener("mousemove", this.mousemove);
canvas.addEventListener("mousedown", this.mousedown);
canvas.addEventListener("mouseup", this.mouseup);
canvas.addEventListener("mouseleave", this.mouseleave);
}
- 参数监听,当参与组件绘制的参数发生变化时会立马响应重新绘制;
computed: {
//计算需要重新绘制的参数,方便统一监听
changeProps() {
let { startMeddleTime, markTime, timeRange } = this;
this.meddleTime = this.startMeddleTime;
return { startMeddleTime, markTime, timeRange };
}
},
watch: {
// 监听需要重新绘制的参数
changeProps: function (newV) {
// 绘制
this.drow();
},
},
- 窗口或屏幕宽自适应,组件会随着电脑端的窗口、手机的屏幕宽的大小变化而自动缩放。
// 监听窗口大小
resize() {
let canvas = this.$refs.canvas;
//有时屏幕尺寸变化了,而容器的尺寸还未改变的情况下的处理
if (canvas.style.width === canvas.parentElement.clientWidth + "px") {
setTimeout(() => {
this.resize();
}, 10);
} else {
//重新初始化
this.init();
}
}
3. 功能丰富
1.拖动或点击时间轴 在释放的时候可以获得时间数据;
2.设置时间点 通过 startMeddleTime
参数可以设置起点时间,可将设置的时间切换到时间轴的中间;
3.设置时间区域 通过 timeRange
参数可以指定录像时间段,时间轴只绘制这段时间内的刻度线,并且只能在这段时间里进行拖动;
4.设置区域标记 通过 markTime
参数可以对时间轴的某个或某几个区域进行标记,标记的区域可以自定义背景颜色和文本说明;
5.刻度缩放 时间轴刻度支持多级显示,可以通过鼠标滚轮或双指对刻度进行缩小放大;
6.自动播放 设置参数 isAutoPlay = true
可以开启自动播放,开启后时间轴将以 1s 的速度前进;
7.事件监听 可以绑定 change
事件来监听时间轴的时间变化和状态变化;
8.hover 显示时间 鼠标放在时间轴上移动可以显示时间;
9.自定义样式 可以设置 colors
参数对颜色自定义;
10.组件独立 可以在同一页面内使用多个时间轴组件互干扰;
11.时间轴绘制 通过 canvas 动态绘制刻度、录像段、时间点。
4. 使用简单
1.时间轴已组件化,可以在 vue 框架直接使用,甚至可做到无参使用;
2.所有代码开源,并且代码注释详细,文档使用说明和示例齐全;
3.API 操作灵活,可以灵活的适用各种应用场景和定制化的二次开发。
动画演示
安装
npm install
运行
npm run serve
配置
引入组件
main.js
:
// 全局注册
import TimeLineCanvas from "./components/timeline-canvas.vue";
Vue.use(TimeLineCanvas);
在 script
中引用组件:
// 如采用全局注册,页面就不需要再引用注册,可以直接使用
import TimeLineCanvas from "./components/timeline-canvas.vue";
export default {
components: {
TimeLineCanvas,
},
....
}
在 template
中使用组件:
<!--无参使用时,参数使用全默认值 -->
<TimeLineCanvas></TimeLineCanvas>
或
<TimeLineCanvas
ref="time_line"
@change="changeDate"
:width="width"
:mark-time="markTime"
:time-range="time_range"
:isAutoPlay="isAutoPlay"
:startMeddleTime="startMeddleTime"
/>
组件数据
export default {
data() {
return {
isAutoPlay: false,
width: "100%",
startMeddleTime: "2023-01-02 08:54:56",
time_range: ["2023-01-02 00:00:00", "2023-01-02 23:59:59"],
markTime: [
{
beginTime: "2023-01-02 01:01:00",
endTime: "2023-01-02 02:02:00",
bgColor: "red",
text: "困人",
},
{
beginTime: "2023-01-02 08:01:00",
endTime: "2023-01-02 10:02:00",
bgColor: "pink",
text: "非法闯入",
},
{
beginTime: "2023-01-02 15:01:00",
endTime: "2023-01-02 16:02:00",
bgColor: "yellow",
text: "故障",
},
],
};
},
methods: {
clickCanvas(date) {
console.log(date);
},
changeDate(date, status) {
console.log("选择时间:" + date + " 播放状态:" + status);
},
},
};
Props
名称 | 说明 | 类型 | 默认 |
---|---|---|---|
width | 时间轴宽度,支持固定(不需要带单位)和百分比,默认自适应父容器 | Number String | 100% |
height | 时间轴高度,非必要可以忽略 | Number String | 60 |
startMeddleTime | 启动时间,未传参时会根据timeRange 计算;timeRange 参数也未传采用当前时间 | String | |
timeRange | 时间轴绘制的时间范,当参数为String 时传某天的日期;当为Array 时传时间范围 | Array String | 当天 |
markTime | 区域进行标记,可以自定义背景颜色和文说明本 | Array | |
isAutoPlay | 开启后时间轴将以 1s 的速度前进进 | Boolean | |
colors | 自定义颜色 | Object |
Props [markTime] 结构
名称 | 说明 | 类型 |
---|---|---|
beginTime | 开始位置 | String |
endTime | 结束位置 | String |
bgColor | 背景颜色 | String |
text | 文本说明 | String |
Props [colors] 结构
名称 | 说明 | 类型 | 默认值 |
---|---|---|---|
background | 背景 | String | #2b2f33 |
meddleLine | 中间线 | String | #33CC33 |
meddleDate | 中间时间 | String | #33CC33 |
moveLine | 移动线 | String | #808080 |
moveDate | 移动时间 | String | #009966 |
scaleLine | 刻度线 | String | #808080 |
scaleBar | 刻度条 | String | #45484c |
Methods
名称 | 说明 | 参数 |
---|---|---|
play | 自动播放未开启,时可以手动播放 | date :播放的起始时间 |
stop | 手动暂停播放 |
Events
名称 | 说明 | 回调参数 |
---|---|---|
click | 拖动或点击时间轴释放的时候触发 | date :选择的时间 |
change | 当时间和播放状态发生变化时触发,只有当开启 isAutoPlay=true 才有状态变化 | date :选择的时间 status :播放状态 (start :开始 play :播放 stop :暂停 end :结束) |
最后说明
在刚开始有考虑用div方案,后面使用起来发现每一次重绘就要操作上百个dom节点,性能很差,所以决定最后决定采用canvas来写时间轴。
以上就是本文的全部内容,希望对大家有所帮助,也希望大家多多支持。
update(更新内容)
- 2023-02-16 完善文档
- 2023-02-13 发布 1.0.0 开源版本
- 2023-02-10 优化代码规范和完善注释
- 2023-02-05 优化功能准备开源
- 2022年底 完成初版
PS: 感谢CSDN将本文评为《CSDN每天最佳新人–2023-12-18》