美文网首页小程序
[打怪升级]小程序自定义头部导航栏“完美”解决方案

[打怪升级]小程序自定义头部导航栏“完美”解决方案

作者: 前端智酷方程 | 来源:发表于2019-10-22 15:52 被阅读0次
    image

    为什么要做这个?

    主要是在项目中,智酷君发现的一些问题

    • 一些页面是通过扫码和订阅消息访问后,没有直接可以点击去首页的,需要添加一个home链接
    • 需要添加自定义搜索功能
    • 需要自定义一些功能按钮
    某些自定义头部功能

    其实,第一个问题,在最近的微信版本更新中已经优化了,通过 小程序模板消息 过来的,系统会自动加上home按钮,但对于其他的访问方式则没有支持~

    一个不大不小的问题:两边ICON不对齐问题

    头部icon对齐问题

    智酷君之前尝试了各种解决方法,发现有一个问题,就是现在手机屏幕太多种多样,有 传统头部、宽/窄刘海屏、水滴屏等等,无法八门,很多解决方案都无法解决特殊头部,系统“胶囊按钮” 和 自定义按钮在Android屏幕可能有 几像素不对齐 的问题(强迫症的噩梦)。

    下面分享下一个相对比较完善的解决方案:

    自定义头部导航

    小程序代码段DEMO

    Link: https://developers.weixin.qq.com/s/cuUaCimT72cH
    ID: cuUaCimT72cH

    智酷君做了一个demo代码段,方便大家直接用IDE工具查看源码~


    IDE工具打开代码段

    页面配置

    1、页面JSON配置
    {
      "usingComponents": {
        "NavComponent": "/components/nav/common"  //以插件的方式引入
      },
      "navigationStyle": "custom"  //自定义头部需要设置
    }
    

    如果需要自定义头部,需要设置navigationStyle为 “custom”

    2、页面代码
    <!-- home 类型的菜单 -->
    <NavComponent v-title="自定义头部" bind:commonNavAttr="commonNavAttr"></NavComponent>
    <!-- 搜索菜单 -->
    <NavComponent is-search="true" bind:commonNavAttr="commonNavAttr"></NavComponent>
    

    可以在自定义导航标签上添加属性配置来设置功能,具体按照实际需要来

    3、目录结构
    │
    ├─components
    │  └─nav
    │          common.js
    │          common.json
    │          common.wxml
    │          common.wxss
    │
    ├─images
    │      back.png
    │      home.png
    │
    └─index
            index.js
            index.json
            index.wxml
            index.wxss
            search.js
            search.json
            search.wxml
            search.wxss
    

    仅供参考

    插件对应的JS部分

    components/nav/common.js部分
    const app = getApp();
    Component({
      properties: {
        vTitle: {
          type: String,
          value: ""
        },
        isSearch:{
          type: Boolean,
          value: false
        }
      },
      data: {
        haveBack: true, // 是否有返回按钮,true 有 false 没有 若从分享页进入则没有返回按钮
        statusBarHeight: 0, // 状态栏高度
        navbarHeight: 0, // 顶部导航栏高度
        navbarBtn: { // 胶囊位置信息
          height: 0,
          width: 0,
          top: 0,
          bottom: 0,
          right: 0
        },
        cusnavH: 0, //title高度
      },
      // 微信7.0.0支持wx.getMenuButtonBoundingClientRect()获得胶囊按钮高度
      attached: function () {
        if (!app.globalData.systeminfo) {
          app.globalData.systeminfo = wx.getSystemInfoSync();
        }
        if (!app.globalData.headerBtnPosi) app.globalData.headerBtnPosi = wx.getMenuButtonBoundingClientRect();
        console.log(app.globalData)
        let statusBarHeight = app.globalData.systeminfo.statusBarHeight // 状态栏高度
        let headerPosi = app.globalData.headerBtnPosi // 胶囊位置信息
        console.log(statusBarHeight)
        console.log(headerPosi)
        let btnPosi = { // 胶囊实际位置,坐标信息不是左上角原点
          height: headerPosi.height,
          width: headerPosi.width,
          top: headerPosi.top - statusBarHeight, // 胶囊top - 状态栏高度
          bottom: headerPosi.bottom - headerPosi.height - statusBarHeight, // 胶囊bottom - 胶囊height - 状态栏height (胶囊实际bottom 为距离导航栏底部的长度)
          right: app.globalData.systeminfo.windowWidth - headerPosi.right // 这里不能获取 屏幕宽度,PC端打开小程序会有BUG,要获取窗口高度 - 胶囊right
        }
        let haveBack;
        if (getCurrentPages().length != 1) { // 当只有一个页面时,并且是从分享页进入
          haveBack = false;
        } else {
          haveBack = true;
        }
        var cusnavH = btnPosi.height + btnPosi.top + btnPosi.bottom // 导航高度
        console.log( app.globalData.systeminfo.windowWidth, headerPosi.width)
        this.setData({
          haveBack: haveBack, // 获取是否是通过分享进入的小程序
          statusBarHeight: statusBarHeight,
          navbarHeight: headerPosi.bottom + btnPosi.bottom, // 胶囊bottom + 胶囊实际bottom
          navbarBtn: btnPosi,
          cusnavH: cusnavH
        });
        //将实际nav高度传给父类页面
        this.triggerEvent('commonNavAttr',{
          height: headerPosi.bottom + btnPosi.bottom
        });
      },
      methods: {
        _goBack: function () {
          wx.navigateBack({
            delta: 1
          });
        },
        bindKeyInput:function(e){
          console.log(e.detail.value);
        }
      }
    })
    

    解决不同屏幕头部不对齐问题的终极办法是 wx.getMenuButtonBoundingClientRect()
    这个方法从微信7.0.0开始支持,通过这个方法我们可以获取到右边系统胶囊的top、height、right等属性,这样无论是水滴屏、刘海屏、异形屏,都能完美对齐右边系统默认的胶囊bar,完美治愈强迫症~

    APP.js 部分
    //app.js
    App({
      /**
       * 加载页面
       * @param {*} options 
       */
      onShow: function (options) {
       
      },
      onLaunch: async function () {
        let self = this;
    
        //设置默认分享
        this.globalData.shareData = {
          title: "智酷方程式"
        }
    
        // this.getSysInfo();
      },
      globalData: {
        //默认分享文案
        shareData: {},
        qrCodeScene: false, //二维码扫码进入传参
        systeminfo: false,   //系统信息
        headerBtnPosi: false,  //头部菜单高度
      }
    });
    

    将获取的参数存储在一个全局变量globalData中,可以减少反复调用的性能消耗。

    插件HTML部分

    <view class="custom_nav" style="height:{{navbarHeight}}px;">
        <view class="custom_nav_box" style="height:{{navbarHeight}}px;">
            <view class="custom_nav_bar" style="top:{{statusBarHeight}}px; height:{{cusnavH}}px;">
                <!-- 搜索部分-->
                <block wx:if="{{isSearch}}">
                    <input class="navSearch"
                        style="height:{{navbarBtn.height-2}}px;line-height:{{navbarBtn.height-4}}px; top:{{navbarBtn.top+1}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;"
                        maxlength="10" bindinput="bindKeyInput" placeholder="输入文字搜索" />
                </block>
                <!-- HOME 部分-->
                <block wx:else>
                    <view class="custom_nav_icon {{!haveBack||'borderLine'}}"
                        style="height:{{navbarBtn.height}}px;line-height:{{navbarBtn.height-2}}px; top:{{navbarBtn.top}}px; left:{{navbarBtn.right}}px; border-radius:{{navbarBtn.height/2}}px;">
                        <view wx:if="{{haveBack}}" class="icon-back" bindtap='_goBack'>
                            <image src='/images/back.png' mode='aspectFill' class='back-pre'></image>
                        </view>
                        <view wx:if="{{haveBack}}" class='navbar-v-line'></view>
                        <view class="icon-home">
                            <navigator class="home_a" url="/pages/home/index" open-type="switchTab">
                                <image src='/images/home.png' mode='aspectFill' class='back-home'></image>
                            </navigator>
                        </view>
                    </view>
                    <view class="nav_title" style="height:{{cusnavH}}px; line-height:{{cusnavH}}px;">
                        {{vTitle}}
                    </view>
                </block>
            </view>
        </view>
    </view>
    

    主要是对几种状态的判断和定位的计算。

    插件CSS部分

    /* components/nav/test.wxss */
    .custom_nav {
        width: 100%;
        background: #3a7dd7;
        position: relative;
        z-index: 99999;
    }
    .custom_nav_box {
        position: fixed;
        width: 100%;
        background: #3a7dd7;
        z-index: 99999;
        border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
    }
    .custom_nav_bar {
        position: relative;
        z-index: 9;
    }
    .custom_nav_box .nav_title {
        font-size: 28rpx;
        color: #fff;
        text-align: center;
        position: absolute;
        max-width: 360rpx;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        z-index: 1;
    }
    .custom_nav_box .custom_nav_icon {
        position:absolute;
        z-index: 2;
        display: inline-block;
        border-radius: 50%;
        vertical-align: top;
        font-size:0;
        box-sizing: border-box;
    }
    .custom_nav_box .custom_nav_icon.borderLine {
        border: 1rpx solid rgba(255, 255, 255, 0.3);
        background: rgba(0, 0, 0, 0.1);
    }
    .navbar-v-line {
        width: 1px;
        margin-top: 14rpx;
        height: 32rpx;
        background-color: rgba(255, 255, 255, 0.3);
        display: inline-block;
        vertical-align: top;
    }
    .icon-back {
        display: inline-block;
        width: 74rpx;
        padding-left: 20rpx;
        vertical-align: top;
        /* margin-top: 12rpx;
        vertical-align: top; */
        height: 100%;
    }
    .icon-home {
        /* margin-top: 8rpx;
        vertical-align: top; */
        display: inline-block;
        width: 80rpx;
        text-align: center;
        vertical-align: top;
        height: 100%;
    }
    .icon-home .home_a {
        height: 100%;
        display: inline-block;
        vertical-align: top;
        width: 35rpx;
    }
    .custom_nav_box .back-pre,
    .custom_nav_box .back-home {
        width: 35rpx;
        height: 35rpx;
        vertical-align: middle;
    }
    .navSearch {
      width: 200px;
      background: #fff;
      font-size: 14px;
      position: absolute;
      padding: 0 20rpx;
      z-index: 9;
    }
    

    总结

    • 通过微信API:getMenuButtonBoundingClientRect(),结果各类手机屏幕的适配问题
    • 将算好的参数存储在全局变量中,一次计算全局使用,爽YY~

    往期回顾:

    相关文章

      网友评论

        本文标题:[打怪升级]小程序自定义头部导航栏“完美”解决方案

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