基于VUE的可以滚动的横向时间轴

这个时间轴可以播放,翻篇,进行一些配置,

HTML:

      <div class="timeline-container">
        <div class="item">
          <div
            class="list"
            v-for="(item, index) in dataChart"
            :key="item.id"
            @click="go(index, '')"
            :style="{
                width: option.itemWidth ? `${option.itemWidth}px` : `100px`,
                height:option.itemHeight ? `${option.itemHeight}px` : `auto`,
                transform: `translateX(${translateNum}px)`,
            }"
          >
            <!-- 内容部分 -->
            <div
              :class="`text ${index === currentIndex?'active':''}`"
              :style="itemStyle(index)"
              :title="item.text"
            >
              {{ item.text }}
            </div>
          </div>
        </div>
        <!-- 播放/暂停,showPlayBtn:是否显示播放按钮 -->
        <div
          class="btn-play"
          @click="play"
          :style="btnStyle"
          v-if="option.showPlayBtn"
        >
          {{option.btnPlayName||'播放/暂停'}}
        </div>
      </div>

scss:

  .timeline {
    width: 100%;
    box-sizing: border-box;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    .line {
      width: 100%;
      height: 1px;
      background: #ccc;
    }
    .timeline-container {
      width: 100%;
      position: relative;
      height: 100%;
      display: flex;
      align-items: center;
      .item {
        width: 100%;
        display: -webkit-box;
        align-items: center;
        overflow: hidden;
        margin-right: 10px;
        .list {
          text-align: center;
          cursor: pointer;
          transition: 0.5s all linear;
          display: flex;
          justify-content: center;
          .text {
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            position: relative;
          }
          .active {
            transition: 0.5s all linear;
          }
        }
        .item-prev,
        .item-next {
          width: 45px;
          height: 45px;
          border-radius: 50%;
          color: #909090;
          background-color: #e9e9e9;
          cursor: pointer;
          margin: -24px auto 0;
          display: flex;
          align-items: center;
          justify-content: center;
          border: none;
          outline: none;
          &:hover {
            color: #fff;
            background-color: red;
          }
          &.item-disabled {
            background: #666;
            cursor: no-drop;
          }
        }
      }
      .btn-play{
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }

JS:

<script>

//dataChart:数据源
export default {
  name: 'timeline',
  data() {
    return {
      currentIndex: 0,
      timer: null,
      onIsActive: true,
      translateNum: 0,
    };
  },
  watch: {
    autoPlay(val) {
      if (val) {
        this.setGo()
      } else {
        clearInterval(this.timer);
      }
    }
  },
  computed: {
    prevIndex() {
      if (!this.loop) {
        if (this.currentIndex == 0) {
          return 0;
        } else {
          return this.currentIndex - 1;
        }
      } else {
        if (this.currentIndex == 0) {
          return this.dataChart.length - 1;
        } else {
          return this.currentIndex - 1;
        }
      }
    },
    nextIndex() {
      if (!this.loop) {
        if (this.currentIndex == this.dataChart.length - 1) {
          return this.dataChart.length - 1;
        } else {
          return this.currentIndex + 1;
        }
      } else {
        if (this.currentIndex == this.dataChart.length - 1) {
          return 0;
        } else {
          return this.currentIndex + 1;
        }
      }
    },
    /**
     * 自动播放
     */
    autoPlay: {
      get() {
        return this.option.autoPlay || false
      },
      set(val) {
        this.option.autoPlay = val;
      }
    },
    /**
     * 循环
     */
    loop() {
      return this.option.loop || false
    },
    /**
     * 一次显示几个列表项
     */
    timelineNum() {
      return this.option.timelineNum || 6
    },
    /**
     * 一次滚动的宽度
     */
    timelineWidth() {
      return ((this.option.itemWidth||0)/this.timelineNum).toFixed(0)*(this.option.timelineNum||0) || 1
    },
    /**
     * 按钮样式
     */
    btnStyle() {
      let obj = {
        fontSize: this.option.btnFontSize ? `${this.option.btnFontSize}px` : '16px',
        color: this.option.btnColor || '#fff',
        width: this.option.btnWidth ? `${this.option.btnWidth}px` : 'auto',
        height: this.option.btnHeight ? `${this.option.btnHeight}px` : 'auto',
      }

      if (this.option.useBtnBgImg) {
        if (this.autoPlay) {
          obj.background = `url(${this.option.btnActiveBg}) no-repeat center/100% 100%`
        } else {
          obj.background = `url(${this.option.btnBg}) no-repeat center/100% 100%`
        }
      } else {
        if (this.autoPlay) {
          obj.backgroundColor = `${this.option.btnActiveBg || '#00FB69'}`
        } else {
          obj.backgroundColor = `${this.option.btnBg || '#22E47F'}`
        }
      }
      return obj;
    }
  },
  methods: {
    /**
     * 当前节点的发送事件
     */
    go(index, str) {
      if(this.currentIndex>index){
        str="prev"
      }else{
        str="next"
      }
      this.currentIndex = index;
      this.onIsActive = true;
      if (this.autoPlay) {
        this.setGo();
      }
      this.calcTranslateNum(str);

      const data = {
        type: 'timeLine',
        itemData: this.dataChart[index]
      };

    },
    /**
     * 自动切换方法
     * goSpeed:切换速度
     */
    setGo() {
      clearInterval(this.timer);
      this.timer = setInterval(() => {
        this.go(this.nextIndex);
        // this.calcTranslateNum("next");
      }, this.option.goSpeed || 2000);
    },
    /**
     * 滚动计算法
     */
    calcTranslateNum(str) {
      if (str === "next") {
        // timelineNum的倍数的时候就翻一次
        if (this.currentIndex % this.timelineNum == 0) {
          this.translateNum += this.timelineNum * -this.timelineWidth;
          return;
        }
        if (this.currentIndex === 0) {
          this.translateNum = 0;
          return;
        }
      } else if (str === "prev") {
        if (this.currentIndex <= this.timelineNum) {
          this.translateNum = 0;
          return;
        }
        if (this.currentIndex % this.timelineNum == 0) {
          this.translateNum -=this.timelineNum * -this.timelineWidth;
          return;
        }
      } 
    },
    /**
     * 列表项样式
     */
    itemStyle(index) {
      let obj = {
        width: '100%',
        fontSize: this.option.fontSize ? `${this.option.fontSize}px` : '16px',
        color: this.option.color || '#fff',
        marginRight: `${this.option.marginRight || 0}px`,
        padding: `${this.option.paddingTop || 0}px ${this.option.paddingRight || 0}px ${this.option.paddingDown || 0}px ${this.option.paddingLeft || 0}px`,
        boxSizing: "border-box"
      }
      if (this.option.useBgImg) {
        obj.background = `url(${index === this.currentIndex ? this.option.bgActive : this.option.bg}) no-repeat center/100% 100%`
      } else {
        obj.backgroundColor = `${index === this.currentIndex ? this.option.bgActive || '#00B7FB' : this.option.bg || '#00FB69'}`
      }
      return obj;
    },
    /**
     * 播放/暂停
     */
    play() {
      this.autoPlay = !this.autoPlay;
    },
  },
  mounted() {
    if (this.autoPlay) {
      this.setGo();
    }
  },
  beforeDestroy() {
    clearInterval(this.timer);
  },
}
</script>

调用:

<timeline :autoPlay="false" :option="option" :data="apiData"></timeline>
  new Vue({
    el: '#app',
    data() {
      return {
        option: {
          itemWidth: 30,
          itemHeight: 30,
          useBgImg:true,
          bgActive:'red',
          bg:'yellow',
          useBtnBgImg:true,
          btnActiveBg:'red',
          btnBg:'yellow',
          fontSize:10,
          color:'#00F1FB',
          timelineWidth:20,
          autoPlay:false,
          loop:true,
          goSpeed:1000,
          txtLength:2,
          btnWidth:85,
          btnHeight:30,
          showPlayBtn:false,
        },
        apiData:[
          {
            id: 1,
            text: "2020-09-28",
          },
          {
            id: 2,
            text: "2020-09-30 12:00:002020-09-28 12:00:00",
          },
          {
            id: 3,
            text: "2020-10-2 ",
          },
          {
            id: 4,
            text: "2020-10-3",
          },
          {
            id: 5,
            text: "2020-10-4",
          },
          {
            id: 6,
            text: "2020-10-5",
          },
          {
            id: 7,
            text: "2020-10-6",
          },
          {
            id: 8,
            text: "2020-10-7",
          },
          {
            id: 9,
            text: "2020-10-8",
          },
          {
            id: 10,
            text: "2020-10-9",
          },
        ]
      }
    }
  })

效果:

 

posted @ 2021-01-27 17:36  洛晨随风  阅读(7402)  评论(1编辑  收藏  举报