Los mini programas de WeChat ofrecen una barra de navegación inferior (tabBar) integrada, pero su personalización es limitada: no permite iconos centrales sobresalientes, estilos dinámicos ni comportamientos híbridos (navegación + acciones funcionales). A continuación se describe una solución modular y reutilizable que sustituye la barra nativa mediante un componente personalizado.
Estructura del comopnente
El componente CustomBottomNav se diseña como un elemento fijo en la parte enferior de la pantalla. Para evitar superposiciones con el contenido, las páginas que lo integran deben aplicar un padding-bottom equivalente a su altura (por ejemplo, 100rpx).
Configuración flexbile
Cada ítem de la barra se define mediante un objeto con propiedades explícitas:
id: identificador único del ítemtarget: ruta relativa de página (vacío si es acción funcional)label: texto visible (opcional)icon/activeIcon: rutas de recursos para estado inactivo y activonormalColor/activeColor: colores de texto o icono
Código del componente
custom-nav.js (lógica)
Component({
properties: {
activeId: {
type: Number,
value: 0
}
},
data: {
items: [
{
id: 0,
target: '/pages/home/home',
label: 'Inicio',
icon: '/assets/icons/home-inactive.png',
activeIcon: '/assets/icons/home-active.png',
normalColor: '#8a8a8a',
activeColor: '#00c853'
},
{
id: 1,
target: '',
icon: '/assets/icons/scan.png',
activeIcon: '/assets/icons/scan.png',
normalColor: '#000',
activeColor: '#000'
},
{
id: 2,
target: '/pages/profile/profile',
label: 'Perfil',
icon: '/assets/icons/profile-inactive.png',
activeIcon: '/assets/icons/profile-active.png',
normalColor: '#8a8a8a',
activeColor: '#00c853'
}
]
},
methods: {
handleItemClick(e) {
const { id } = e.currentTarget.dataset;
const item = this.data.items.find(i => i.id === id);
if (!item.target) {
this.triggerEvent('action', { id });
return;
}
wx.switchTab({ url: item.target });
}
}
});
custom-nav.wxml (plantilla)
<view class="nav-container">
<view
wx:for="{{items}}"
wx:key="id"
class="nav-item"
data-id="{{item.id}}"
bindtap="handleItemClick"
>
<image
class="nav-icon"
src="{{item.id === activeId ? item.activeIcon : item.icon}}"
mode="aspectFit"
/>
<text
class="nav-label"
style="color: {{item.id === activeId ? item.activeColor : item.normalColor}}"
>{{item.label}}</text>
</view>
</view>
custom-nav.wxss (estilos)
.nav-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
align-items: center;
height: 100rpx;
background: #fff;
border-top: 1rpx solid #eee;
z-index: 999;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.nav-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 6rpx;
}
.nav-label {
font-size: 24rpx;
line-height: 1;
}
Uso en una página
page.json
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
}
}
page.wxml
<custom-nav
active-id="{{currentTab}}"
bind:action="onFunctionalAction"
/>
page.js
Page({
data: {
currentTab: 0
},
onFunctionalAction(e) {
console.log('Acción funcional disparada:', e.detail.id);
// Ejecutar lógica específica (escaneo QR, etc.)
}
});