vue 实现点击滚动效果

效果

vue 实现点击滚动效果_第1张图片

实现

<template>
  <div class="home">
    <div class="box">
      <div class="left">
        <div class="title" :class="index === 0 ? 'index' : ''" @click="titleClick(0)">0</div>
        <div class="title" :class="index === 1 ? 'index' : ''" @click="titleClick(1)">1</div>
        <div class="title" :class="index === 2 ? 'index' : ''" @click="titleClick(2)">2</div>
        <div class="title" :class="index === 3 ? 'index' : ''" @click="titleClick(3)">3</div>
        <div class="title" :class="index === 4 ? 'index' : ''" @click="titleClick(4)">4</div>
        <div class="title" :class="index === 5 ? 'index' : ''" @click="titleClick(5)">5</div>
      </div>
      <div class="right">
        <div class="content" style="height:200px; background-color: rgb(225, 0, 100);">0</div>
        <div class="content" style="height:800px; background-color: rgb(0, 0, 0);">1</div>
        <div class="content" style="height:500px; background-color: rgb(225, 0, 20);">2</div>
        <div class="content" style="height:1000px; background-color: rgb(225, 171, 0);">3</div>
        <div class="content" style="height:700px; background-color: rgb(221, 200, 20);">4</div>
        <div class="content" style="height:400px; background-color: rgb(150, 0, 150);">5</div>
      </div>
    </div>
  </div>
</template>

<script>
import _ from 'lodash'

export default {
  data() {
    return {
      // 节流
      time: undefined,
      // 定时器
      timeout: undefined,
      // 当前高亮的 title 标志
      index: 0,
      // 右侧滚动区域 DOM
      rightDiv: undefined,
      // 右侧内容 DOM 数组
      content: undefined
    }
  },
  mounted() {
    // 获取 DOM,也可以使用 ref 获取
    this.rightDiv = document.querySelector('.right')
    this.content = document.querySelectorAll('.content')

    // 注意:避免过多的消耗计算机资源,对于这类方法应使用 节流 进行限制
    // throttle 是 lodash 提供的节流方法
    this.time = _.throttle(this.rightScroll, 100)
    // 滚动监听
    this.rightDiv.addEventListener('scroll', this.time)
  },
  destroyed() {
    // 取消定时器
    clearTimeout(this.timeout)
    // 取消节流
    this.time.cancel()
  },
  methods: {
    // 滚动监听回调
    rightScroll() {
      for (let i = 0; i < this.content.length; i++) {
        // 判断元素顶部距离父元素的距离 小于等于 父元素卷去的高度,则元素已经显示到顶部了
        if (this.content[i].offsetTop <= this.rightDiv.scrollTop) {
          this.index = i
        }
      }
      // 判断如果滚动条到底了,则高亮最后一个标题
      if (this.rightDiv.scrollTop + this.rightDiv.clientHeight === this.rightDiv.scrollHeight) {
        this.index = this.content.length - 1
      }
    },
    // 点击滚动到对应位置
    titleClick(index) {
      clearTimeout(this.timeout)
      this.index = index
      this.rightDiv.removeEventListener('scroll', this.time)

      // 滚动到指定的位置
      this.scrollToElem(this.content[index], 500)

      this.timeout = setTimeout(() => {
        this.rightDiv.addEventListener('scroll', this.time)
      }, 600)
    },
    /**
     * 滚动到指定的位置
     * @param {String} elem DOM元素
     * @param {Number} duration  滚动动画执行的时间
     */
    scrollToElem(elem, duration) {
      // 初始位置
      const startingY = this.rightDiv.scrollTop

      // 需要滚动的距离
      const diff = elem.offsetTop - startingY
      if (!diff) return

      const that = this
      // 贝赛尔曲线 (滚动动画)
      const easing = x => 1 - Math.pow(1 - x, 4)
      // 初始滚动时间
      let start
      window.requestAnimationFrame(function step(timestamp) {
        if (!start) start = timestamp
        // 计算时间的差值, 根据差值计算偏移量
        const time = timestamp - start
        let percent = Math.min(time / duration, 1)

        percent = easing(percent)
        that.rightDiv.scrollTo(0, startingY + diff * percent)

        if (time < duration) {
          window.requestAnimationFrame(step)
        }
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.home {
  display: flex;
  justify-content: center;
  padding: 20px;
}
.box {
  width: 1000px;
  height: 800px;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  display: flex;
  overflow: hidden;
  .left {
    width: 30%;
    .title {
      width: 100%;
      height: 50px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-bottom: 1px solid;
    }
    .index {
      background-color: aqua;
    }
  }
  .right {
    width: 70%;
    position: relative;
    overflow-y: auto;
    .content {
      width: 100%;
      height: 400px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      background-color: rgb(221, 20, 20);
    }
  }
}
</style>

你可能感兴趣的:(vue,vue.js,javascript,前端)