Custom Animated Navigation Bar for WeChat Mini Programs

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:

  1. A background container with colored elements
  2. Interactive menu items at the bottom
  3. 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

Tags: wechat-miniprogram WXSS WXML javascript animation

Posted on Sat, 12 Sep 2026 16:29:42 +0000 by scotmcc