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代码

html 复制代码
<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>
html 复制代码
<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其他说明

原文参考了

相关推荐
就爱学编程7 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
wakangda18 分钟前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡21 分钟前
lodash常用函数
前端·javascript
Oneforlove_twoforjob30 分钟前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
emoji11111131 分钟前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼33 分钟前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_7482500338 分钟前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
每天都要学信号42 分钟前
Python(第一天)
开发语言·python
TENET信条42 分钟前
day53 第十一章:图论part04
开发语言·c#·图论
一个处女座的程序猿O(∩_∩)O43 分钟前
vue3 如何使用 mounted
前端·javascript·vue.js