美文网首页微信小程序的坑边杂谈
微信小程序自定义tabBar

微信小程序自定义tabBar

作者: 国王不在家 | 来源:发表于2019-05-05 15:56 被阅读168次

    概括

    • 注意事项
    • 项目结构
    • 代码
    • 适配低版本

    参考文档:

    小程序官方的tabBar文档
    小程序官方的Component文档
    小程序app.json的配置

    注意事项

    1. 自定义tabBar 基础库 2.5.0 开始支持
    2. 自定义tabBar的组件一定要叫 custom-tab-bar 并且一定要与app.js同级
    3. 在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置也补充完整。
    4. 所有 tab 页的 json 里需声明usingComponents 项,也可以在 app.json 全局开启。
    // app.json 记得删掉注释
    
      "usingComponents": {
        "nav": "/components/nav_bar/nav_bar" // 用与做低版本适配的 自定义tabBar组件
      },
      "tabBar": {
        "custom": true, // 打开tabBar的自定义功能
        "color": "#212121",
        ...
    

    项目结构

    • 代替原生tabBar的 custom-tab-bar 是不是一定要这样放?
      是的,一定要放在与app.js同级的项目结构, 而且一定要叫 custom-tab-bar
      微信小程序自定义tabBar 项目结构

    先码为敬

    custom-tab-bar

    1. wxml
    <!--components/custom-tab-bar/index.wxml-->
    <view class='my-bar ak-flexB {{isIpx ? "hackIPX" : ""}}'>
      <view wx:for="{{list}}" wx:key="index" class='my-bar__item ak-flexC' data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab" data-jump_type='{{item.jumpType}}'>
        <view class='my-bar__item-text ak-flex-columnC {{selected == index ? "my-bar__item-active" : ""}}'>
          <image class='my-bar__btn-img animated' mode='widthFix' src='{{selected === index ? item.selectedIconPath : item.iconPath}}'></image>
          {{item.text}}
          <view hidden="{{item.tagNum <= 0}}" class='my-bar__item-tag ak-flexC'>
            {{item.tagNum}}
          </view>
        </view>
      </view>
    </view>
    
    1. js
    // components/custom-tab-bar/index.js
    let app = getApp();
    Component({
      /**
       * 组件的属性列表
       */
      properties: {
        now: {
          type: String,
          value: 'index'
        },
        cartNum: {
          type: Number,
          value: 0,
        }
      },
    
      /**
       * 组件的初始数据
       */
      data: {
        isIpx: app.globalData.isIpx,  // 用于适配全面屏的底部高度(iPhone X* 的底部杠杠)
        selected: 0, // 当前选中的项
        color: "#333", // 未选中的字体的颜色
        selectedColor: "#d01716", // 选中时的字体颜色
        list: [{
          pagePath: "/pages/index/index", // 跳转路径, 【switchTab的跳转一定要在app.json中配置】
          iconPath: "/images/icon_index.png",
          selectedIconPath: "/images/icon_index_act.png",
          jumpType: "switchTab", // 跳转的类型
          tagNum: 0,
          text: "首页"
        }, {
          pagePath: "/pages/classificationII/classificationII",
          iconPath: "/images/icon_classify.png",
          selectedIconPath: "/images/icon_classify_act.png",
          jumpType: "switchTab",
          tagNum: 0,
          text: "分类"
        }, {
          pagePath: "/pages/shoppingCart/shoppingCart",
          iconPath: "/images/icon_cart.png",
          selectedIconPath: "/images/icon_cart_act.png",
          jumpType: "navigateTo",
          tagNum: 0,
          text: "购物车"
        }, {
          pagePath: "/pages/center2/center2",
          iconPath: "/images/icon_my.png",
          selectedIconPath: "/images/icon_my_act.png",
          jumpType: "switchTab",
          tagNum: 0,
          text: "我的"
        }]
      },
      /**
       * 组件的方法列表
       */
      methods: {
        switchTab(e) {
          const data = e.currentTarget.dataset
          const url = data.path
          const jumpType = data.jump_type;
          wx[jumpType]({
            url
          })
          this.setData({
            selected: data.index
          })
        },
      }
    })
    
    1. wxss
    /* components/custom-tab-bar/index.wxss */
    /*      弹性盒居中            */
    .animated {
      -webkit-animation-duration: .7s;
      animation-duration: .7s;
      -webkit-animation-fill-mode: both;
      animation-fill-mode: both;
    }
    
    .animated.infinite {
      -webkit-animation-iteration-count: infinite;
      animation-iteration-count: infinite;
    }
    @keyframes bounceIn {
      from,  20%,  40%,  60%,  80%,
      to {
        -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
        animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
      }
      0% {
        opacity: 0;
        -webkit-transform: scale3d(0.3, 0.3, 0.3);
        transform: scale3d(0.3, 0.3, 0.3);
      }
      20% {
        -webkit-transform: scale3d(1.1, 1.1, 1.1);
        transform: scale3d(1.1, 1.1, 1.1);
      }
      40% {
        -webkit-transform: scale3d(0.9, 0.9, 0.9);
        transform: scale3d(0.9, 0.9, 0.9);
      }
      60% {
        opacity: 1;
        -webkit-transform: scale3d(1.03, 1.03, 1.03);
        transform: scale3d(1.03, 1.03, 1.03);
      }
      80% {
        -webkit-transform: scale3d(0.97, 0.97, 0.97);
        transform: scale3d(0.97, 0.97, 0.97);
      }
    
      to {
        opacity: 1;
        -webkit-transform: scale3d(1, 1, 1);
        transform: scale3d(1, 1, 1);
      }
    }
    
    .bounceIn {
      -webkit-animation-duration: 0.75s;
      animation-duration: 0.75s;
      -webkit-animation-name: bounceIn;
      animation-name: bounceIn;
      animation-delay: 0.26s;
    }
    
    .ak-flexC {
        display: -webkit-box;
        display: -moz-box;
        display: -ms-flexbox;
        display: -webkit-flex;
        display: flex;
        -webkit-justify-content: space-around;
        justify-content: space-around;
        -moz-box-pack: space-around;
        -webkit--moz-box-pack: space-around;
        box-pack: space-around;
        align-items: center;
        -webkit-align-items: center;
        box-align: center;
        -moz-box-align: center;
        -webkit-box-align: center;
    }
    
    /*      弹性盒居两边            */
    .ak-flexB {
        display: -webkit-box;
        display: -moz-box;
        display: -ms-flexbox;
        display: -webkit-flex;
        display: flex;
        -webkit-justify-content: space-between;
        justify-content: space-between;
        -moz-box-pack: space-between;
        -webkit--moz-box-pack: space-between;
        box-pack: space-between;
        align-items: center;
        -webkit-align-items: center;
        box-align: center;
        -moz-box-align: center;
        -webkit-box-align: center;
    }
    
    /*              弹性盒纵向排列(居中)              */
    .ak-flex-columnC {
        display: -webkit-box;
        display: -moz-box;
        display: -ms-flexbox;
        display: -webkit-flex;
        display: flex;
        -webkit-justify-content: space-around;
        justify-content: space-around;
        -moz-box-pack: space-around;
        -webkit--moz-box-pack: space-around;
        box-pack: space-around;
        align-items: center;
        -webkit-align-items: center;
        box-align: center;
        -moz-box-align: center;
        -webkit-box-align: center;
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -moz-box-orient: vertical;
        -moz-box-direction: normal;
        flex-direction: column;
        -webkit-flex-direction: column;
    }
    view{
      box-sizing: border-box;
    }
    .my-bar{
      position: fixed;
      bottom: 0;
      left: 0;
      z-index: 9;
      width: 100%;
      background-color: rgba(254, 254, 254, .96);
      box-shadow: 0 0 16px rgba(155, 155, 155, .5);
    }
    .my-bar__item{
      flex: 1;
      height: 98rpx;
      padding-top: 10rpx;
    }
    .my-bar__item-text{
      height: 100%;
      text-align: center;
      width: 100%;
      color: #333;
      position: relative;
    }
    .my-bar__item-text2:last-child{
      height: 100%;
      text-align: center;
      width: 100%;
      color: #333;
    }
    
    .my-bar__item-text, .my-bar__item-text2{
      font-size: 22rpx;
    }
    .my-bar__item-tag{
      position: absolute;
      top: 0;
      right: 62rpx;
      width: 26rpx;
      height: 26rpx;
      background-color: #d01716;
      color: #fff;
      border-radius: 50%;
      font-size: 20rpx;
    }
    .iconfont{
      font-size: 46rpx;
    }
    
    .my-bar__item-active{
      color: #d01716 !important;
    }
    
    
    .hackIPX{
      box-sizing: content-box;
      padding-bottom: 68rpx;
    }
    
    .my-bar__btn-img{
      width: 50rpx;
      height: 50rpx;
    }
    .my-bar__btn-img-act{
      width: 60rpx;
      height: 60rpx;
    }
    
    .hackIPX{
      box-sizing: content-box;
      padding-bottom: 48rpx;
      padding-top: 20rpx;
    }
    
    1. app.json
    
      "usingComponents": {
        "nav": "/components/nav_bar/nav_bar"
      },
      "tabBar": {
        "custom": true,
        "color": "#212121",
        "selectedColor": "#d01716",
        "backgroundColor": "#fefefe",
        "list": [
          {
            "pagePath": "pages/index/index",
            "iconPath": "/images/icon_index.png",
            "selectedIconPath": "/images/icon_index_act.png",
            "text": "首页"
          },
          {
            "pagePath": "pages/classificationII/classificationII",
            "iconPath": "/images/icon_classify.png",
            "selectedIconPath": "/images/icon_classify_act.png",
            "text": "分类"
          },
          {
            "pagePath": "pages/center2/center2",
            "iconPath": "/images/icon_my.png",
            "selectedIconPath": "/images/icon_my_act.png",
            "text": "我的"
          }
        ]
      },
    
    1. 在用到tabBar的页面.js
      /**
       * 生命周期函数--监听页面显示
       */
      onShow: function() {
        let _this = this;
        if (!app.globalData.accessToken){
          // Tips: 1、没有有登录状态: 只设置tabbar
          console.log('没有有登录状态: 只设置tabbar')
          if (typeof this.getTabBar === 'function' &&
            this.getTabBar()) {
            this.getTabBar().setData({
              ['selected']: 0
            })
          }
        }else{
          // Tips: 2、有登录状态: 拿购物车 用户消息 优惠券数据
          console.log('有登录状态: 拿购物车 用户消息 优惠券数据')
          app.myRequest('cartNum', {}, 'get',
            function (res) {
              if (typeof _this.getTabBar === 'function' &&
                _this.getTabBar()) {
                _this.getTabBar().setData({
                  ['selected']: 0,
                  ['list[2].tagNum']: res.data
                })
              }
              _this.setData({
                cartNums: res.data
              })
            }, 'top');
          this.showCoupons();
        }
      },
    
    
    // 其实核心代码 其他的代码跟我的业务逻辑挂钩的
    //if (typeof this.getTabBar === 'function' &&
    //  this.getTabBar()) {
    //  this.getTabBar().setData({
    //    ['selected']: 0
    //  })
    //}
    

    OK 以上的这些玩意儿就够你玩转小程序的自定义tabBar了
    但是我们要考虑到有些用户死活不升级的情况
    (别问我为什么,在遍地都是iOS12.x 的时候,我的用户还有iOS8.x 的)
    就是微信版本低, 小程序的基础库版本也低,不支持展示小程序的自定义tabBar;
    此时如果我们不做兼容的话 用户将看不到底部栏

    适配低版本

    • 注意, 我们这里用的是自定义组件来代替小程序的自定义tabBar
      从项目结构上来说是用/components/tab_bar 来代替 /custom-tab-bar
      由于用到了自定义组件 而支自定义组件是从1.6.3 的基础版本开始支持的
      如果需要稳定的话, 推荐线上最低基础库需要支持到 2.1.0

    关键点有3个

    1. 自己的tabBar组件
    2. 隐藏官方的tabBar
    3. 控制自己的tabBar组建的显示和隐藏

    1. 自己的tanBar组件

    a. /components/tab_bar/tab_bar.js

    // components/table_bar/table_bar.js
    let app = getApp();
    Component({
      /**
       * 组件的属性列表
       */
      properties: {
        now: {
          type: String,
          value: 'index'
        },
        cartNum: {
          type: Number,
          value: 0,
        }
      },
    
      /**
       * 组件的初始数据
       */
      data: {
        isIpx: app.globalData.isIpx,
      },
      /**
       * 组件的方法列表
       */
      methods: {
        _tableJump: function(e) {
          let jumpType = e.currentTarget.dataset.jump;
          if (jumpType == this.properties.now) {
            return;
          } else {
            if (jumpType == 'index') {
              wx.switchTab({
                url: '/pages/index/index'
              })
            } else if (jumpType == 'my') {
              wx.switchTab({
                url: '/pages/center2/center2'
              })
            } else if (jumpType == 'classify') {
              wx.switchTab({
                url: '/pages/classificationII/classificationII'
              })
            } else if (jumpType == 'cart') {
              wx.navigateTo({
                url: '/pages/shoppingCart/shoppingCart'
              })
            } else {
              wx.reLaunch({
                url: '/pages/find/find'
              })
            }
          }
        },
      }
    })
    

    b. /components/tab_bar/tab_bar.wxml

    <!--components/tab_bar/tab_bar.wxml-->
    
    <view class='my-bar ak-flexB {{isIpx ? "hackIPX" : ""}}'>
      <view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='index'>
        <view class='my-bar__item-text ak-flex-columnC {{now == "index" ? "my-bar__item-active" : ""}}'>
          <image src='/images/icon_index.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'index'}}"></image>
          <image src='/images/icon_index_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'index'}}"></image>
          首页
        </view>
      </view>
    
      <view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='classify'>
        <view class='my-bar__item-text ak-flex-columnC {{now == "classify" ? "my-bar__item-active" : ""}}'>
          <image src='/images/icon_classify.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'classify'}}"></image>
          <image src='/images/icon_classify_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'classify'}}"></image>
          分类
        </view>
      </view>
    
      <view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='cart'>
        <view class='my-bar__item-text ak-flex-columnC {{now == "cart" ? "my-bar__item-active" : ""}}'>
          <image src='/images/icon_cart.png' style='width: 56rpx; margin-top: -4rpx;' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'cart'}}"></image>
          <image src='/images/icon_cart_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'cart'}}"></image>
          购物车
          <view hidden="{{cartNum <= 0}}" class='my-bar__item-tag ak-flexC'>{{cartNum}}</view>
        </view>
      </view>
    
      <view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='my'>
        <view class='my-bar__item-text2 ak-flex-columnC {{now == "my" ? "my-bar__item-active" : ""}}'>
          <image src='/images/icon_my.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'my'}}"></image>
          <image src='/images/icon_my_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'my'}}"></image>
          我的
        </view>
      </view>
    </view>
    

    c. 在使用的页面:

    pages/index/index.wxml
    <!-- pages/index/index.wxml -->
    <tab-bar wx:if="{{useMyTB}}" now="index" cart-num="{{cartNums}}"></tab-bar>
    
    
    pages/index/index.js :
    Page({
      data: {
        useMyTB: app.globalData.useMyTB,
      },
    
      /**
       * 生命周期函数--监听页面显示
       */
      onShow: function() {
        let _this = this;
        if (!app.globalData.accessToken){
          // Tips: 1、没有有登录状态: 只设置tabbar
          console.log('没有有登录状态: 只设置tabbar')
          if (typeof this.getTabBar === 'function' &&
            this.getTabBar()) {
            this.getTabBar().setData({
              ['selected']: 0
            })
          }
        }else{
          // Tips: 2、有登录状态: 拿购物车 用户消息 优惠券数据
          console.log('有登录状态: 拿购物车 用户消息 优惠券数据')
          
          app.myRequest('cartNum', {}, 'get',
            function (res) {
              if (typeof _this.getTabBar === 'function' &&
                _this.getTabBar()) {
                _this.getTabBar().setData({
                  ['selected']: 0,
                  ['list[2].tagNum']: res.data
                })
              }
              _this.setData({
                cartNums: res.data
              })
            }, 'top');
    
          app.myRequest('centerInfo', {},
            'get',
            function (res) {
              _this.setData({
                msg_num: res.data.msg_num,
                infoData: res.data,
                hiddenBindPhoneBlock: res.data.member.phone == '' ? false : true
              })
            }, 'top');
          this.showCoupons();
        }
      },
    

    2. 隐藏官方的tabBar

    首先我们要判断版本,如果低于2.5.0的话,
    做兼容, 隐藏官方的tabBar用自定义的tabBar;
    app.js的onLaunch中检查版本做适配

    // 做适配
        wx.getSystemInfo({
          success: function (res) {
            // 判断SDK版本
            let sdkv = res.SDKVersion;
            console.log('当前版本: ' + sdkv)
            let basicsVersion = [2, 5, 0]
            sdkv = sdkv.split(".");
            for (let i in sdkv) {
              if (parseInt(basicsVersion[i]) > parseInt(sdkv[i])) {
                console.warn('当前版本小于2.5.0')
                wx.hideTabBar();
                _this.globalData.useMyTB = true;
              }
            }
          },
        })
    

    3. 控制自己的tabBar组建的显示和隐藏

    其实上面代码有写啦 搜索 useMyTB

    以上这些就是我对小程序自定义tabBar的理解和应用了, 欢迎指点

    参考文档:

    小程序官方的tabBar文档
    小程序官方的Component文档
    小程序app.json的配置

    相关文章

      网友评论

        本文标题:微信小程序自定义tabBar

        本文链接:https://www.haomeiwen.com/subject/jamggqtx.html