Nissi商城首页(一):仿唯品会的自定义头部导航栏(完美)

一、前言

最近无意中打开“某品会”的小程序端,发现他们的头部布局为啥和公司开发的小程序不一样,自己的只能文字居中,改变背景色。对方的竟然可以设置2行文字,字体也有大有小,感觉非常的高大上。

某品会小程序首页:

接下来小编带着问题去晚上查相关资料,发现微信7.0.0新增了解决不同屏幕头部问题的终极办法:

wx.getMenuButtonBoundingClientRect()

有了这个神器支持获取胶囊按钮的高度,轻松解决“留海屏” 的适配问题。

二、开发

废话不多说,直接撸代码......

1、代码目录结构

├─components

│   └─navBar

│              navBar.js

│              navBar.json

│              navBar.wxml

│              navBar.wxss 

├─pages

│   └─index

│              index.js

│              index.json

│              index.wxml

│              index.wxss 

2、代码片段

components/navBar/navBar.js部分

Component({
  options: {
    multipleSlots: true,
    addGlobalClass: true
  },
  data: {},
  pageLifetimes: {
    show: function() {
      if (getApp().globalSystemInfo.ios) {
        this.getSystemInfo();
        this.setStyle(); //设置样式1
      }
    },
    hide: function() {}
  },
  methods: {
    setStyle: function(life) {
      const {
        statusBarHeight,
        navBarHeight,
        capsulePosition,
        navBarExtendHeight,
        ios,
        windowWidth
      } = getApp().globalSystemInfo;
      const { back, home, title } = this.data;
      let rightDistance = windowWidth - capsulePosition.right; //胶囊按钮右侧到屏幕右侧的边距
      let leftWidth = windowWidth - capsulePosition.left; //胶囊按钮左侧到屏幕右侧的边距

      let navigationbarinnerStyle = [
        `color: ${this.data.color}`,
        `background: ${this.data.background}`,
        `height:${navBarHeight + navBarExtendHeight}px`,
        `padding-top:${statusBarHeight}px`,
        `padding-right:${leftWidth}px`,
        `padding-bottom:${navBarExtendHeight}px`
      ].join(';');
      let navBarLeft = [];
      if ((back && !home) || (!back && home)) {
        navBarLeft = [`width:${capsulePosition.width}px`, `height:${capsulePosition.height}px`].join(';');
      } else if ((back && home) || title) {
        navBarLeft = [
          `width:${capsulePosition.width}px`,
          `height:${capsulePosition.height}px`,
          `margin-left:${rightDistance}px`
        ].join(';');
      } else {
        navBarLeft = [`width:auto`, `margin-left:0px`].join(';');
      }
      if (life === 'created') {
        this.data = {
          navigationbarinnerStyle,
          navBarLeft,
          navBarHeight,
          capsulePosition,
          navBarExtendHeight,
          ios
        };
      } else {
        this.setData({
          navigationbarinnerStyle,
          navBarLeft,
          navBarHeight,
          capsulePosition,
          navBarExtendHeight,
          ios
        });
      }
    },
    _showChange: function(value) {
      this.setStyle();
    },
    // 返回事件
    back: function() {
      this.triggerEvent('back', { delta: this.data.delta });
    },
    home: function() {
      this.triggerEvent('home', {});
    },
    search: function() {
      this.triggerEvent('search', {});
    },
    getSystemInfo() {
      var app = getApp();
      if (app.globalSystemInfo && !app.globalSystemInfo.ios) {
        return app.globalSystemInfo;
      } else {
        let systemInfo = wx.getSystemInfoSync();
        let ios = !!(systemInfo.system.toLowerCase().search('ios') + 1);
        let rect;
        try {
          rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null;
          if (rect === null) {
            throw 'getMenuButtonBoundingClientRect error';
          }
          //取值为0的情况  有可能width不为0 top为0的情况
          if (!rect.width || !rect.top || !rect.left || !rect.height) {
            throw 'getMenuButtonBoundingClientRect error';
          }
        } catch (error) {
          let gap = ''; //胶囊按钮上下间距 使导航内容居中
          let width = 96; //胶囊的宽度
          if (systemInfo.platform === 'android') {
            gap = 8;
            width = 96;
          } else if (systemInfo.platform === 'devtools') {
            if (ios) {
              gap = 5.5; //开发工具中ios手机
            } else {
              gap = 7.5; //开发工具中android和其他手机
            }
          } else {
            gap = 4;
            width = 88;
          }
          if (!systemInfo.statusBarHeight) {
            //开启wifi的情况下修复statusBarHeight值获取不到
            systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
          }
          rect = {
            //获取不到胶囊信息就自定义重置一个
            bottom: systemInfo.statusBarHeight + gap + 32,
            height: 32,
            left: systemInfo.windowWidth - width - 10,
            right: systemInfo.windowWidth - 10,
            top: systemInfo.statusBarHeight + gap,
            width: width
          };
          console.log('error', error);
          console.log('rect', rect);
        }

        let navBarHeight = '';
        if (!systemInfo.statusBarHeight) {
          systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
          navBarHeight = (function() {
            let gap = rect.top - systemInfo.statusBarHeight;
            return 2 * gap + rect.height;
          })();

          systemInfo.statusBarHeight = 0;
          systemInfo.navBarExtendHeight = 0; //下方扩展4像素高度 防止下方边距太小
        } else {
          navBarHeight = (function() {
            let gap = rect.top - systemInfo.statusBarHeight;
            return systemInfo.statusBarHeight + 2 * gap + rect.height;
          })();
          if (ios) {
            systemInfo.navBarExtendHeight = 4; //下方扩展4像素高度 防止下方边距太小
          } else {
            systemInfo.navBarExtendHeight = 0;
          }
        }
        systemInfo.navBarHeight = navBarHeight; //导航栏高度不包括statusBarHeight
        systemInfo.capsulePosition = rect; //右上角胶囊按钮信息bottom: 58 height: 32 left: 317 right: 404 top: 26 width: 87 目前发现在大多机型都是固定值 为防止不一样所以会使用动态值来计算nav元素大小
        systemInfo.ios = ios; //是否ios

        app.globalSystemInfo = systemInfo; //将信息保存到全局变量中,后边再用就不用重新异步获取了

        //console.log('systemInfo', systemInfo);
        return systemInfo;
      }
    }
  }
});

components/navBar/navBar.wxml部分

<view class="lxy-nav-bar {
   
   {extClass}}" style="background: {
   
   {backgroundColorTop}};height: {
   
   {navBarHeight+ navBarExtendHeight}}px;">
    <view class="lxy-nav-bar__placeholder {
   
   {ios ? 'ios' : 'android'}}" style="padding-top: {
   
   {navBarHeight+ navBarExtendHeight}}px;visibility: hidden;"></view>
    <view class="lxy-nav-bar__inner {
   
   {ios ? 'ios' : 'android'}}" style="{
   
   {navigationbarinnerStyle}}{
   
   {displayStyle}}">
        <view class='lxy-nav-bar__left' style="{
   
   {navBarLeft}}">
            <block wx:if="{
   
   {back&&!home}}">
                <view bindtap="back" class="lxy-nav-bar__button lxy-nav-bar__btn_goback {
   
   {iconTheme}}"></view>
            </block>
            <block wx:if="{
   
   {!back&&home}}">
                <view bindtap="home" class="lxy-nav-bar__button lxy-nav-bar__btn_gohome {
   
   {iconTheme}}"></view>
            </block>
            <block wx:elif="{
   
   {back&&home}}">
                <view class="lxy-nav-bar__buttons {
   
   {ios ? 'ios' : 'android'}}" wx:if="{
   
   {home}}">
                    <view bindtap="back" class="lxy-nav-bar__button lxy-nav-bar__btn_goback {
   
   {iconTheme}}" wx:if="{
   
   {back}}"></view>
                    <view bindtap="home" class="lxy-nav-bar__button lxy-nav-bar__btn_gohome {
   
   {iconTheme}}"></view>
                </view>
            </block>
            <block wx:else>
                <slot name="left"></slot>
            </block>
        </view>
        <view class='lxy-nav-bar__right'>
            <slot name="right"></slot>
        </view>
    </view>
</view>

pages/index/index.wxml部分

<view class="custom_head" style="height:{
   
   {navHeight}}px;">
  <view class="flex-row j_b" style="height:{
   
   {navObj}}px;padding-top:{
   
   {navTop}}px;padding-right:{
   
   {navObjWid+5}}px;">
    <view class="head_store text_title">
      {
   
   {store_name}}
    </view>
  </view>
</view>

pages/index/index.js部分

const app = getApp();
Page({

  /**
   * 页面的初始数据
   */
  data: {
    navHeight: app.globalData.navHeight, //导航栏高度
    navTop: app.globalData.navTop, //导航栏距顶部距离
    navObj: app.globalData.navObj, //胶囊的高度
    navObjWid: app.globalData.navObjWid, //胶囊宽度+距右距离
    storeList: [{
      name: '品牌特卖 100%正品'
    }],
  }
}

此处省略样式部分代码块......

三、结果

总结:

  • 通过微信官方API: getMenuButtonBoundingClientRect(),可以解决各类手机屏幕的适配问题。
  • 将算好的参数存储在全局变量中,只需一次计算就可以全局使用。

 四、源码
Nissi商城微信小程序: NissI商城微信小程序:主要是提供给前端开发人员学习的电商项目,为了达到真实业务场景整体风格借鉴了“唯品会特卖”商城,小编会不断更新并丰富功能。如果有想要学习小程序的同学,可以加入进来一起开发。语言:微信小程序原生https://gitee.com/itunion/nissi-mall-wechat-applet

猜你喜欢

转载自blog.csdn.net/zhenghhgz/article/details/125691093
今日推荐