天天看點

微信小程式自定義 tab-bar(基于 wepy)

背景

微信小程式提供的原生 tab-bar 功能簡單,樣式單一,無法滿足業務需求。

項目中使用的是 wepy 1.x 架構,實作原理與原生類似。

方案

一、 使用元件,在每個Tab頁引入

  1. 修改全局配置
    // app.wpy
    export default class extends wepy.app {
        config = {
          tabBar: {
                custom: true,
                // ...
            }
        }
    }
               
  2. 編寫

    tab-bar

    元件
    <!-- CustomTabBar.wpy -->
    <template>
        <cover-view class="tab-bar">
            <cover-view class="tab-bar-border"></cover-view>
            <cover-view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
                <cover-image class="icon" src="{{selected == index ? item.selectedIconPath : item.iconPath}}"></cover-image>
                <cover-view style="color: {{selected == index ? selectedColor : color}}">{{item.text}}</cover-view>
            </cover-view>
        </cover-view>
    </template>
    
    <script>
    import wepy from 'wepy';
    
    export default class CustomTabBar extends wepy.component {
        props = {
            selected: {
                type: String,
                default: '0'
            }
        }
    
        data = {
            color: '#7A7E83',
            selectedColor: '#6280f5',
            list: [
                {
                    selectedIconPath: '../images/tab_application_pre.png',
                    iconPath: '../images/tab_application.png',
                    pagePath: '/pages/Application',
                    text: '應用'
                },
                {
                    selectedIconPath: '../images/tab_project_pre.png',
                    iconPath: '../images/tab_project.png',
                    pagePath: '/pages/Machines',
                    text: '機械'
                },
                {
                    selectedIconPath: '../images/tab_message_pre.png',
                    iconPath: '../images/tab_message.png',
                    pagePath: '/pages/Message',
                    text: '消息'
                },
                {
                    selectedIconPath: '../images/tab_mine_pre.png',
                    iconPath: '../images/tab_mine.png',
                    pagePath: '/pages/MyHome',
                    text: '我的'
                }
            ]
        }
        methods = {
            switchTab(e) {
                const data = e.currentTarget.dataset;
                const url = data.path;
                wx.switchTab({ url });
            }
        }
    }
    </script>
    <style  scoped>
    .tab-bar {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        height: 160rpx;
        background: white;
        display: flex;
        padding-top: 8rpx;
        padding-bottom: env(safe-area-inset-bottom);
        .tab-bar-border {
            background-color: rgba(0, 0, 0, 0.33);
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 1px;
            transform: scaleY(0.5);
        }
    
        .tab-bar-item {
            flex: 1;
            text-align: center;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
    
            cover-image {
                width: 27px;
                height: 27px;
                margin-bottom: 12rpx;
            }
    
            cover-view {
                font-size: 10px;
            }
        }
    }
    </style>
               
  3. 在所有 tab 頁引用
    <!-- Application.wpy -->
    <template>
    	<view>
      	<CustomTabBar selected="0" />
      </view>
    </template>
    
    <script>
    import CustomTabBar from '../components/CustomTabBar';
     
    export default class Application extends wepy.page {
    	components = {
        CustomTabBar
      }
    }
    </script>
               

二、 官方推薦方式

  1. 在根目錄建立

    custom-tab-bar

    檔案夾,建立

    index.wpy

    檔案。
    實際上,根據官方文檔指引,應該在

    custom-tab-bar

    目錄下建立

    .js

    .wxml

    .wxss

    .json

    檔案。由于項目小程式使用 wepy 架構,是以建立的是

    .wpy

    檔案,編譯後會生成相應的檔案。
    <!-- index.wpy -->
    <template>
        <cover-view class="tab-bar">
            <cover-view class="border"></cover-view>
            <cover-view wx:for="{{list}}" wx:key="index" class="item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
                <cover-view wx:if="{{badge[index]}}" class="badge">{{badge[index]}}</cover-view>
                <cover-image src="{{selected === index ? item.selectedIconPath : item.iconPath}}" />
                <cover-view style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</cover-view>
            </cover-view>
        </cover-view>
    </template>
    
    <script>
    // 此處使用的是原生建立元件的方式,保證編譯後符合官方推薦的格式。
    Component({
        data: {
            badge: {},
            selected: 0,
            color: '#7A7E83',
            selectedColor: '#6280f5',
            list: [
                {
                    selectedIconPath: '../images/tab_application_pre.png',
                    iconPath: '../images/tab_application.png',
                    pagePath: '/pages/Application',
                    text: '應用'
                },
                {
                    selectedIconPath: '../images/tab_news_pre.png',
                    iconPath: '../images/tab_news.png',
                    pagePath: '/pages/News',
                    text: '新聞'
                },
                {
                    selectedIconPath: '../images/tab_message_pre.png',
                    iconPath: '../images/tab_message.png',
                    pagePath: '/pages/Message',
                    text: '消息'
                },
                {
                    selectedIconPath: '../images/tab_mine_pre.png',
                    iconPath: '../images/tab_mine.png',
                    pagePath: '/pages/MyHome',
                    text: '我的'
                }
            ]
        },
        attached () {
        },
        methods: {
            switchTab (e) {
                const data = e.currentTarget.dataset
                const url = data.path
                wx.switchTab({ url })
                this.setData({
                    selected: data.index
                });
            },
            setTabBarBadge(options) {
                if (!options) return;
                const { index, text } = options
                const badge = this.data.badge || {};
                badge[index] = text;
                this.setData({
                    badge
                });
            },
            removeTabBarBadge(options) {
                if (!options || options.index === undefined) return;
                const { index } = options;
                const badge = this.data.badge || {};
                badge[index] = null;
                this.setData({
                    badge
                });
            }
        },
    })
    
    // NOTE: 此處導出含 config 屬性的類,是為了保證編譯後生成 .json 檔案
    export default class CustomTabBar {
        config = {
            component: true
        }
    }
    </script>
    <style  scoped>
    .tab-bar {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        background: white;
        display: flex;
        padding-top: 16rpx;
        padding-bottom: env(safe-area-inset-bottom);
        .border {
            background-color: rgba(0, 0, 0, 0.33);
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 1px;
            transform: scaleY(0.5);
        }
        .item {
            position: relative;
            flex: 1;
            text-align: center;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            .badge {
                position: absolute;
                top: 0;
                right: 50%;
                padding: 4rpx 12rpx;
                border-radius: 28rpx;
                font-size: 20rpx;
                background-color: red;
                color: #fff;
                z-index: 3;
                transform: translateX(100%);
            }
            cover-image {
                width: 27px;
                height: 27px;
                margin-bottom: 12rpx;
            }
            cover-view {
                font-size: 10px;
            }
        }
    }
    </style>
               
  2. 同方案一的步驟一,修改全局配置。
  3. 在每個 tab 頁中設定 tabBar 的選中狀态。
    <!-- Application.wpy -->
    export default class Application extends wepy.page {
     	onShow() {
        if (this.$wxpage && typeof this.$wxpage.getTabBar === 'function') {
            const tabBar = this.$wxpage.getTabBar();
          	tabBar.setData({
              selected: 0, // 由于Application是第一個tab頁
            });
        }
      } 
    }
               
  4. 封裝常用操作 tabBar 的方法,包括 tabBar 徽标功能。
    // 擷取自定義 tabBar 元件示例
    export const getCustomTabBar = ctx => {
        let tabBar = null;
        if (ctx && ctx.$wxpage && typeof ctx.$wxpage.getTabBar === 'function') {
            tabBar = ctx.$wxpage.getTabBar();
        }
        return tabBar;
    };
    
    // 設定 active tabBar
    export const setActiveTabBar = (ctx, index = 0) => {
        const tabBar = getCustomTabBar(ctx);
        if (tabBar) {
            tabBar.setData({
                selected: index
            });
        }
    };
    
    // 設定徽标
    export const setTabBarBadge = (ctx, options = { index: 0, text: '' }) => {
        const { index = 0, text = '' } = options;
        const tabBar = getCustomTabBar(ctx);
        if (tabBar) {
            tabBar.setTabBarBadge({
                index,
                text
            });
        } else {
            wx.setTabBarBadge({
                index,
                text
            });
        }
    };
    
    // 移除徽标
    export const removeTabBarBadge = (ctx, options = { index: 0 }) => {
        const { index = 0 } = options;
        const tabBar = getCustomTabBar(ctx);
        if (tabBar) {
            tabBar.removeTabBarBadge({
                index
            });
        } else {
            wx.removeTabBarBadge({
                index
            });
        }
    };
               

注意

在自定義 tabBar 模式下

  • 為了保證低版本相容以及區分哪些頁面是 tab 頁,tabBar 的相關配置項仍然需要完整聲明,但這些字段不會作用于自定義 tabBar 的渲染。
  • 此時需要開發者提供一個自定義元件來渲染 tabBar,所有 tabBar 的樣式都由該自定義元件渲染。推薦用 fixed 在底部的 cover-view + cover-image 元件渲染樣式,以保證 tabBar 層級相對較高。
  • 與 tabBar 樣式相關的接口,如 wx.setTabBarItem 等将失效。
  • 每個 tab 頁下的自定義 tabBar 元件執行個體是不同的,可通過自定義元件下的

    getTabBar

    接口,擷取目前頁面的自定義 tabBar 元件執行個體。

注意

如需實作 tab 選中态,要在目前頁面下,通過

getTabBar

接口擷取元件執行個體,并調用 setData 更新選中态。

參考

  1. 微信小程式開發文檔
  2. wepy github issue#2052
  3. https://www.yuque.com/currywxj/pwgsgm/mci8h7#ZSOWI