better-scroll左右联动

本文介绍了一个使用Vue.js和Better Scroll实现的音乐人列表滚动与右侧字母索引同步的技术方案。通过监听滚动事件,计算左侧列表高度,使右侧字母索引随滚动位置自动高亮显示当前字母,同时提供了触摸右侧字母快速定位到左侧相应位置的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<template>
  <div class="box">
    <div class="tab"></div>
    <div class="content-box">
      <div ref="wrapBox" class="wrapBox">
        <div class="scrollBox">
          <ul v-for="(item, i) in list" :key="i" ref="listItem">
            <h2 class="letter">{{item.letter}}</h2>
            <ul v-for="(iitem, ii) in item.letterList" :key="ii">
              <li class="name">{{iitem}}</li>
            </ul>
          </ul>
        </div>
      </div>
      <!-- 右侧导航栏 -->
      <div class="rightBar" @touchstart='onShortcutTouchstart' @touchmove.stop.prevent='onShortcuutTouchMove'>
        <span ref="letterList" :class="[currentIndex === i ?'active':'']" v-for="(item, i) in list" :key="i" :data-index='i'>{{item.letter}}</span>
      </div>
    </div>
  </div>
</template>
<style scoped>
  .box{
    background-color: #222;
  }
  .tab{
    height: 44px;
  }
  .letter{
    background-color: #ddd;
  }
  .wrapBox{
    position: fixed;
    top: 44px;
    bottom: 0;
    width: 100%;
    overflow: hidden;
  }
  .content-box{
    position: fixed;
    width: 100%;
    bottom: 0;
    overflow: hidden;
    top: 0;
    top: 44px;
  }
  .name{
    line-height: 40px;
  }
  .rightBar{
    position: absolute;
    display: flex;
    flex-direction: column;
    top: 50%;
    right: 0;
    text-align: center;
    padding: 0 20px;
    transform: translateY(-50%)
  }
  .active{
    color: red;
  }
</style>
<script>
import BScroll from 'better-scroll'
export default {
  data () {
    return {
      scrollY: -1, 
      currentIndex: 0,
      list: [
        {
          letter: '热门',
          letterList: ['薛之谦','薛之谦','薛之谦','薛之谦','薛之谦','薛之谦','薛之谦','薛之谦']
        },{
          letter: 'A',
          letterList: ['A-Lin','A-Lin','A-Lin','A-Lin','A-Lin','A-Lin','A-Lin']
        },{
          letter: 'B',
          letterList: ['本兮','本兮','本兮','本兮','本兮','本兮','本兮','本兮']
        },{
          letter: 'C',
          letterList: ['陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅','陈奕迅']
        },{
          letter: 'H',
          letterList: ['华晨宇','华晨宇','华晨宇','华晨宇','华晨宇']
        },{
          letter: 'L',
          letterList: ['林俊杰','林俊杰','林俊杰','林俊杰','林俊杰']
        },{
          letter: 'M',
          letterList: ['马伊利','马伊利','马伊利','马伊利','马伊利','马伊利','马伊利','马伊利']
        }
      ]
    }
  },
  watch: {
    list() {
      setTimeout(() => {
        this.calculateHeight()
      }, 20);
    },
    scrollY (newY) {
      const listHeight = this.listHeight
      // 当滚动到顶部,newY > 0
      if(newY > 0) {
        this.currentIndex = 0
        return
      }
      // 当在中间滚动
      for(let i=0; i<listHeight.length - 1; i++) {
        let height1 = listHeight[i]
        let height2 = listHeight[i+1]
        if(-newY >= height1 && -newY < height2) {
          this.currentIndex = i
          return
        }
      }
      // 当滚动到底部,且 -newY 大于最后一个元素的上限
      this.currentIndex = listHeight.length - 2
    }
    // 
  },
  created () {
    this.touch = {}
    this.listenScroll = true
  },
  mounted () {
    this._initScroll()
    // 这个方法可以放在watch中,左边滚动部分封装成一个组件,然后传入渲染的数据,监听这个渲染的数据,这里因为没有做组件,所以手动调用一下
    this.calculateHeight()  
  },
  methods: {
    _initScroll () {
      this.scroll = new BScroll(this.$refs.wrapBox, {
        scrollY: true,
        probeType: 3
      })
      if(this.listenScroll) {
        this.scroll.on('scroll', pos => {
          this.scrollY = pos.y
        })
      }
    },
    // 点击右侧的字母
    onShortcutTouchstart (e) {
      let anchorIndex = e.target.getAttribute('data-index')  //获取右侧索引
      // 点击右侧的字母栏,左边跳转到对应的字母
      // this.$refs.wrapBox.scrollToElement(this.$refs.listItem[anchorIndex], 0)
      this.scrollToElement(this.$refs.listItem[anchorIndex], 0)
      // 点击高亮
      this.scrollY = -this.listHeight[anchorIndex]
      // 滚动右侧字母
      let firstTouch = e.touches[0]
      this.touch.y1 = firstTouch.pageY
      this.touch.anchorIndex = anchorIndex
    },
    // 滚动右侧的字母
    onShortcuutTouchMove (e) {
      let ANCHOR_HEIGHT = 21  //右侧每个字母的高度
      let firstTouch = e.touches[0]
      this.touch.y2 = firstTouch.pageY
      let delta = Math.floor((this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT)
      let anchorIndex = parseInt(this.touch.anchorIndex) + delta
      this.scrollToElement(this.$refs.listItem[anchorIndex], 0)
      // 实现点击高亮
      // 这里的anchorIndex可能超过listHeight的范围而导致 this.listHeight[anchorIndex]  NaN 所以这里要对 anchorIndex 添加一层校验
      if(!this.listHeight[anchorIndex] && anchorIndex !== 0) {
        return
      }
      this.scrollY = -this.listHeight[anchorIndex]
    },
    // 计算左边每一块字母的高度,使得左边的滚动右边对应字母高亮
    calculateHeight () {
      this.listHeight = []
      const list = this.$refs.listItem
      let height = 0
      this.listHeight.push(height)
      for(let i=0; i<list.length; i++) {
        let item = list[i]
        height += item.clientHeight
        this.listHeight.push(height)
      }
    },
    scrollTo () {
      this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
    },
    scrollToElement () {
      this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
    }
  }
}
</script>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值