vue里面实现字母搜索栏,左侧浏览到那个首字母处,右侧首字母高亮

 效果:红色位置代码

 

 

实现功能描述:

左侧内容滚动的时候,右侧首字母会变换选中,右侧选中指定首字母,左侧内容滚到指定位置。

组件game_list

<template>
  <div>
    <div class="game_list">
      <div class="left_list_box" ref="scrollBox" @scroll="contentScroll">
        <div
          v-for="(key, index) in Object.keys(gamesObject).sort()"
          :key="index"
          :id="key"
          :ref="key"
        >
          <div class="title_name">{{ key }}</div>
          <div class="left_list_game">
            <div
              class="game_item"
              v-for="(item, _index) in gamesObject[key]"
              :key="_index"
              :class="$attrs['letter-hidden'] ? 'letter_hidden' : ''"
            >
              <van-image :src="item.Icon" width="1.12rem" height="1.12rem"></van-image>
              <p :class="$attrs['letter-hidden'] ? 'show_oneline' : ''">{{ item.Name }}</p>
            </div>
          </div>
        </div>
      </div>
      <div class="right_word_box" v-if="!$attrs['letter-hidden']" @scroll="menuScroll">
        <div ref="rightBox" :style="{ top: rightScrollTop + 'px' }">
          <div
            class="letter add_margin_top"
            :class="isChoose == '热' ? 'is_choose' : ''"
            @click="toLetter('热')"
          >
            热
          </div>
          <div
            class="letter"
            v-for="(letter, index) in firstLetter.split('')"
            :key="index"
            :class="isChoose == letter ? 'is_choose' : ''"
            @click="toLetter(letter)"
            :ref="'menu_' + letter"
          >
            {{ letter }}
          </div>
        </div>
      </div>
    </div>
    <!-- <empty-page></empty-page> -->
  </div>
</template>

<script>
import { nextTick } from 'vue'
import EmptyPage from '../../components/Empty/EmptyPage.vue'
export default {
  name: '',
  props: {
    gameList: {
      type: Array
    }
  },
  data() {
    return {
      // 处理后的游戏列表
      gamesObject: {},
      firstLetter: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#',
      isChoose: '热',
      clickMenu: false, //点击菜单时候
      rightScrollTop: 200 //右侧菜单的滚动距离
    }
  },
  components: { EmptyPage },
  watch: {
    gameList(newVal) {
      let arr = newVal.concat()
      arr.reverse()
      let tempName = ''
      //重构列表结构
      arr.forEach((game, index) => {
        if (index == 0) {
          this.$set(this.gamesObject, game.NameInitials.toUpperCase(), [])
          tempName = game.NameInitials.toUpperCase()
        }
        if (tempName != game.NameInitials.toUpperCase()) {
          this.$set(this.gamesObject, game.NameInitials.toUpperCase(), [])
        }
        this.gamesObject[game.NameInitials.toUpperCase()].push(game)
      })
    },
    // 选中的首字母监听用于右侧首字母栏的滚动
    isChoose(newVal) {
      // 获取内容盒子到头部的距离
      let headerHeight = this.$refs['rightBox'].offsetTop
      // 当前点击字母的距离右侧菜单框的距离
      let chooseTop = this.$refs['menu_' + newVal][0].offsetTop - headerHeight
      // 获取右侧菜单盒子可见高度
      let rightMenuHeight = this.$refs['scrollBox'].offsetHeight
      // 右侧菜单内容实际高度
      let rightRealHeight = this.$refs['rightBox'].offsetHeight
      // 如果当前点击的字母的位置占据了rightMenuHeight高后四分之一的位置。就让右侧菜单网上滚一个二分之一rightMenuHeight的高度
      if (chooseTop >= rightMenuHeight * (3 / 4)) {
        //让右侧菜单往上滚动1/2rightMenuHeight后没有到达右侧菜单的实际高度
        if (this.rightScrollTop + rightMenuHeight * (3 / 2) < rightRealHeight) {
          console.log('执行')
          // 继续向上滚动
          nextTick(() => {
            this.rightScrollTop = this.rightScrollTop + rightMenuHeight * (3 / 2)
          })
        } else {
          //让右侧菜单往上滚动1/2rightMenuHeight过程中会打到达右侧菜单的实际高度,为最后一下的滚动
          // debugger
          nextTick(() => {
            this.rightScrollTop =
              this.rightScrollTop + rightRealHeight - this.rightScrollTop - rightMenuHeight
          })
        }
      }
    }
  },
  created() {},
  mounted() {},
  methods: {
    toLetter(letter) {
      this.isChoose = letter
      this.clickMenu = true
      const point = document.getElementById(letter)
      point &&
        point.scrollIntoView({
          block: 'start',
          behavior: 'smooth'
        })
      // 设置滚动之后过一段时间恢复clickMenu字段,避免自定义滚动操作和点击后滚动同时出现
      setTimeout(() => {
        this.clickMenu = false
      }, 1000)
    },
    // 监听内容盒子的滚动事件
    contentScroll(e) {
      // 判断是不是鼠标点击菜单导致滚动
      if (!this.clickMenu) {
        // 记录内容滚动距离
        let scrollTop = e.srcElement.scrollTop
        // 获取内容盒子到头部的距离
        let headerHeight = this.$refs['scrollBox'].offsetTop
        //  根据滚动值,查询在哪两个字母的区间内
        for (let i = 0; i < this.firstLetter.length - 1; i++) {
          // 首个字母出现的位置
          let letter_before = 0
          // 第二个字母出现的位置
          let letter_after = null
          if (this.$refs[this.firstLetter.split('')[i]]) {
            letter_before = this.$refs[this.firstLetter.split('')[i]][0].offsetTop - headerHeight
          }
          if (i != this.firstLetter.length - 1 && this.$refs[this.firstLetter.split('')[i + 1]]) {
            letter_after = this.$refs[this.firstLetter.split('')[i + 1]][0].offsetTop - headerHeight
          }
          // 鼠标滚动一次是100
          if (scrollTop + 100 >= letter_before && scrollTop + 100 < letter_after) {
            this.isChoose = this.firstLetter.split('')[i]
            break
          }
        }
      }
    },
    // 监听右侧菜单盒子滚动事件
    menuScroll(e) {
      this.rightScrollTop = e.srcElement.scrollTop
    }
  }
}
</script>

<style lang="scss" scoped>
.game_list {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
// 左侧游戏列表
.left_list_box {
  flex: 1;
  height: calc(100vh - 0.92rem);
  overflow-y: scroll;
  padding: 0 0.14rem;
  padding-bottom: 1.08rem;
  box-sizing: border-box;
  .title_name {
    font-size: 0.28rem;
    color: #111;
    font-weight: bold;
    padding: 0.32rem 0.3rem;
  }
  .left_list_game {
    overflow: hidden;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-content: flex-start;
    border-bottom: 0.02rem solid #edeef1;
    padding-bottom: 0.34rem;

    .game_item {
      width: 1.48rem;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin: 0 0.06rem 0.2rem;
      height: max-content;
      p {
        width: 1.48rem;
        font-size: 0.24rem;
        color: #555555;
        margin-top: 0.22rem;
        white-space: pre-wrap;
        text-align: center;
      }
      // 只显示一行
      .show_oneline {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
    // 当右侧首字母列隐藏时候
    .letter_hidden {
      margin: 0 0.16rem 0.2rem;
    }
  }
}

/* 右侧首字母选择 */
.right_word_box {
  width: 0.8rem;
  box-sizing: border-box;
  height: calc(100vh - 0.92rem);
  overflow-y: scroll;
  padding-bottom: 1.08rem;
  padding-top: 0.5rem;
  .letter {
    height: 0.7rem;
    line-height: 0.7rem;
    text-align: center;
    font-size: 0.28rem;
    color: #111;
  }
  .is_choose {
    width: 0.8rem;
    height: 0.4rem;
    line-height: 0.4rem;
    text-align: center;
    font-size: 0.28rem;
    background: linear-gradient(90deg, #ff650d, #ffa40d);
    color: #fff;
    border-radius: 0.3rem 0 0 0.3rem;
    padding: 0.04rem 0;
    box-sizing: content-box;
  }
}
</style>

使用组件

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值