当前位置:   article > 正文

Vue.js 实现时间轴功能_vue 时间轴

vue 时间轴

GitHub

时间轴组件封装

Main.js

<template>
  <div class="timeline-main">
    <div class="timeline-axis">
      <div class="axis-item"
        v-for="(time, index) in dateTimes"
        :key="index">
        <div class="axis-item-tick"
          :class="{ 'axis-item-tick-active': index === highlightIndex }"
          @mouseenter="hoverIndex = index"
          @mouseleave="hoverIndex = -1"
          @click="tickClick(time, index)">
        </div>
        <div class="axis-item-label"
          v-if="dateTimeIndexes.indexOf(index) >= 0">
          {{ time }}</div>
        <div class="axis-item-tip"
          v-if="index === highlightIndex || index === hoverIndex">
          {{ time }}</div>
      </div>
    </div>
    <div class="timeline-control">
      <i class="menu-icon icon-left"
        :class="{'menu-icon-disabled': playing}"
        @click="backward"></i>
      <i class="menu-icon"
        :class="{'icon-play': !playing, 'icon-pause': playing}"
        @click="togglePlay"
        @mouseleave="hoverIndex = -1"></i>
      <i class="menu-icon icon-right"
        :class="{'menu-icon-disabled': playing}"
        @click="forward"></i>
      <i class="menu-icon icon-up"
        :class="{'menu-icon-disabled': playing}"
        @click="speedSlow"></i>
      <i
        class="menu-icon speed">{{ options.speed }}</i>
      <i class="menu-icon icon-down"
        :class="{'menu-icon-disabled': playing}"
        @click="speedQuick"></i>
    </div>
  </div>
</template>
<script>
import { dateFormat } from '../util/formatdate.js' // 日期格式化
export default {
  data() {
    return {
      intervalTimer: null, // 定时器
      dateTimeIndexes: [], // 日期列表
      playing: false, // 播放
      activeIndex: 0, // 当前的时间位置
      hoverIndex: 0 // 鼠标移入的时间位置
    }
  },
  props: {
    options: {
      type: Object,
      default() {
        return {}
      }
    },
    dateTimes: {
      type: Array,
      default() {
        return []
      }
    },
    interval: {
      type: Number,
      default() {
        return 100
      }
    }
  },
  computed: {
    highlightIndex() {
      return (
        (this.activeIndex === -1 && this.dateTimes.length - 1) ||
        this.activeIndex
      )
    }
  },
  watch: {
    options: {
      handler() {
        this.renderTimeline()
      },
      deep: true
    },
    playing() {
      if (this.playing) {
        this.intervalTimer = setInterval(() => {
          this.activeIndex = (this.activeIndex + 1) % this.dateTimes.length
        }, this.options.speed * 1000)
      } else {
        if (this.intervalTimer) {
          clearInterval(this.intervalTimer)
          this.intervalTimer = null
        }
      }
    },
    activeIndex() {
      const time = this.dateTimes[this.activeIndex].split(' ')[0]
      this.$emit('getDateFun', time)
    }
  },
  mounted() {
    this.renderTimeline()
    let that = this
    window.onresize = function () {
      that.renderTimeline()
    }
  },
  filters: {
    formatDatetime(dateTime) {
      dateTime = dateFormat(dateTime, 'MM.dd')
      return dateTime
    }
  },
  methods: {
    /**
     * @name: 初始化时间轴
     */
    renderTimeline() {
      // 时间轴的宽度
      const timelineWidth = this.$el.offsetWidth - 40
      // 日期个数
      const dateTimesSize = this.dateTimes.length
      // 如果时间全部显示,时间轴的理想宽度
      const dateTimesWidth = dateTimesSize * this.interval
      // 如果时间轴的宽度小于理想宽度
      if (timelineWidth >= dateTimesWidth) {
        this.dateTimeIndexes = this.dateTimes.map((dateTime, index) => {
          return index
        })
        return
      }
      // 当前时间轴的宽度最大能容纳多少日期刻度
      const maxTicks = Math.floor(timelineWidth / this.interval)
      // 间隔刻度数
      const gapTicks = Math.floor(dateTimesSize / maxTicks)
      // 记录需要显示的日期索引
      this.dateTimeIndexes = []
      for (let t = 0; t <= maxTicks; t++) {
        this.dateTimeIndexes.push(t * gapTicks)
      }
      const len = this.dateTimeIndexes.length
      // 最后一项需要特殊处理
      if (len > 0) {
        const lastIndex = this.dateTimeIndexes[len - 1]
        if (lastIndex + gapTicks > dateTimesSize - 1) {
          this.dateTimeIndexes[len - 1] = dateTimesSize - 1
        } else {
          this.dateTimeIndexes.push(dateTimesSize - 1)
        }
      }
    },

    /**
     * @name: 点击刻度
     * @param {time}
     * @param {index}
     */
    tickClick(time, index) {
      if (this.playing) {
        return
      }
      this.activeIndex = index
    },

    /**
     * @name: 播放和暂停
     */
    togglePlay() {
      this.playing = !this.playing
    },

    /**
     * @name: 时间退后一日
     */
    backward() {
      if (this.playing) {
        return
      }
      this.activeIndex = this.activeIndex - 1
      if (this.activeIndex === -1) {
        this.activeIndex = this.dateTimes.length - 1
      }
    },

    /**
     * @name: 时间前进一日
     */
    forward() {
      if (this.playing) {
        return
      }
      this.activeIndex = (this.activeIndex + 1) % this.dateTimes.length
    },

    /**
     * @name: 减慢速度
     */
    speedSlow() {
      if (this.playing || this.options.speed >= this.options.speedMax) {
        return
      }
      this.options.speed = this.options.speed + 1
    },

    /**
     * @name: 加快速度
     */
    speedQuick() {
      if (this.playing || this.options.speed <= 1) {
        return
      }
      this.options.speed = this.options.speed - 1
    }
  }
}
</script>
<style scoped lang="scss">
.timeline-main {
  padding: 10px;
  box-sizing: border-box;
  .timeline-axis {
    position: relative;
    display: flex;
    justify-content: space-around;
    padding: 8px 0;
    &::before {
      content: '';
      width: 100%;
      height: 10px;
      position: absolute;
      left: 0;
      bottom: 8px;
      display: inline-block;
      background: rgba(0, 0, 0, 0.5);
    }
    .axis-item {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      .axis-item-tick {
        display: inline-block;
        width: 4px;
        height: 20px;
        background: rgba(0, 0, 0, 0.5);
        transition: background 0.3s;
        cursor: pointer;
        &:hover {
          background: #000;
        }
      }
      .axis-item-tick-active {
        background: #000;
      }
      .axis-item-label {
        position: absolute;
        bottom: -30px;
        white-space: nowrap;
      }
      .axis-item-tip {
        position: absolute;
        top: -25px;
        padding: 2px 6px;
        border-radius: 2px;
        background: rgba(0, 0, 0, 0.5);
        white-space: nowrap;
        color: #fff;
      }
    }
  }
  .timeline-control {
    margin-top: 40px;
    text-align: center;
    i {
      cursor: pointer;
      display: inline-block;
      font-style: normal;
    }
    .menu-icon {
      font-size: 20px;
      width: 20px;
      height: 20px;
      background-size: cover;
      background-repeat: no-repeat;
      &.icon-left {
        background-image: url('../assets/icon-left.png');
      }

      &.icon-right {
        background-image: url('../assets/icon-right.png');
      }

      &.icon-play {
        background-image: url('../assets/icon-play.png');
      }

      &.icon-pause {
        background-image: url('../assets/icon-pause.png');
      }
      &.icon-up {
        background-image: url('../assets/icon-up.png');
      }

      &.icon-down {
        background-image: url('../assets/icon-down.png');
      }
      &.menu-icon-disabled {
        cursor: no-drop;
        opacity: 0.5;
      }
    }
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320

使用组件

App.vue

<template>
  <div>
    <h2
      style="margin:0;text-align:center;">
      {{this.date}}
    </h2>
    <Main :options="options"
      :dateTimes="dateTimes"
      @getDateFun="getDateFun"
      :interval="interval"></Main>
  </div>
</template>

<script>
import { dateFormat } from './util/formatdate.js'
import Main from './components/Main'
export default {
  name: 'app',
  data() {
    return {
      date: '',
      options: {
        speed: 1, // 速度
        speedMax: 10 // 速度最大值
      },
      interval: 20, // 日期间的间隔
      dateTimes: [
        '03-04',
        '03-05',
        '03-06',
        '03-07',
        '03-08',
        '03-09',
        '03-10',
        '03-11',
        '03-12',
        '03-13'
      ]
    }
  },
  components: {
    Main
  },
  mounted() {
    // 获取最近 10 天的日期
    let list = []
    for (let i = 0; i < 10; i++) {
      list.unshift(
        dateFormat(
          new Date(
            new Date().setDate(new Date().getDate() - i)
          ).toLocaleDateString(),
          'MM-dd'
        )
      )
    }
    this.date = list[0]
    this.dateTimes = list
  },
  methods: {
    // 接收父组件传值
    getDateFun(time) {
      console.log(time)
      this.date = time
    },
  }
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/489504
推荐阅读
相关标签