Implementing Dynamic Navigation in WeChat Mini Programs
This implementation creates a fluid navigation bar with animated bubble transitions. The solution adapts web-based animation concepts to WeChat Mini Program constraints by replacing GSAP enimations with the native animation API.
Core Implementation
The navigation system uses layered elements to acheive the bubble effect:
- A background container with colored elements
- Interactive menu items at the bottom
- Animated bubble elemants that respond to user selection
WXML Structure
<view id="navContainer" animation="{{containerAnim}}">
<view id="navBar">
<view id="bubbleContainer">
<view wx:for="{{navItems}}" wx:key="index"
class="navBubble"
animation="{{item.bubbleAnim}}">
<van-icon name="{{item.icon}}" size="25px" />
</view>
</view>
<view id="menuContainer">
<view wx:for="{{navItems}}" wx:key="index"
class="menuItem"
bindtap="handleSelection"
data-index="{{index}}"
data-position="{{item.position}}"
data-color="{{item.color}}">
<van-icon name="{{item.icon}}" size="20px" />
</view>
</view>
</view>
<view id="backgroundLayer">
<view id="baseBackground" animation="{{bgAnim}}"></view>
<view id="bubbleBackground" animation="{{bubbleAnim}}"></view>
</view>
</view>
WXSS Styling
#navContainer {
width: 100%;
height: 90%;
background-color: #ffcc80;
border-radius: 40rpx;
overflow: hidden;
position: relative;
box-shadow: 0 20rpx 20rpx rgba(0,0,0,0.2);
}
#bubbleContainer {
position: absolute;
display: flex;
justify-content: space-around;
width: 100%;
bottom: 50rpx;
}
.navBubble {
background-color: #fff;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
z-index: 1;
transform: translateY(120%);
display: flex;
justify-content: center;
align-items: center;
}
#backgroundLayer {
filter: blur(3rpx);
width: 100%;
height: 200rpx;
position: absolute;
bottom: 120rpx;
}
#bubbleBackground {
position: absolute;
background-color: #ffcc80;
width: 140rpx;
height: 140rpx;
border-radius: 50%;
bottom: -100rpx;
}
JavaScript Animation Logic
Page({
data: {
navItems: [
{ icon: 'location-o', position: '95rpx', color: '#ffcc80' },
{ icon: 'contact-o', position: '280rpx', color: '#81d4fa' },
{ icon: 'link-o', position: '467rpx', color: '#c5e1a5' },
{ icon: 'list-switch', position: '655rpx', color: '#ce93d8' }
],
bubbleAnimations: Array(4).fill(null),
activeIndex: 0
},
handleSelection: function(event) {
const index = event.currentTarget.dataset.index;
const position = event.currentTarget.dataset.position;
const color = event.currentTarget.dataset.color;
this.animateTransition(index, position, color);
},
animateTransition: function(targetIndex, targetPosition, targetColor) {
const animationSteps = [];
// Initial bubble movement
const bubbleMove = wx.createAnimation({
duration: 150,
timingFunction: 'ease-out'
});
bubbleMove.bottom('-60rpx').step();
animationSteps.push(bubbleMove);
// Position transition
const positionChange = wx.createAnimation({
duration: 400,
timingFunction: 'ease-in-out'
});
positionChange.left(targetPosition).step();
animationSteps.push(positionChange);
// Final bubble placement
const bubbleFinal = wx.createAnimation({
duration: 450,
timingFunction: 'ease-out'
});
bubbleFinal.bottom('-100rpx').step();
animationSteps.push(bubbleFinal);
// Update UI with animation sequence
this.executeAnimationSequence(targetIndex, animationSteps, targetColor);
},
executeAnimationSequence: function(targetIndex, animations, color) {
const newAnimations = [...this.data.bubbleAnimations];
// Reset all bubbles
for (let i = 0; i < newAnimations.length; i++) {
const resetAnim = wx.createAnimation({ duration: 100 });
resetAnim.translateY('120%').step();
newAnimations[i] = resetAnim.export();
}
// Animate selected bubble
const selectAnim = wx.createAnimation({ duration: 1000 });
selectAnim.translateY('0%').opacity(1).step();
newAnimations[targetIndex] = selectAnim.export();
// Apply color transitions
const containerAnim = wx.createAnimation({ duration: 300 });
containerAnim.backgroundColor(color).step();
const bgAnim = wx.createAnimation({ duration: 300 });
bgAnim.backgroundColor(color).step();
// Update component state
this.setData({
bubbleAnimations: newAnimations,
containerAnim: containerAnim.export(),
bgAnim: bgAnim.export(),
activeIndex: targetIndex
});
}
})
Key Implementation Notes
- Uses WeChat's createAnimation API instead of unsupported libraries
- Implements sequential animations through chained steps
- Leverages Vant Weapp for icon components
- Emulates the "gooey" effect through careful layering