微信小程序自定义 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 lang='less' 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 lang='less' 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值