Vue2时间轴组件(TimeLine/分页、自动顺序播放、暂停、换肤功能、时间选择,鼠标快速滑动)
目录
1介绍背景
2实现原理
3组件介绍
4代码
5其他说明
1介绍背景
项目背景是 一天的时间轴 10分钟为一间隔 一天被划分成144个节点 一页面12个节点 代码介绍的很详细 可参考或者借鉴
2实现原理
对Element-plus滑块组件的二次封装
基于Vue2(2.6.14),对element-plus(2.15.13)中slider滑块组件的二次封装。其中,用到了element-plus中的部分icon图标。
组件主要是对时间节点进行分页、顺序播放、暂停、换肤功能、时间选择,点击事件,上一页下一页,父组件监听到当前时间的改变,从而触发相应的业务逻辑。
3组件介绍
light天亮模式
dark黑夜模式
4代码
<template>
<div class="TimeLineBox">
<h3>时间轴 {{ timeYMD }} {{ timeSFM }} </h3>
<div class="TimeLineBox_content">
<div class="block custom-block">
<!-- <span class="demonstration">默认</span> -->
<el-date-picker v-model="dayDate" type="date" placeholder="选择日期" size="small" @change="handleDatePickerChange"
style="width: 150px;" :editable="false">
</el-date-picker>
</div>
<TimeLine :second="1000" :timeYMD="timeYMD" :timeSet="timeSet"
@handleNowValueChange="handleNowValueChange">
</TimeLine>
</div>
</div>
</template>
<script>
//--start-#引入时间轴组件#-->
import TimeLine from "./../components/TimeLine.vue";
//--start-#引入第三方关于时间的文件#-->
import moment from 'moment';
export default {
components: {
TimeLine
},
data() {
return {
//--start-#用于时间轴显示的初始化时间时分秒#-->
timeSFM: "00:10:00",
//--start-#时间集合#-->
timeSet: [],
//--start-#用于时间轴显示的初始化时间年月日#-->
timeYMD: "",
// //--start-#拼接时间组合 具体就是年月日时分秒#-->
// timeCombination: null
dayDate: '',
}
},
mounted() {
this.computerTimeSet();
},
computed: {},
methods: {
handleDatePickerChange() {
//--start-#获取时间选择器的年月日#-->
//console.log("@@", this.dayDate);
if (this.dayDate) {
//--start-#非空执行#-->
this.handleTimeSetNode(moment(this.dayDate).format('YYYY-MM-DD'))
}
},
//--start-#计算时间轴中需要的时间节点集合方法#-->
computerTimeSet() {
//--start-#获取当天的年月日0时0分0秒#-->
let startOfDay = moment().startOf('day');
//--start-#获取当天的前一天年月日0时0分0秒#-->
let previousDate = startOfDay.subtract(1, 'days');
this.handleTimeSetNode(previousDate)
},
handleTimeSetNode(timeNode) {
//--start-#获取当天的前一天年月日用于计算时间节点#-->
let oneTimeNode = new Date(timeNode);
//--start-#获取当天的前一天年月日 用于时间轴标题显示#-->this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD HH:mm:ss');
this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD');
//--start-#遍历计算获取当天10分钟一次的时间节点组合#-->
for (let i = 0; i < 144; i++) {
//--start-#获取时间 并设置分钟位加10,意思是加10分钟#-->
oneTimeNode.setMinutes(oneTimeNode.getMinutes() + 10);
//this.timeSet.push(moment(oneTimeNode).format('YYYY-MM-DD HH:mm:ss'));
//--start-#获取时间节点并加入到时间节点集合中#-->
this.timeSet.push(moment(oneTimeNode).format('HH:mm:ss'));
}
},
//--start-#父子函数回调,接收时间值的改变并响应#-->
handleNowValueChange(val, val2) {
if (val === "nextDay") {
let oneTimeNode = new Date(this.timeYMD);
oneTimeNode.setDate(oneTimeNode.getDate() + 1);
this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD');
this.timeSFM = val2;
}
else if (val === "preDay") {
let oneTimeNode = new Date(this.timeYMD);
oneTimeNode.setDate(oneTimeNode.getDate() - 1);
this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD');
this.timeSFM = val2;
}
else if (val === "nextDayAndZero") {
let oneTimeNode = new Date(this.timeYMD);
oneTimeNode.setDate(oneTimeNode.getDate() + 1);
this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD');
this.timeSFM = val2;
}
else if (val === "preDayAndZero") {
let oneTimeNode = new Date(this.timeYMD);
oneTimeNode.setDate(oneTimeNode.getDate() - 1);
this.timeYMD = moment(oneTimeNode).format('YYYY-MM-DD');
this.timeSFM = val2;
}
else {
this.timeYMD = val;
this.timeSFM = val2;
}
console.log(val, val2);
}
},
// watch:{
// light(newVal,oldVal){
// console.log(newVal,oldVal);
// }
// }
}
</script>
<style lang="less" scoped>
.TimeLineBox {
width: 100%;
height: 100%;
//background-image: url("./../assets/images/播放.png");
background-size: 100% 100%;
background-color: aqua;
position: relative;
h3 {
color: rgb(241, 19, 19);
position: absolute;
bottom: 150px;
left: 50%;
transform: translate(-50%, 0);
}
.TimeLineBox_content {
position: absolute;
bottom: 50px;
left: 50%;
transform: translate(-50%, 0);
width: 1000px;
// width: 80%;
height: 90px;
.custom-block {
position: absolute;
left: 20px;
top: 5px;
z-index: 10;
//background-color: rgb(233, 11, 11);
::v-deep .el-input__inner {
background-color: rgba(255, 255, 255, 0.0);
border: none;
}
}
}
}
</style>
<template>
<div :class="bgcolor == 'light' ? 'TimeLine2-light' : 'TimeLine2-dark'">
<div class="LeftBox">
<el-slider v-model="value" :step="1" show-stops :show-tooltip="false" :min="minValue" :max="maxValue"
:marks="marks" @change="handleTimeLineChange" v-if="timeSet.length" />
</div>
<div class="RightBox">
<el-tooltip class="box-item" effect="light" content="上一页" placement="top">
<el-icon :color="bgcolor == 'light' ? '#4a0987' : 'white'" size="30px" @click.native="handlePrePage"
class="el-icon-d-arrow-left icon-size">
</el-icon>
</el-tooltip>
<el-tooltip class="box-item" effect="light" content="播放/暂停" placement="top">
<el-icon :color="bgcolor == 'light' ? '#4a0987' : 'white'" size="30px" v-if="playSwitch"
class="el-icon-video-pause icon-size" @click.native="handlePlay">
</el-icon>
<el-icon :color="bgcolor == 'light' ? '#4a0987' : 'white'" size="30px" v-if="!playSwitch"
class="el-icon-video-play icon-size" @click.native="handlePlay">
</el-icon>
</el-tooltip>
<el-tooltip class="box-item" effect="light" content="下一页" placement="top">
<el-icon :color="bgcolor == 'light' ? '#4a0987' : 'white'" size="30px" @click.native="handleNextPage"
class="el-icon-d-arrow-right icon-size">
</el-icon>
</el-tooltip>
</div>
<div class="skin-control">
<el-icon :color="bgcolor == 'light' ? '#4a0987' : 'white'" size="30px" @click.native="skinControlSwitch"
class="el-icon-s-opportunity" :class="bgcolor == 'light' ? 'skin-control-light' : 'skin-control-dark'">
</el-icon>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//--start-#定义时间定时器指引#-->
timer: null,
//--start-#时间轴页面容量#-->
pageSize: 12,
//--start-#时间轴页面数#-->
pageCode: 1,
//--start-#时间轴初始化显示的位置(0-11)#-->
value: 0,
//--start-#时间轴最小值#-->
minValue: 0,
//--start-#时间轴最大值#-->
maxValue: 0,
//--start-#遮罩,element公司提供的技术接口#-->
marks: {},
//--start-#播放按键转换#-->
playSwitch: false,
//--start-#时间集合12节点为一组#-->
time12ArrNode: [],
zeroTimeNode: undefined,
startZero: true,
bgcolor: "light"
}
},
props: {
//--start-#父组件时间轴展示标题#-->
timeYMD: {
type: String,
default: '',
},
//--start-#时间节点集合#-->
timeSet: {
type: Array,
default: [],
},
//--start-#播放时的时间间隔#-->
second: {
type: Number,
default: 1000,
},
},
mounted() { },
computed: {},
methods: {
//--start-#处理皮肤空间切换#-->
skinControlSwitch() {
if (this.bgcolor === "dark")
this.bgcolor = "light"
else if (this.bgcolor === "light")
this.bgcolor = "dark"
},
//--start-#处理时间轴点击事件#-->
handleTimeLineChange(val) {
if (this.time12ArrNode[val] === "00:00:00") {
this.zeroTimeNode = "00:00:00";
this.$emit("handleNowValueChange", "nextDayAndZero", "00:00:00");
}
else {
if (this.zeroTimeNode === "00:00:00") {
this.zeroTimeNode = undefined;
this.$emit("handleNowValueChange", "preDayAndZero", this.time12ArrNode[val]);
}
else {
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[val]);
}
}
},
//--start-#处理播放是与否#-->
handlePlay() {
this.playSwitch = !this.playSwitch;
},
computeTime12ArrNode() {
this.time12ArrNode = Array.from(this.timeSet).slice(
(this.pageCode - 1) * this.pageSize,
this.pageCode * this.pageSize
);
},
//--start-#处理上一页#-->
handlePrePage() {
//console.log(this.zeroTimeNode);
this.startZero = false;
if (this.pageCode === 1) {
this.pageCode = Math.ceil(this.timeSet.length / this.pageSize)
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", "preDay", "22:10:00");
}
else if (this.pageCode > 1 && !this.zeroTimeNode) {
this.pageCode--;
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[0]);
if (this.time12ArrNode[0] == "00:10:00") {
this.startZero = true;
}
}
else if (this.pageCode === Math.ceil(this.timeSet.length / this.pageSize) && this.zeroTimeNode === "00:00:00") {
// this.zeroTimeNode = undefined;
this.pageCode--;
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", "preDay", "20:10:00");
}
this.zeroTimeNode = undefined;
// console.log("@@", this.zeroTimeNode);
},
//--start-#处理下一页#-->
handleNextPage() {
this.startZero = false;
//console.log(this.zeroTimeNode);
if (this.pageCode === Math.ceil(this.timeSet.length / this.pageSize) && this.zeroTimeNode === "00:00:00") {
this.pageCode = 1;
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[0]);
this.startZero = true;
}
else if (this.pageCode === Math.ceil(this.timeSet.length / this.pageSize)) {
this.pageCode = 1;
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", "nextDay", "00:10:00");
}
else if (this.pageCode < Math.ceil(this.timeSet.length / this.pageSize)) {
this.pageCode++;
this.value = 0;
this.computeTime12ArrNode();
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[0]);
}
this.zeroTimeNode === undefined;
},
handlePalyNextPage() {
this.pageCode++;
this.value = 0;
this.computeTime12ArrNode();
this.zeroTimeNode = undefined;
}
},
//--start-#监视时间轴#-->
watch: {
timeYMD(newVal, oldVal) {
if (this.playSwitch)
this.playSwitch = !this.playSwitch;
},
timeSet(newVal, oldVal) {
this.time12ArrNode = Array.from(newVal).slice(
(this.pageCode - 1) * this.pageSize,
this.pageCode * this.pageSize
);
},
time12ArrNode(newVal, oldVal) {
let obj = {};
this.time12ArrNode.forEach((item, index) => {
obj[index * 1] = item;
});
this.marks = obj;
this.maxValue = this.time12ArrNode.length - 1;
},
//--start-#深度监视#-->
deep: true,
playSwitch(newVal, oldVal) {
if (newVal) {
this.timer = setInterval(() => {
if (this.value < this.maxValue) {
//--start-#判断是不是一天的第一个节点,即00:10:00#-->
if (this.value === 0 && this.startZero) {
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[Math.floor(this.value)]);
this.startZero = false;
}
else {
this.value++;
//--start-#判断是不是一天的最后一个节点,即00:00:00#-->
if (this.time12ArrNode[Math.floor(this.value)] === "00:00:00") {
this.$emit("handleNowValueChange", "nextDayAndZero", "00:00:00");
this.zeroTimeNode = "00:00:00";
this.playSwitch = !this.playSwitch;
}
//--start-#正常范围内增长值#-->
else {
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[Math.floor(this.value)]);
}
}
}
//--start-#判断是不是每页12个时间节点的最后一个执行完毕,调用下一页生成#-->
else if (this.value === this.maxValue && this.pageCode < Math.ceil(this.timeSet.length / this.pageSize)) {
this.handlePalyNextPage();
this.$emit("handleNowValueChange", this.timeYMD, this.time12ArrNode[Math.floor(this.value)]);
}
//console.log("@2", this.value, this.maxValue, this.time12ArrNode[Math.floor(this.value)]);
}, this.second);
} else {
if (this.timer) {
clearInterval(this.timer);
}
}
}
}
}
</script>
<style lang="less" scoped>
.TimeLine2-light {
user-select: none;
width: 100%;
height: 100%;
border: 2px solid slateblue;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(10px);
display: flex;
justify-content: space-between;
align-items: center;
.LeftBox {
width: 80%;
height: 100;
padding: 0px 30px;
::v-deep .el-slider__runway {
background-color: #cccccc88;
}
::v-deep .el-slider__button {
width: 10px;
height: 10px;
background-color: transparent;
border: none;
}
::v-deep .el-slider__stop {
width: 1px;
height: 12px;
background-color: #4a0987;
z-index: 999;
display: block;
}
::v-deep .el-slider__marks-text {
color: #4a0987;
}
}
.RightBox {
width: 20%;
height: 100;
display: flex;
justify-content: space-around;
align-items: center;
}
}
.skin-control {
position: absolute;
top: 5px;
right: 5px;
}
.skin-control-light {
color: rgba(248, 17, 17, 0.5);
}
.skin-control-dark {
color: rgba(12, 35, 241, 0.5);
}
.icon-size {
font-size: 20px;
}
.TimeLine2-dark {
user-select: none;
width: 100%;
height: 100%;
border: 2px solid white;
border-radius: 10px;
background-color: rgba(41, 5, 75, .5);
backdrop-filter: blur(10px);
display: flex;
justify-content: space-between;
align-items: center;
.LeftBox {
width: 80%;
height: 100;
padding: 0px 30px;
::v-deep .el-slider__runway {
background-color: #fff3;
}
::v-deep .el-slider__button {
width: 10px;
height: 10px;
background-color: transparent;
border: none;
}
::v-deep .el-slider__stop {
width: 1px;
height: 12px;
background-color: white;
z-index: 999;
display: block;
}
::v-deep .el-slider__marks-text {
color: white;
}
}
.RightBox {
width: 20%;
height: 100;
display: flex;
justify-content: space-around;
align-items: center;
}
}
</style>
5其他说明
原文参考了