uni-app 中用 custom-tab-bar 实现中间凸起按钮需关闭原生 tabbar,pages.json 设 "custom": true,根目录放 components/custom-tab-bar/index.vue,tab 页面 page.json 加 "disablescroll": true;current 值通过 getcurrentpages() 动态计算;凸起按钮用 transform: translatey(12px) + 固定宽高 + z-index: 999 实现,app-plus 需 padding-bottom: env(safe-area-inset-bottom),h5 需监听视口变化;跳转用 uni.switchtab 保状态,非 tabbar 页面需手动触发更新;图标状态用条件 class 控制,交互反馈用纯 css。

uni-app 中如何用 custom-tab-bar 实现中间凸起按钮
uni-app 官方支持的 custom-tab-bar 是唯一稳定、跨端(iOS/Android/H5)可行的方案。原生 tabbar 不允许中间凸起,必须关掉原生 tabbar,自己实现整个底部栏——不是只改一个按钮,而是整套替换。
关键前提:"mp-weixin" 和 "app-plus" 下必须启用自定义 tabBar;"h5" 端需手动控制显示逻辑(它不走 custom-tab-bar 生命周期)。
- 在
pages.json中设置"tabBar": { "custom": true },否则自定义组件不会被加载 - 项目根目录下必须存在
components/custom-tab-bar/index.vue(路径和文件名不能错) - 所有 tab 页面的
page.json里要加"disableScroll": true,避免 iOS 上滑动时 tabbar 被顶起
中间按钮点击后如何跳转并保持高亮状态
自定义 tabbar 没有自动同步激活态,current 值需要手动维护。官方推荐用 uni.$emit + uni.$on 或 pinia/vuex 同步,但最轻量的是通过 getCurrentPages() 动态读取当前页面路径来判断。
示例逻辑(放在 custom-tab-bar/index.vue 的 computed 中):
computed: {
current() {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const path = currentPage.route
return this.list.findIndex(item => item.pagePath === path)
}
}
- 不要依赖
uni.getTabBar()(H5 不支持,App 端也常返回 undefined) - 中间按钮的
pagePath必须是合法页面路径,不能是空字符串或外部链接 - 如果中间按钮需打开非 tabBar 页面(如发布页),记得在
list配置中留空该位置,用单独的<view class="add-btn"></view>覆盖
凸起区域的样式怎么写才不遮挡、不偏移、适配各端
凸起本质是绝对定位 + 负 margin 或 transform,但直接写 bottom: 20px 在不同端表现差异极大:iOS 安全区、Android 虚拟导航栏、H5 浏览器 toolbar 都会影响实际位置。
- 统一用
transform: translateY(12px)(比top/bottom更稳定),配合z-index: 999 - 凸起按钮容器宽高建议设为固定值(如
width: 60px; height: 60px),避免 flex 布局下被挤压变形 - 在
app-plus下,需额外加padding-bottom: env(safe-area-inset-bottom)给底部留白,否则可能被遮挡 - H5 端需监听
window.innerHeight变化,动态调整 bottom 值(因为地址栏展开/收起会改变视口高度)
为什么点击中间按钮后 tabbar 图标闪烁或状态丢失
这是最常见的问题,根本原因是:中间按钮触发的是 uni.navigateTo(非 tabBar 页面),导致页面栈变化,而自定义 tabbar 组件本身没重绘或未监听页面栈变更。
- 确保中间按钮使用
uni.switchTab跳转到已配置在tabBar.list中的页面;若必须跳非 tabBar 页面,请在目标页面onLoad中主动uni.$emit('updateTabBar')通知组件刷新 - 不要在
custom-tab-bar组件里调用this.$forceUpdate()—— 它在 App 端无效,在 H5 端可能引发循环渲染 - 图标状态建议全用条件 class 控制(如
:class="{ 'active': current === 2 }"),而不是靠 v-if 切换整个 icon 标签
凸起按钮的交互反馈(比如按下缩放、点击波纹)必须用纯 CSS 实现,避免引入 JS 动画库——小程序环境对 requestAnimationFrame 支持有限,容易卡顿。真正难的不是画出那个凸起,而是让它的位置、状态、响应在三个平台都一致。这点没有银弹,只能逐端调试,记下每个平台的 offset 偏差值。











