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

废话不多说先看效果

 

 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>

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
实现搜索功能,首先需要在 Pinia 中定义一个 store,用于存储搜索相关的状态和数据。示例代码如下: ```typescript import { defineStore } from 'pinia'; interface SearchState { keyword: string; results: any[]; // 存储搜索结果 } export const useSearchStore = defineStore({ id: 'search', state: (): SearchState => ({ keyword: '', results: [], }), getters: { hasResults(state): boolean { return state.results.length > 0; }, }, actions: { search(keyword: string) { // 发起搜索请求,获取搜索结果 // 这里可以使用 axios 或其他类库,获取后端接口返回的数据 const results = [{ title: '搜索结果1' }, { title: '搜索结果2' }]; this.keyword = keyword; this.results = results; }, clearResults() { this.results = []; }, }, }); ``` 在组件中使用该 store,需要使用 `useStore` 方法获取 store 实例,然后在模板中绑定输入框和搜索按钮的事件。示例代码如下: ```vue <template> <div> <input v-model="keyword" placeholder="输入关键字" /> <button @click="handleSearch">搜索</button> <ul v-if="hasResults"> <li v-for="result in results" :key="result.title">{{ result.title }}</li> </ul> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; import { useStore } from 'pinia'; import { useSearchStore } from './searchStore'; export default defineComponent({ setup() { const store = useStore(useSearchStore); const keyword = store.keyword; const results = store.results; const hasResults = store.hasResults; const handleSearch = () => { store.search(keyword); }; return { keyword, results, hasResults, handleSearch, }; }, }); </script> ``` 在模板中,我们使用了 `v-model` 指令绑定了输入框的值,使用 `@click` 绑定了搜索按钮的点击事件。在 `setup` 中使用 `useStore` 方法获取了 store 实例,并将 store 中的状态和方法绑定到模板中使用。在 `handleSearch` 方法中,调用了 store 的 `search` 方法,发起了搜索请求,更新了 store 中的状态,从而触发了模板中对应的响应式状态的更新。最终,我们根据 store 中的状态来展示搜索结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会打挡拆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值