微信小程序案例 - 自定义 tabBar

一、前言:为什么需要自定义 tabBar?

微信小程序原生 tabBar 虽然简单易用,但存在明显限制:

  • ❌ 不支持中间"+"号等凸起按钮
  • ❌ 图标和文字样式无法高度自定义(如选中态动画)
  • ❌ 无法动态隐藏/显示 tabBar
  • ❌ 不能嵌入徽标(Badge)、红点等业务元素

解决方案 :使用自定义 tabBar!

本文将带你从零实现一个支持中间凸起按钮、带动画、可扩展的自定义 tabBar,并封装为通用组件。


二、最终效果预览

✅ 底部 5 个 tab(中间为"+"发布按钮)

✅ 点击 tab 平滑切换页面

✅ 中间按钮跳转独立功能页(如发布内容)

✅ 支持徽标、选中高亮、图标切换


三、实现原理

由于小程序页面是全屏渲染,我们无法像 H5 那样用 fixed 布局直接覆盖原生 tabBar。

正确做法:

  1. 关闭原生 tabBar (app.json 中不配置 tabBar)
  2. 每个页面底部手动引入自定义 tabBar 组件
  3. 通过 wx.switchTab 或 wx.navigateTo 模拟 tab 切换

⚠️ 注意:自定义 tabBar 不是全局组件,需在每个 tab 页面中手动引入!


四、第一步:创建自定义 tabBar 组件

1. 目录结构

复制代码
components/
└── custom-tab-bar/
    ├── custom-tab-bar.js
    ├── custom-tab-bar.json
    ├── custom-tab-bar.wxml
    └── custom-tab-bar.wxss

2. 配置组件(custom-tab-bar.json)

javascript 复制代码
{
  "component": true,
  "usingComponents": {}
}

3. 定义数据与方法(custom-tab-bar.js)

javascript 复制代码
// components/custom-tab-bar/custom-tab-bar.js
Component({
  properties: {
    current: {
      type: Number,
      value: 0
    }
  },

  data: {
    // tab 配置(可抽离为常量)
    tabs: [
      { pagePath: "/pages/home/index", text: "首页", icon: "home", selectedIcon: "home-fill" },
      { pagePath: "/pages/category/index", text: "分类", icon: "category", selectedIcon: "category-fill" },
      { pagePath: "/pages/publish/index", text: "", icon: "add", isCenter: true }, // 中间按钮
      { pagePath: "/pages/cart/index", text: "购物车", icon: "cart", selectedIcon: "cart-fill" },
      { pagePath: "/pages/my/index", text: "我的", icon: "my", selectedIcon: "my-fill" }
    ]
  },

  methods: {
    switchTab(e) {
      const { index } = e.currentTarget.dataset;
      const item = this.data.tabs[index];

      if (item.isCenter) {
        // 中间按钮:跳转非 tab 页(如发布页)
        wx.navigateTo({ url: '/pages/publish/index' });
        return;
      }

      // 普通 tab:切换页面
      wx.switchTab({ url: item.pagePath });

      // 可选:触发父页面更新 current
      this.triggerEvent('change', { index });
    }
  }
});

💡 提示:图标建议使用字体图标(如 IconFont)或本地 PNG


4. 编写模板(custom-tab-bar.wxml)

XML 复制代码
<!-- components/custom-tab-bar/custom-tab-bar.wxml -->
<view class="tab-bar">
  <view 
    wx:for="{{tabs}}" 
    wx:key="index"
    class="tab-item {{item.isCenter ? 'center-btn' : ''}} {{current === index ? 'active' : ''}}"
    data-index="{{index}}"
    bindtap="switchTab"
  >
    <view class="icon">
      <image 
        src="/assets/icons/{{current === index ? item.selectedIcon : item.icon}}.png" 
        mode="aspectFit"
        class="icon-img"
      />
      <!-- 示例:购物车徽标 -->
      <view wx:if="{{index === 3 && cartCount > 0}}" class="badge">{{cartCount}}</view>
    </view>
    <text wx:if="{{!item.isCenter}}" class="tab-text">{{item.text}}</text>
  </view>
</view>

5. 编写样式(custom-tab-bar.wxss)

css 复制代码
/* components/custom-tab-bar/custom-tab-bar.wxss */
.tab-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100rpx;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background: white;
  border-top: 1px solid #eee;
  padding-bottom: env(safe-area-inset-bottom);
  z-index: 999;
}

.tab-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  position: relative;
}

.center-btn {
  position: relative;
  top: -40rpx; /* 凸起效果 */
  width: 120rpx;
  height: 120rpx;
  background: #007aff;
  border-radius: 50%;
  box-shadow: 0 4rpx 12rpx rgba(0,122,255,0.3);
}

.center-btn .icon-img {
  width: 56rpx;
  height: 56rpx;
}

.icon-img {
  width: 48rpx;
  height: 48rpx;
}

.tab-text {
  font-size: 20rpx;
  margin-top: 8rpx;
  color: #888;
}

.active .tab-text {
  color: #007aff;
}

.badge {
  position: absolute;
  top: -8rpx;
  right: -8rpx;
  background: #ff3b30;
  color: white;
  border-radius: 50%;
  min-width: 24rpx;
  height: 24rpx;
  font-size: 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 2rpx;
}

五、第二步:在页面中使用 tabBar

1. 页面 JSON 引入组件

javascript 复制代码
// pages/home/index.json
{
  "usingComponents": {
    "custom-tab-bar": "/components/custom-tab-bar/custom-tab-bar"
  }
}

2. 页面 WXML 使用

XML 复制代码
<!-- pages/home/index.wxml -->
<view class="page-container">
  <!-- 页面内容 -->
  <view>首页内容</view>
</view>

<!-- 底部 tabBar -->
<custom-tab-bar current="0" bindchange="onTabChange" />

⚠️ 注意:每个 tab 页面都要引入,并传入对应的 current(0,1,3,4)


3. 页面 JS 处理(可选)

javascript 复制代码
// pages/home/index.js
Page({
  data: {
    cartCount: 5 // 示例:从全局状态获取
  },

  onTabChange(e) {
    // 如果需要同步 current(一般不需要)
    console.log('切换到 tab:', e.detail.index);
  }
});

六、关键问题解答

A:因为 switchTab 只能跳转到 app.json 中配置的 tabBar 页面,而发布页通常不是 tab 页面 ,所以用 navigateTo。

Q2:如何动态更新徽标(如购物车数量)?

A:可通过全局状态(如 globalData、Event Bus 或 Store)传递数据到 tabBar 组件。

Q3:页面内容被 tabBar 遮挡怎么办?

A:在页面最外层加 padding-bottom: 100rpx + safe-area:

css 复制代码
.page-container {
  min-height: 100vh;
  padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
  box-sizing: border-box;
}

七、进阶优化建议

  1. 图标使用字体图标:减少图片请求,支持颜色动态修改
  2. 动画效果 :点击时添加 scale 动画(transform: scale(0.9))
  3. 状态管理 :将 cartCount 等数据通过 Store 统一管理
  4. 适配 iPhone X :使用 env(safe-area-inset-bottom) 避免遮挡

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
考拉讲支付6 小时前
母婴亲子服务平台分账架构设计:多场景多方分账与合规清结算实践
大数据·运维·人工智能·微信小程序·架构·微信公众平台
拖孩6 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
喵喵虫7 小时前
汉字笔顺笔画元数据导出 JSON
小程序·json·元数据·汉字·笔顺·笔画
seconp8 小时前
计算机毕设做什么项目?整理了 80 个 Web、小程序、AI 与数据方向
前端·小程序·课程设计
程序员小孔.10 小时前
基于 SpringBoot + UniApp + Vue 的家政服务系统设计与实现
前端·数据库·spring boot·python·docker·微信小程序
万岳科技程序员小金1 天前
直播电商带货系统源码、小程序/APP开发:平台搭建方案全面解析
小程序·私域直播小程序·私域直播软件开发·私域直播app·直播电商系统源码·直播电商平台搭建
IT研究室1 天前
最新计算机毕业设计选题推荐-基于微信小程序的汉服交易系统-网站-文档指导-Java-springboot
java·微信小程序·课程设计
2301_32241428041 天前
springboot学生课堂出勤统计系统15835-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·mysql·随机森林·微信小程序·uni-app·微信开发者工具·推荐算法
小蒜学长2 天前
微信小程序-高校自习室管理系统(代码+数据库+LW)
java·spring boot·后端·微信小程序·自习室管理
上海秉坤2 天前
小程序如何打通门店?预约、券码核销与会员服务记录的流程设计
小程序·零售