导读:本期聚焦于又改需求创作的《微信小程序如何自定义tabBar实现中间凸起按钮与动画效果?》,敬请观看详情。微信小程序默认的tabBar样式比较固定,想做出中间凸起的圆形发布按钮,或者给每个tab项加上切换动画,靠原生的tabBar配置是做不到的。其实小程序官方提供了custom-tab-bar组件机制,只要在app.json里开启custom开关,再编写自定义组件,就能完全接管底部导航栏的渲染逻辑。本文详细讲解custom-tab-bar的目录结构、与页面getTabBar的联动方式、凸起按钮的定位实现,以及点击切换时的缩放动画和图标跳动效果,同时总结了iOS安全区域适配、选中状态同步等常见坑,帮你做出交互细腻的底部导航。

做小程序开发的同学应该都见过类似抖音、小红书那种底部导航——中间一个凸起的圆形按钮,点击还能弹出一圈功能入口,切换tab时图标会弹跳一下。这种效果用微信小程序原生的tabBar配置文件是写不出来的,因为app.json里只能设置文字、图标和颜色,样式完全由官方渲染。好在小程序官方预留了custom-tab-bar组件机制,允许开发者彻底接管底部导航栏。本文就从零开始,带你实现一个带中间凸起按钮和切换动画的自定义tabBar。

微信小程序如何自定义tabBar实现中间凸起按钮与动画效果?

一、custom-tab-bar的基础配置与目录结构

首先要在app.json的tabBar节点里开启自定义开关。注意一点:即便全部使用自定义渲染,list数组里的页面配置也不能删掉,因为小程序依然依赖这个list来管理页面栈和路由跳转。

{
  "tabBar": {
    "custom": true,
    "color": "#999999",
    "selectedColor": "#1AAD19",
    "backgroundColor": "#ffffff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页"
      },
      {
        "pagePath": "pages/publish/publish",
        "text": "发布"
      },
      {
        "pagePath": "pages/mine/mine",
        "text": "我的"
      }
    ]
  }
}</code>

接下来要在项目根目录(与pages同级)创建一个固定名称为custom-tab-bar的文件夹,里面放index.js、index.json、index.wxml、index.wxss四个文件。这个文件夹名和文件名都是约定好的,写错一个字母小程序都识别不到。index.json里要把component字段声明为true,表示这是一个自定义组件:

{
  "component": true
}

js部分通常会在data里定义一个选中索引和tab列表,并提供一个跳转方法。这里有个关键点:每个tab页面都拥有自己独立的custom-tab-bar实例,切换页面后组件会重新初始化,所以每个页面的onShow里都要通过getTabBar()重新设置选中状态:

// custom-tab-bar/index.js
Component({
  data: {
    selected: 0,
    list: [
      { pagePath: '/pages/index/index', text: '首页', icon: 'home' },
      { pagePath: '/pages/publish/publish', text: '发布', icon: 'add' },
      { pagePath: '/pages/mine/mine', text: '我的', icon: 'user' }
    ]
  },
  methods: {
    switchTab(e) {
      const index = e.currentTarget.dataset.index;
      const path = '/' + this.data.list[index].pagePath;
      wx.switchTab({ url: path });
      this.setData({ selected: index });
    }
  }
});

// 各个tab页面的onShow中同步选中态
Page({
  onShow() {
    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      this.getTabBar().setData({ selected: 0 }); // 每个页面写自己的索引
    }
  }
});

二、中间凸起按钮的布局实现

凸起按钮的核心思路是:让中间那个tab项脱离正常的排列流,用绝对定位把它往上顶出容器。tabBar容器保持固定高度(比如100rpx),凸起按钮设置成圆形,直径比容器高度大,再用position: absolute配合负的bottom值让它一半悬浮在tabBar之外。

这里有个非常容易被忽略的坑:tabBar容器默认会被小程序限制在页面底部,超出容器的部分会被裁剪掉,导致凸起的半圆显示不出来。解决办法是给容器加overflow: visible,同时背景不要直接铺在容器上,而是铺在内层。另外凸起按钮的定位基准点要用bottom而不是top,这样在全面屏手机上跟随安全区域变化时不会错位。

<view class="tab-bar">
  <view
    wx:for="{{list}}"
    wx:key="index"
    class="tab-item {{selected === index ? 'active' : ''}}"
    data-index="{{index}}"
    bindtap="switchTab"
  >
    <!-- 中间项渲染成凸起按钮 -->
    <block wx:if="{{index === 1}}">
      <view class="raise-btn {{animClass}}">
        <image src="/images/add.png" class="raise-icon"/>
      </view>
    </block>
    <block wx:else>
      <image src="/images/{{item.icon}}.png" class="tab-icon"/>
      <text class="tab-text">{{item.text}}</text>
    </block>
  </view>
</view>
.tab-bar {
  display: flex;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100rpx;
  padding-bottom: env(safe-area-inset-bottom);
  background: #ffffff;
  overflow: visible;
}

.raise-btn {
  width: 110rpx;
  height: 110rpx;
  border-radius: 50%;
  background: linear-gradient(135deg, #ff7e5f, #feb47b);
  margin-top: -60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6rpx 16rpx rgba(255, 126, 95, 0.4);
}

.raise-icon {
  width: 56rpx;
  height: 56rpx;
}

关于安全区域适配,苹果X之后的机型底部有一条约34px的home指示条,直接用padding-bottom: env(safe-area-inset-bottom)处理最稳妥。如果要在js里拿到具体数值做更精细的布局,也可以通过wx.getSystemInfoSync()safeArea字段计算,不过纯CSS方案已经能覆盖绝大多数场景。

三、点击切换动画的实现方案

动画部分分两块:一是普通tab项被选中时图标的弹跳效果,二是凸起按钮被按下时的缩放反馈。小程序里实现动画最轻量的方式是CSS过渡,配合切换class名触发。

选中弹跳可以用@keyframes定义一个先放大再回弹的动画,只对active状态的图标生效。凸起按钮的按压效果则要区分hover-class和点击态,hover-class按住即生效松手即消失,适合做按压反馈;而点击后的放大动画要在js里切换一个class,动画结束后再移除,避免下次点击不触发。这里要注意动画重复触发的问题:同一个class如果上次动画还没结束又点击一次,动画不会重新播放,所以要在bindanimationend回调里把这个class置空。

@keyframes tabBounce {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.35) translateY(-8rpx); }
  70%  { transform: scale(0.9); }
  100% { transform: scale(1); }
}

.tab-icon.active {
  animation: tabBounce 0.4s ease;
}

@keyframes pressScale {
  0%   { transform: scale(1); }
  50%  { transform: scale(0.85); }
  100% { transform: scale(1); }
}

.raise-btn.pressed {
  animation: pressScale 0.25s ease;
}
switchTab(e) {
  const index = e.currentTarget.dataset.index;
  if (index === 1) {
    // 凸起按钮:先播动画再跳转,也可以改成弹出半屏菜单
    this.setData({ pressed: true });
    setTimeout(() => {
      wx.switchTab({ url: '/' + this.data.list[1].pagePath });
      this.setData({ pressed: false });
    }, 200);
    return;
  }
  wx.switchTab({ url: '/' + this.data.list[index].pagePath });
}

如果产品要求更炫的动画,比如凸起按钮点击后旋转扩散出一圈菜单项,CSS动画就会显得吃力,这时可以上wx.createAnimation或者直接用小程序的animate接口。不过从性能角度讲,能用CSS transformopacity实现的动画尽量用CSS,它们走的是合成层渲染,不会引起重排,低端机上也能保持流畅。

四、常见问题与避坑总结

第一个高频问题是切换tab后选中态错乱。原因前面提过,每个tab页对应独立的custom-tab-bar实例,只靠组件内部记录selected是不够的。规范做法是封装一个公共函数,在每个tab页面的onShow里调用this.getTabBar().init(),把选中态注入进去,这样无论从哪个入口进入页面都能保证导航高亮正确。

第二个问题是凸起按钮点击无效或者点击区域被遮挡。因为凸起部分超出了tabBar容器,如果上方页面内容有元素叠在上面,需要检查页面的z-index层级。自定义tabBar的层级在小程序里并不是最高的,遇到遮挡时可以在组件根节点显式设置一个较大的z-index。另外记得给凸起按钮本身绑定tap事件而不是依赖外层的tab-item,避免点击到半圆之外的空白区域误触发。

第三个问题是一些项目里tabBar会闪烁一下。这是因为自定义组件也要走下载渲染流程,首次进入时可能先显示空白的原生占位。可以在custom-tab-bar的wxss里给容器一个和页面背景接近的底色,减少视觉跳变。同时在app.json的tabBar里保留list配置的意义也在这里,框架需要它来预知有哪些tab页面。

最后补充一点:如果中间的凸起按钮实际并不是一个真正的tab页面,而只是触发一个动作(比如打开发布弹窗),可以在switchTab方法里直接return掉,改为调用this.triggerEvent通知页面层处理,或者直接在组件里用wx.navigateTo打开普通页面。这种情况下app.json的list里依然要保留一个占位的tab页面,只是这个按钮永远不执行switchTab而已。掌握这些细节后,一个交互流畅、视觉突出的自定义底部导航就完整落地了。

微信小程序custom-tab-bar自定义tabBar修改时间:2026-09-14 10:50:55

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260914/56639.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。