前端好用的时间轴组件,支持滚动、点击、日期选择、时间刻度间隔【纯js手写】

280 阅读7分钟

先上效果图

具体功能:

  • 支持滚动、点击、日期选择、时间刻度间隔等 顶顶顶顶的点点滴滴.png

全部代码如下

<template>
  <div class="yxkz-page-timer">
    <div class="left-box">
      <div class="left-bg" @click="prevTime">
        <img src="./timepanel/img/prev.png" />
      </div>
      <div class="left-bg" @click="playTime">
        <img
          v-show="slidertimerStatus == false"
          src="./timepanel/img/play.png"
        />
        <img
          v-show="slidertimerStatus == true"
          src="./timepanel/img/pause.png"
        />
      </div>
      <div class="left-bg" @click="nextTime">
        <img src="./timepanel/img/next.png" />
      </div>
    </div>
    <div
      class="time-container"
      :style="{ width: (datetimes.length - 1) * 6 + 'px' }"
    >
      <div
        class="time-line"
        :style="{ width: (datetimes.length - 1) * 6 + 'px' }"
      ></div>
      <div class="time-progress" :style="{ width: timeIndex * 6 + 'px' }"></div>
      <div
        class="time-ceng"
        :style="{ width: (datetimes.length - 1) * 6 + 'px' }"
        @click="handleSelectTime"
        @mousemove="onMousemove"
      ></div>
      <div
        class="time-label"
        :style="{ width: (datetimes.length - 1) * 6 + 'px' }"
      >
        <span v-for="(item, index) in datetimes" :key="index" class="label-box">
          <div class="label-line" v-if="item.labelName != null"></div>
          <span class="label-name">{{ item.labelName }}</span>
        </span>
      </div>

      <span
        class="time-btn"
        :style="{ left: timeIndex * 6 - 13 + 'px' }"
        @mousedown="onMousedown"
        @mouseup="onMouseup"
      >
        <span class="time-btn-tip">{{
          datetimes.length == 0 ? "" : datetimes[timeIndex].showTime
        }}</span>
      </span>
    </div>

    <div class="right-box">
      <div class="step-box" style="position: relative">
        <div class="step-name">间隔:</div>
        <select
          v-model="stepValue"
          class="step-select"
          @change="handleTimeStepChange"
        >
          <option
            style="height: 20px; line-height: 20px"
            v-for="item in timeleapList"
            :value="item.value"
            :key="item.value"
          >
            {{ item.label }}
          </option>
        </select>
      </div>

      <DatePicker
        :open="open"
        :value="datetime"
        :options="options"
        class="datetimepick"
        type="date"
        @on-change="handleDatetimeChange"
      >
        <a
          href="javascript:void(0)"
          @click="handleOpenDateOption"
          class="datetimelink"
        >
          <template v-if="datetime === ''">选择时间</template>
          <template v-else>{{ datetime }}</template>
          <Icon type="md-arrow-dropdown" />
        </a>
      </DatePicker>
    </div>
  </div>
</template>
<script type="text/javascript">
import { DatePicker, Icon, Select, Option, Checkbox } from "iview";

//TODO:获取数据最新时间
import { getLatestTime } from "@/api/radar.js";
import { setTimeout, clearTimeout, setInterval, clearInterval } from "timers";

/**
 * @description: 请求当前最新数据的时间 //TODO:暂时不对接借口
 * @param {*}
 * @return {*}
 */
export default {
  components: {
    DatePicker,
    Icon,
    Select,
    Option,
    Checkbox
  },
  // props: ["date"],
  // watch: {
  //   date(val) {},
  // },
  data() {
    return {
      isDragging: false, // 是否滑动
      timeIndex: 36, // 当前时间轴下标
      datetimes: [], // 时间轴的时间列表
      stepValue: 1, // 时间间隔
      timeleapList: [
        { value: 1, label: "10min" },
        { value: 3, label: "30min" },
        { value: 6, label: "1h" },
        { value: 18, label: "3h" }
      ],
      open: false, // 打开日期面板
      options: {
        // 日历插件快捷选取
        shortcuts: [
          {
            text: "今天",
            value() {
              return new Date();
            }
          }
        ]
      },
      datetime: "", // 当前选中的日期
      //TODO:自动播放
      slidertimerStatus: false, // 是否开始播放
      slidertimerInterval: null, // 定时器对象
      speed: 2500, // 自动播放的速度
    };
  },
  beforeMount() {},
  mounted() {
    let a = new Date(new Date().getTime() - 24 * 3600000);
    let starttime = new Date(new Date(a).format("yyyy-MM-dd hh:00:00"));
    let endtime = new Date(new Date().format("yyyy-MM-dd hh:mm:00"));
    this.datetime = new Date().format("yyyy-MM-dd");
    this.initDate(starttime, endtime, 1000 * 60 * 10, 0);

    setInterval(() => {
      this.autoRefresh();
    }, 1000 * 60 * 5);
  },
  methods: {
    autoRefresh() {
      console.log('每隔5分钟时间轴刷新', new Date(new Date().format("yyyy-MM-dd hh:mm:00")))
      let a = new Date(new Date().getTime() - 24 * 3600000);
      let starttime = new Date(new Date(a).format("yyyy-MM-dd hh:00:00"));
      let endtime = new Date(new Date().format("yyyy-MM-dd hh:mm:00"));
      this.datetime = new Date().format("yyyy-MM-dd");
      this.initDate(starttime, endtime, 1000 * 60 * 10, 0);
    },
    // 根据日期加载时间轴时间列表
    initDate(startTime, endTime, step, initnew) {
      var _datetimes = [];
      for (var i = startTime.getTime(); i <= endTime.getTime(); i = i + step) {
        let t = new Date(i);
        let item = this.dateConversion(t);
        _datetimes.push(item);
      }

      if (new Date().format("yyyy-MM-dd") == this.datetime) {
        this.timeIndex = _datetimes.length - 1;
      } else {
        this.timeIndex = 0;
      }
      console.log("根据日期加载时间轴时间列表", _datetimes);
      this.datetimes = _datetimes;
      this.handleTimeLineIndex()
    },

    // 时间处理
    dateConversion(item) {
      var chinaStandard = item;
      var date = new Date(chinaStandard);
      var y = date.getFullYear();
      var m = date.getMonth() + 1;
      m = m < 10 ? "0" + m : m;
      var d = date.getDate();
      d = d < 10 ? "0" + d : d;
      var h = date.getHours();
      h = h < 10 ? "0" + h : h;
      var minute = date.getMinutes();
      minute = minute < 10 ? "0" + minute : minute;
      let showTime = d + "日" + h + "时" + minute + "分"; //这里如果不需要小时 分  后边的可以不需要拼接
      let labelTime = y + "-" + m + "-" + d + " " + h + ":" + minute + ":00";
      let kItem = {
        showTime: showTime,
        labelTime: labelTime,
        labelName: minute == "00" ? date.getHours() : null,
        status: 1
      };
      return kItem;
    },

    // 时间发生变化
    handleTimeLineIndex() {
      console.log('时间轴变化的时间0000000', this.timeIndex)
      console.log('时间轴变化的时间2222222', this.datetimes[this.timeIndex].labelTime)
      this.$emit("changeDateTime", this.datetimes[this.timeIndex].labelTime);
    },

    // 单击时间轴
    handleSelectTime(e) {
      console.log("单击时间轴", e);
      let offserWidth = e.offsetX;
      this.timeIndex = Math.ceil(offserWidth / 6);
      console.log("单击时间轴--timeIndex", this.timeIndex);
      this.handleTimeLineIndex()
    },

    // 鼠标按下
    onMousedown() {
      console.log("鼠标按下");
      this.isDragging = true;
    },

    // 鼠标松开
    onMouseup() {
      console.log("鼠标松开");
      this.isDragging = false;
      this.handleTimeLineIndex()
    },

    // 鼠标移动
    onMousemove(e) {
      if (this.isDragging == true) {
        console.log("按钮左右滑动", e);
        let offserWidth = e.offsetX;
        this.timeIndex = Math.ceil(offserWidth / 6);
        console.log("按钮左右滑动--timeIndex", this.timeIndex);
      }
    },

    // 上一刻时间
    prevTime() {
      console.log("上一刻时间", this.timeIndex);
      if (this.stepValue == 1) {
        if (this.timeIndex > 0) {
          this.timeIndex--;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 3) {
        if (this.timeIndex >= 3) {
          this.timeIndex = this.timeIndex - this.stepValue;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 6) {
        if (this.timeIndex >= 6) {
          this.timeIndex = this.timeIndex - this.stepValue;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 18) {
        if (this.timeIndex >= 18) {
          this.timeIndex = this.timeIndex - this.stepValue;
          this.handleTimeLineIndex()
        }
      }
    },
    // 开始播放或者暂停
    playTime() {
      this.slidertimerStatus = !this.slidertimerStatus;
      clearInterval(this.slidertimerInterval);
      if (this.slidertimerStatus == true) {
        this.slidertimerInterval = setInterval(() => {
          if (this.stepValue == 1) {
            if (this.timeIndex < this.datetimes.length - 1) {
              this.timeIndex++;
            } else {
              this.timeIndex = 0
            }
          } else if (this.stepValue == 3) {
            if (this.timeIndex < this.datetimes.length - 3) {
              this.timeIndex = this.timeIndex + this.stepValue;
            } else if (this.timeIndex >= this.datetimes.length - 3 && this.timeIndex < this.datetimes.length - 1) {
              this.timeIndex = this.datetimes.length - 1
            } else {
              this.timeIndex = 0
            }
          } else if (this.stepValue == 6) {
            if (this.timeIndex < this.datetimes.length - 6) {
              this.timeIndex = this.timeIndex + this.stepValue;
            } else if (this.timeIndex >= this.datetimes.length - 6 && this.timeIndex < this.datetimes.length - 1) {
              this.timeIndex = this.datetimes.length - 1
            } else {
              this.timeIndex = 0
            }
          } else if (this.stepValue == 18) {
            if (this.timeIndex < this.datetimes.length - 18) {
              this.timeIndex = this.timeIndex + this.stepValue;
            } else if (this.timeIndex >= this.datetimes.length - 18 && this.timeIndex < this.datetimes.length - 1) {
              this.timeIndex = this.datetimes.length - 1
            } else {
              this.timeIndex = 0
            }
          }
          this.handleTimeLineIndex()
        }, this.speed);
      }
    },
    // 下一刻时间
    nextTime() {
      console.log("下一刻时间", this.timeIndex);
      if (this.stepValue == 1) {
        if (this.timeIndex < this.datetimes.length - 1) {
          this.timeIndex++;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 3) {
        if (this.timeIndex < this.datetimes.length - 3) {
          this.timeIndex = this.timeIndex + this.stepValue;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 6) {
        if (this.timeIndex < this.datetimes.length - 6) {
          this.timeIndex = this.timeIndex + this.stepValue;
          this.handleTimeLineIndex()
        }
      } else if (this.stepValue == 18) {
        if (this.timeIndex < this.datetimes.length - 18) {
          this.timeIndex = this.timeIndex + this.stepValue;
          this.handleTimeLineIndex()
        }
      }
    },

    // 选择时间间隔
    handleTimeStepChange(event) {
      console.log("选中的时间间隔", event);
      this.stepValue = parseInt(event.target.value);
    },
    // 打开日期面板
    handleOpenDateOption() {
      this.open = !this.open;
    },

    // 选中日期
    handleDatetimeChange(date) {
      console.log("选择的日期", date);
      this.open = false;

      if (new Date().format("yyyy-MM-dd") == date) {
        console.log("选择的日期-----当天", date);
        let a = new Date(new Date().getTime() - 24 * 3600000);
        let starttime = new Date(new Date(a).format("yyyy-MM-dd hh:00:00"));
        let endtime = new Date(new Date().format("yyyy-MM-dd hh:mm:00"));
        this.datetime = new Date().format("yyyy-MM-dd");
        this.initDate(starttime, endtime, 1000 * 600, 0);
      } else {
        console.log("选择的日期---不是当天的", date);
        let starttime = new Date(date + " 00:00:00");
        let endtime = new Date(
          new Date(starttime.format("yyyy-MM-dd 00") + ":00:00").getTime() +
            24 * 3600000
        );
        this.datetime = starttime.format("yyyy-MM-dd");
        this.initDate(starttime, endtime, 1000 * 600, 1);
      }
    }
  },
  beforeDestroy() {
    clearInterval(this.slidertimerInterval);
  }
};
</script>
<style lang="scss" scoped>
.yxkz-page-timer {
  position: absolute;
  z-index: 600;
  left: 20%;
  bottom: 50px;
  opacity: 0.9;
  border-radius: 6px;
  display: flex;
  align-items: center;
  padding: 2px 20px;
  padding-left: 10px;
  border-radius: none;

  .left-box {
    width: 100px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-right: 20px;

    .left-bg {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 23px;
      width: 23px;
      background: #013a67;
      border: 1px solid #02a2fa;
      border-radius: 50%;
      cursor: pointer;
    }
  }

  .time-container {
    position: relative;
    width: 100%;
    height: 8px;
    display: flex;
    justify-content: space-between;
    background: #cff;
    border: 1px solid #000;
    border-radius: 3px;

    .time-line {
      width: 100%;
      height: 100%;
      border-radius: 3px;
      z-index: 1001;
    }

    .time-progress {
      position: absolute;
      left: 0;
      top: 0;
      height: 100%;
      border-radius: 3px;
      background: #00d8ff;
    }

    .time-ceng {
      position: absolute;
      left: 0;
      top: 0;
      height: 35px;
      cursor: pointer;
      z-index: 1002;
    }

    .time-label {
      position: absolute;
      left: 0;
      top: 8px;
      display: flex;
      justify-content: space-between;
      cursor: pointer;

      .label-box {
        width: 6px;
        display: flex;
        flex-direction: column;
        align-items: center;
        margin-left: -3px;

        .label-line {
          width: 1px;
          height: 5px;
          background: #00d8ff;
        }

        .label-name {
          color: #44a8f6;
        }
      }
    }

    .time-btn {
      position: absolute;
      top: -9px;
      width: 26px;
      height: 26px;
      border: 8px solid #fff;
      border-radius: 50%;
      background: #016cee;
      box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.75);
      z-index: 1010;

      &:hover {
        cursor: pointer;

        .time-btn-tip {
          top: -30px;
        }
      }

      .time-btn-tip {
        position: absolute;
        opacity: 1;
        top: -33px;
        left: -42px;
        width: 92px;
        text-align: center;
        border: none;
        border-radius: 9px;
        background: #013a67;
        -webkit-box-shadow: 0 2px 3px 0 rgba(35, 64, 94, 0.24);
        box-shadow: 0 2px 3px 0 rgba(35, 64, 94, 0.24);
        color: #fff;
        border: 1px solid #02a2fa;

        // &::before {
        //   border-top-color: #02a2fa;
        // }

        &::after {
          content: " ";
          width: 0;
          height: 0;
          border: 5px solid hsla(0, 0%, 100%, 0);
          border-top-color: #02a2fa;
          position: absolute;
          bottom: -10px;
          left: 50%;
          margin-left: -5px;
        }
      }
    }
  }

  .right-box {
    margin-left: 20px;
    margin-top: 20px;

    .step-box {
      display: flex;
      width: 105px;
      height: 20px;
      border-radius: 5px;
      border: 1px solid #02a2fa;
      background: #013a67;

      .step-name {
        color: #fff;
        background: #004747;
        padding-left: 10px;
      }

      .step-select {
        border: 0;
        color: #fff;
        background: #013a67;
      }
    }
  }

  .right-box .datetimepick .datetimelink {
    margin-top: 2px;
    display: block;
    height: 20px;
    width: 105px;
    color: #013a67;
    text-align: center;
    line-height: 20px;
    background: #013a67;
    color: #fff;
    display: flex;
    justify-content: space-between;
    padding-left: 12px;
    border: 1px solid #00d8ff;
    border-radius: 5px;
    margin-bottom: 5px;
  }
  .right-box .datetimepick .datetimelink i {
    width: 22px;
    height: 22px;
    display: flex;
    justify-content: center;
    align-items: center;
    // background-color: #38DE98;
  }
  .right-box .datetimepick .datetimelink i::before {
    content: "";
    width: 8px;
    height: 8px;
    border: 2px solid #00d8ff;
    border-top: 0;
    border-left: 0;
    transform: rotate(45deg) scale(1);
    transition: all 0.2s ease-in-out;
    position: absolute;
    top: 5px;
  }
  .right-box .datetimepick /deep/.ivu-date-picker-cells {
    margin: 0;
  }
  .right-box .datetimepick /deep/ .ivu-date-picker-cells-header span {
    color: #333;
    color: #fff;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-select-dropdown
    span.ivu-date-picker-cells-cell {
    height: 24px;
  }
  .right-box .datetimepick /deep/ .ivu-picker-panel-body {
    background: #032155;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-date-picker-header
    .ivu-date-picker-header-label {
    color: #fff;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span {
    color: #2571a2;
  }
  .right-box .datetimepick /deep/ .ivu-picker-panel-body span > em {
    &:hover {
      background: #2d8cf0;
      color: #fff;
    }
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span.ivu-date-picker-cells-focused
    > em {
    background: #2d8cf0;
    color: #fff !important;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span.ivu-date-picker-cells-cell-next-month
    > em {
    color: #16415d;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span.ivu-date-picker-cells-cell-next-month
    > em {
    &:hover {
      color: #fff;
    }
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span.ivu-date-picker-cells-cell-prev-month
    > em {
    color: #16415d;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    .ivu-picker-panel-content
    span.ivu-date-picker-cells-cell-prev-month
    > em {
    &:hover {
      color: #fff;
    }
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    span.ivu-date-picker-cells-cell-disabled {
    background: #0b3a7e;
    color: #c0c4cc;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    span.ivu-date-picker-cells-cell-disabled
    > em {
    background: #0b3a7e;
    color: #c0c4cc !important;
  }
  .right-box
    .datetimepick
    /deep/
    .ivu-picker-panel-body
    span.ivu-date-picker-cells-cell-disabled
    > em {
    &:hover {
      background: #0b3a7e;
      color: #c0c4cc !important;
    }
  }
}
</style>