vue tab可拖动,点击居中显示

vue yekong

数据可视化大屏项目开发中,如果菜单太多的话,我们就需要滚动显示。

这里我们要实现菜单可以鼠标拖动滚动,左右箭头点击滚动,点击菜单对应的菜单居中,如果左侧或者右侧没有多余的菜单了则不再滚动。

vue tab可拖动,点击居中显示

动态效果

使用到的插件

"@better-scroll/core": "^2.5.1",

实例代码

/**
* @Author: 858834013@qq.com
* @Name: menuList
* @Date: 2024年02月19日
* @Desc: 自动居中的滚动菜单
*/
<template>
  <div class="menuList">
    <div class="menuListLeft" @click="scrollLeft">
    </div>
    <div class="tabsBody horizontal-scrollbar-container">
      <div class="tabs scroll-wrapper" ref="scroll">
        <div class="scroll-content" ref="scroll2">
          <div class="newTabs">
            <div class="tabItem" @click="scrollToCenter(index)" ref="scrollItem" v-for="(item,index) in list">
              <router-link
                  :key="index"
                  :to="item.link"
                  class="tab"
                  :class="{active: $route.path === item.link}">
                <span>{{ item.title }}</span>
              </router-link>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="menuListRight" @click="scrollRight">
    </div>
  </div>
</template>
<script>
import BScroll from '@better-scroll/core'

export default {
  name: "menuList",
  data() {
    return {
      list: [{
        title: '总览',
        id: 0,
        link: '/home'
      }, {
        title: '办理建筑许可',
        id: 1,
        link: '/ProcessBuildingPermit'
      }, {
        title: '劳动力市场监控',
        id: 2,
        link: '/LaborMarketMonitoring'
      }, {
        title: '获得信贷',
        id: 3,
        link: '/ObtainCredit'
      }, {
        title: '办理破产',
        id: 4,
        link: '/FileBankruptcy'
      }, {
        title: '执行合同',
        id: 5,
        link: '/EnforceContract'
      }, {
        title: '政府采购',
        id: 6,
        link: '/GovernmentProcurement'
      }, {
        title: '纳税分析',
        id: 7,
        link: '/TaxAnalysis'
      }, {
        title: '保护中小投资者',
        id: 8,
        link: '/ProtectingSmallInvestors'
      }, {
        title: '登记财产',
        id: 9,
        link: '/RegisterProperty'
      }, {
        title: '跨境贸易',
        id: 9,
        link: '/CrossBorderTrade'
      }, {
        title: '获得电力',
        id: 9,
        link: '/AcquireElectricity'
      }, {
        title: '企业开办',
        id: 9,
        link: '/StartEnterprise'
      }],
    }
  },
   mounted() {
    var that = this;
    that.$nextTick(() => {
      that.scrollInit();
      setTimeout(() => {
        const currentPath = this.$route.path;
        console.log(currentPath)
        // 查找当前路由在list中的下标
        const currentIndex = this.list.findIndex(item => item.link === currentPath);

        // 如果找到了对应的下标,触发scrollToCenter方法
        if (currentIndex !== -1) {
          this.scrollToCenter(currentIndex);
        }
      }, 1000)
      // 获取当前路由路径
    });
  },
  methods: {
    scrollLeft() {
      this.scroll.scrollTo(this.scroll.x + 200, 0, 500);
    },
    scrollRight() {
      this.scroll.scrollTo(this.scroll.x - 200, 0, 500);
    },
    scrollToCenter(index) {
      this.$nextTick(() => {
        // 确保DOM已更新
        const scrollItems = this.$refs.scrollItem;
        if (scrollItems && scrollItems.length > index) {
          const targetElement = scrollItems[index];
          const scrollContainerRect = this.$refs.scroll.getBoundingClientRect();
          const targetElementRect = targetElement.getBoundingClientRect();

          // 计算目标元素中心相对于滚动容器中心的偏移
          let offsetX = targetElementRect.left + targetElementRect.width / 2 - scrollContainerRect.left - scrollContainerRect.width / 2;

          // 计算滚动的目标位置
          let targetX = this.scroll.x - offsetX;

          // 获取滚动容器的最大滚动宽度
          const maxScrollX = this.scroll.maxScrollX;

          // 确保目标位置不超出滚动容器的边界
          targetX = Math.max(targetX, maxScrollX);
          targetX = Math.min(targetX, 0);

          // 使用BetterScroll的scrollTo方法滚动到目标位置
          this.scroll.scrollTo(targetX, 0, 500);
        }
      });
    },
    scrollInit() {
      var that = this
      this.scroll = new BScroll(this.$refs.scroll, {
        scrollX: true,
        scrollY: false,
        click: true,
        bounce: true, // 禁用回弹效果
        probeType: 1,
        scrollbar: {
          fade: false,
          interactive: false,
          scrollbarTrackClickable: false,
          scrollbarTrackOffsetType: 'clickedPoint' // can use 'step'
        }
      })
      this.scroll.on('scrollEnd', (e) => {
        console.log('scrollEnd')
        this.lastSpot = Math.abs(e.x)
      })
      this.scroll.on('scrollStart', (e) => {
        console.log('scrollStart')
        console.log(e)
      })
      this.scroll.on('scroll', () => {
        console.log('scroll')
      })
    }
  }
}
</script>
<style scoped lang="scss">
.menuList {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  position: relative;
  height: 100%;
  width: calc(100% - 0px);

  .tab {
    height: calc(100% - 5px);
    position: relative;
    //width: 178px;
    //background: url("./assets/tabbg.png");
    //background-size: 100% 100%;
    opacity: 1;
    display: flex;
    cursor: pointer;
    justify-content: center;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
    align-content: flex-start;
    border-bottom: 2px solid rgba(#ACE5FF, 0);

    span {
      font-size: 26px;
      font-family: YouSheBiaoTiHei;
      font-weight: 400;
      color: #BBCDE6;
    }
  }

  .tab.active {
    opacity: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
    align-content: flex-start;
    border-bottom: 2px solid #ACE5FF;

    span {
      font-size: 26px;
      font-family: YouSheBiaoTiHei;
      font-weight: 400;
      color: #BBCDE6;
      background: linear-gradient(180deg, #ACE5FF 0%, #FFFFFF 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
  }

  .menuListLeft {
    width: 33px;
    background: url("./assets/icon_left.png") no-repeat;
    background-size: 100% 100%;
    height: 46px;
    cursor: pointer;
  }

  .menuListRight {
    width: 33px;
    background: url("./assets/icon_right.png") no-repeat;
    background-size: 100% 100%;
    height: 46px;
    cursor: pointer;
  }
}

.tabsBody {
  width: 100%;
  overflow: hidden;
  position: relative;
  //height: 100%;
  height: calc(100% - 10px);
  //margin-bottom: 10px;
}

.newTabs {
  display: flex;
  display: -webkit-flex;
  padding: 0;
  position: relative;
  height: calc(100% - 10px);
}

.tabItem {
  position: relative;
  height: 100%;
  margin-left: 20px;
  margin-right: 20px;
}

.tabs {
  display: flex;
  display: -webkit-flex;
  padding: 0;
  position: relative;
  height: 100%;

  .tab {
    position: relative;
    //width: 200px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    height: 100%;

    span {
      font-size: 26px;
      font-family: YouSheBiaoTiHei;
      font-weight: 400;
      color: #BBCDE6;
    }
  }
}

.scroll-content {
  display: inline-block;
  align-self: center;
  position: relative;
  height: 100%;
}
</style>

更新

2024年02月21日

如果页面是通过点击菜单进行跳转的菜单会滚动对应的菜单位置,当刷新页面后菜单组件没有滚动到对应的位置,当页面刷新后马上获取路由会回去不到准确的路由,我们延迟1秒再获取然后触发滚动方法。

 mounted() {
    var that = this;
    that.$nextTick(() => {
      that.scrollInit();
      setTimeout(() => {
        const currentPath = this.$route.path;
        console.log(currentPath)
        // 查找当前路由在list中的下标
        const currentIndex = this.list.findIndex(item => item.link === currentPath);

        // 如果找到了对应的下标,触发scrollToCenter方法
        if (currentIndex !== -1) {
          this.scrollToCenter(currentIndex);
        }
      }, 1000)
      // 获取当前路由路径
    });
  },

项目应用

营商环境智能监控中心 可视化数据大屏 vue3

喜欢