当前位置: 首页 > news >正文

vue 自定义 tabs 控件,可自动左右滑动使得选中项居中显示

效果图如下:

录屏如下:

tabs录屏

控件用法如下:

<navi-tabs :data="tabs" @changeTab="changeTab"></navi-tabs>

import NaviTabs from "@/components/navi-tabs";

components: { NaviTabs },

tabs: [
  { codeName: "菜单1" },
  { codeName: "菜单2" },
  // { codeName: "菜单..." },
  { codeName: "菜单N" },
],
currentTabIndex: 0,

changeTab(index) {
  this.currentTabIndex = index;
  // this.refreshTargetTabPage();
},

控件源码如下:

<template>
  <div class="tabs-root">
    <div class="tabs-container">
      <div class="tabs" ref="tabs" v-if="data && data.length > 0">
          <div class="item" v-for="(tab, index) in data" :key="index"
            :class="activeIndex == index ? 'active': ''"
            @click="setActiveIndex(index)">
            {{ tab.codeName }}
          </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      default: [],
    },
  },
  data() {
    return {
      activeIndex: 0, // 默认激活的tab索引
    };
  },
  mounted() {
    this.scrollToActiveTab(); // 初始化时也滑动到第一个tab
  },
  methods: {
    setActiveIndex(index) {
        this.activeIndex = index;
        this.$emit("changeTab", index);
        this.scrollToActiveTab(); // 调用滑动函数
    },
    scrollToActiveTab() {
      this.$nextTick(() => {
        if (this.activeIndex == 0) {
          tabs.scrollTo({ left: 0, behavior: 'smooth' });
          return;
        }
        const tabs = this.$refs.tabs;
        const tabsRect = tabs.getBoundingClientRect();
        const activeTab = tabs.children[this.activeIndex];
        const activeTabRect = activeTab.getBoundingClientRect();
        let widthBeforeAll = 0;
        for (let index = 0; index < this.activeIndex; index++) {
          const element = tabs.children[index];
          const elementsRect = element.getBoundingClientRect();
          widthBeforeAll += elementsRect.width;
        }
        const offSetX = widthBeforeAll + (activeTabRect.width / 2) - (tabsRect.width / 2);
        tabs.scrollTo({ left: (tabsRect.left / 2 + offSetX), behavior: 'smooth' });
      });
    }
  },
};
</script>
<style lang="scss" scoped>
  .tabs-root {
    width: 100%;
    height: 44px;
    display: flex;
    justify-content: center;

    .tabs-container {
      width: auto;
      height: 100%;
      overflow-x: hidden;
      overflow-y: hidden;

      .tabs {
        display: flex;
        flex-direction: row;
        align-items: center;
        overflow-x: auto;

        .item {
          min-width: 120px;
          height: 26px;
          margin-top: 9px;
          margin-bottom: 9px;
          margin-right: 9px;
          cursor: pointer;
          white-space: nowrap;
          font-family: PingFangSC-Semibold;
          font-size: 18px;
          color: #FFFFFF;
          text-align: center;
          line-height: 26px;
          font-weight: 600;
          background: #C70019;
          border-radius: 13px;
        }
        .active {
          background: #DD6675;
        }
      }
    }
  }
</style>

相关文章:

  • VulnHub-FALL通关攻略
  • CSS3学习教程,从入门到精通,CSS3 弹性盒子(Flexbox)布局全面指南(20)
  • linux ACL权限控制之用户权限控制程序设计
  • HO与OH差异之Navigation三
  • 【leetcode刷题日记】lc.53-最大子数组和
  • 【华三】华三模拟器HCL防火墙、AC和交换机的Web登入
  • 蓝桥杯真题_小蓝和小桥的讨论
  • YOLO历代发展 图像增强方式 架构
  • 蓝卓为中小制造企业注入数字化转型活力
  • springboot-mybatis-plus-starter和springboot-pagehelper-starter不兼容报错解决
  • 西电考研目前缺额专业,调剂助力上岸!
  • 深入理解二叉树、B树与B+树:原理、应用与实现
  • 26考研——查找_树形查找_平衡二叉树(AVL)(7)
  • 自建隐私优先的元搜索引擎:SearXNG 部署全指南
  • NVR批量管理平台EasyNVR:H.265与H.264编码优势和差异深度剖析
  • SLAM——多传感器标定
  • Linux目录及文件管理
  • Docker技术系列文章,第七篇——Docker 在 CI/CD 中的应用
  • 【第22章】亿级电商订单系统架构-DDD设计
  • Spring MVC 拦截器
  • 自己网站上做支付宝怎么收费的/前端seo是什么
  • 把网站内的文本保存到txt怎么做/超级外链工具有用吗
  • 学生个人网站制作软件/中国网络优化公司排名
  • 做h5页面的网站/新媒体seo指的是什么
  • 石家庄做网站建设的公司哪家好/开网店如何运营和推广
  • 北京燕华工程建设有限公司网站/发外链的平台有哪些