vue3+Ts+pinia实现左右联动的楼层导航案例

本文展示了如何利用Pinia状态管理库和Vue框架创建一个滚动选项卡组件。代码示例详细说明了如何定义store、处理滚动事件、切换选项卡以及实现内容区域与选项卡的同步滚动效果。
摘要由CSDN通过智能技术生成

废话不多说先看效果

 

 ts文件


import { defineStore } from 'pinia'
import { onMounted, reactive, toRefs } from 'vue'

export const useScrollTabStore = defineStore('Scroll', () => {
  interface iList {
    id: string,
    text: string
  }

  interface iState {
    List: iList[],
    tabParent: HTMLDivElement | null,
    contentSlide: HTMLDivElement | null,
    current: number,
    viewHeight: number,
    tabItemHeight: number,
    median: number,
    scrollTop: number,
    flag: boolean
  }
  const state = reactive<iState>({
    List: [
      {
        id: '1',
        text: '内容一'
      },
      {
        id: '2',
        text: '内容二'
      },
      {
        id: '3',
        text: '内容三'
      },
      {
        id: '4',
        text: '内容四'
      },
      {
        id: '5',
        text: '内容五'
      },
      {
        id: '6',
        text: '内容六'
      },
      {
        id: '7',
        text: '内容七'
      },
      {
        id: '8',
        text: '内容八'
      },
      {
        id: '9',
        text: '内容九'
      },
      {
        id: '10',
        text: '内容十'
      },
      {
        id: '11',
        text: '内容十一'
      },
      {
        id: '12',
        text: '内容十二'
      }
    ],
    tabParent: null,
    contentSlide: null,
    current: 0,
    viewHeight: 0,
    tabItemHeight: 0,
    median: 0,
    scrollTop: 0,
    flag: false
  })


  const onSwitch = (index: number) => {
    // 设置开关是为了解决点击选择时每次过渡要经过途经的格子
    state.flag = true;

    // 点击将下标值赋值给current,实现切换颜色
    state.current = index

    // 下标大于如果大于求出的中位数,让左侧每个item向上几个
    if (index > state.median) {
      state.tabParent?.scrollTo({
        top: state.tabItemHeight * (index - 3),
        behavior: 'smooth'
      })
    } else {
      state.tabParent?.scrollTo({
        top: 0,
        behavior: 'smooth'
      })
    }

    // 右侧的滚动
    // console.log(state.contentSlide,99999);
    // 下标乘以视口高度
    state.contentSlide?.scrollTo({
      top: index * state.viewHeight,
      behavior: 'smooth'
    })
    console.log(index * state.viewHeight, 888);


  }
  // 绑定滚动事件,如果开关为true,直接return不执行下面操作
  const onScroll = () => {

    if (state.flag) return;
    // 获取右侧滚动卷上去的高
    state.scrollTop = state.contentSlide!.scrollTop
    console.log(state.scrollTop, 99999);

    // 如果卷上去的高大于视口高乘下标
    state.List.forEach((_, index: number) => {
      if (state.scrollTop > index * state.viewHeight) {
        state.current = index
      }
    })

    // 实现左右侧联动,右侧滚动时,让左侧也跟着滚
    if (state.current > state.median) {
      state.tabParent?.scrollTo({
        top: state.tabItemHeight * (state.current - 3),
        behavior: 'smooth'
      })
    } else {
      state.tabParent?.scrollTo({
        top: 0,
        // behavior: 'smooth'
      })
    }


  }
  const onStart = () => {
    state.flag = false
  }
  onMounted(() => {
    // 屏幕可视区高
    state.viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
    // console.log(state.viewHeight);

    // 当前每个按钮高
    state.tabItemHeight = (state.tabParent?.children[0].children[0] as HTMLDivElement).offsetHeight
    // console.log(state.tabItemHeight);

    state.median = Math.floor(Math.floor(state.viewHeight / state.tabItemHeight) / 2)

    // 监听右侧页面的滚动
    state.contentSlide!.onscroll = onScroll

    state.contentSlide!.ontouchstart = onStart


  })

  return {
    ...toRefs(state),
    onSwitch
  }
})

vue文件

<template>
  <div class="scrolltab">
    <div class="wai" ref="tabParent">
      <div class="tab">
        <div v-for="(item, index) in List" @click="onSwitch(index)" :class="index == current ? 'ac' : ''">{{ item.id }}
        </div>
      </div>
    </div>

    <div class="wai" ref="contentSlide">
      <div class="content">
        <div v-for="item in List">{{ item.text }}</div>
      </div>
    </div>

  </div>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useScrollTabStore } from '@/store';

const { List, contentSlide, tabParent, current } = storeToRefs(useScrollTabStore());

const { onSwitch } = useScrollTabStore()


</script>

<style scoped lang="scss">
.scrolltab {
  width: 100vw;
  height: 100vh;
  display: flex;
  overflow: hidden;

  .wai::-webkit-scrollbar {
    display: none;

  }
  .wai {
    background-color: #fff;
    overflow: scroll;
  }

  .tab {
    width: 30vw;
    overflow-y: scroll;

    div {
      box-sizing: border-box;
      height: 200px;
      width: 30vw;
      text-align: center;
      line-height: 200px;
      background-color: skyblue;
    }
  }


  .tab::-webkit-scrollbar {
    display: none;
  }

  .content::-webkit-scrollbar {
    display: none;
  }

  .content {
    width: 70vw;
    overflow-y: scroll;


    div:nth-child(2n+1) {
      background-color: red;
    }

    div:nth-child(2n) {
      background-color: pink;
    }

    div {
      width: 70vw;
      height: 100vh;
      line-height: 230px;
      text-align: center;
    }

  }


}

.ac {
  background-color: #ccc !important;
}
</style>

以上就是这节的全部内容,欢迎各位大佬在下方评论区提问,如有不足,还望多多指教

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会打挡拆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值