uniapp之如何开发微信小程序中自适应的导航栏

lxf2023-04-11 08:48:01

前言

使用uniapp开发微信小程序时,小程序右上角会有一个胶囊按钮,如果随便定义自定义的导航栏的高度,就会出现被遮盖的情况,特别是ios13+的手机下。理想情况是导航栏标题与胶囊按钮对齐。

布局

导航是需要占位的但是又是需要固定在头部的,以便滚动时导航栏固定不动。这里就可以拆分为两个内容,一个用于dom正常流式布局占位,一个用于固定显示。

    // BaseNavigation.vue
    <view class="navigation-box">
        <view class="fixed-navigation">
          <view class="navigation">
            <view class="navigation-container">
              <view class="left">
                <image class="icon" @click="onBack" :src="`/static/images/public/back.png`"/>
              </view>
              <view class="center">{{ title }}</view>
              <view class="right"></view>
            </view>
          </view>
        </view>
      </view>
      navigation-box 最外层的也是占位的 它只需要有个高度即可
      fixed-navigation 固定定位的元素,背景色的设置
      navigation 设置内容高度
      navigation-container 内容所在区域,返回按钮 标题
      

如何获取准确的高度对导航栏设置

通过 uni.getMenuButtonBoundingClientRect() 可以获取胶囊按钮的 top bottom height width left 等属性,利用这个api即可进行对应高度计算。

// navigationMixin.js
const navigationMixin = {
  data: function() {
    return {
      WXBUTTONINFO: {},
      WXNAVIGATIONGAP: 8, // 距离胶囊按钮底部的间隙px,
      navigationHeight: 0
    };
  },
  created: function() {
    // #ifdef MP-WEIXIN
    this.WXBUTTONINFO = uni.getMenuButtonBoundingClientRect();
    // #endif

    // #ifdef H5
    this.WXBUTTONINFO = { height: 30, width: 100, top: 50, bottom: 80, left: 300 };
    // #endif

    this.navigationHeight = this.WXBUTTONINFO.bottom + this.WXNAVIGATIONGAP; // 直接获取按钮底部位置 + 间隙
  },
  computed: {
    computedNavigationStyle: function() { // 导航栏总高度
      return `height: ${this.navigationHeight}px`;
    },
    computedNavigationContainerStyle: function() {
      const { top, height } = this.WXBUTTONINFO; 
      return `margin-top: ${top}px; height: ${height}px`; // 导航栏内容高度 和 设置的 margin top
    }
  },
  mounted: function() {},
  methods: {}
};

export default navigationMixin;

设置dom对应动态高度

依次把定义好的fiexd高度,内容高度进行设置

  // BaseNavigation.vue
  <view class="navigation-box">
    <view class="fixed-navigation">
      <view class="navigation" :style="computedNavigationStyle">
        <view class="navigation-container" :style="computedNavigationContainerStyle">
          <view class="left">
            <image class="icon" @click="onBack" :src="`/static/images/public/back.png`"/>
          </view>
          <view class="center">{{ title }}</view>
          <view class="right"></view>
        </view>
      </view>
    </view>
  </view>
  navigation 总高度
  computedNavigationContainerStyle 内容高度

导航栏主题切换

大家都知道导航栏有深色或者浅色主题,这里我们也需要进行设置,深色就用白色返回图片,浅色就用黑色返回图片。

  // BaseNavigation.vue
  <view class="navigation-box">
    <view class="fixed-navigation" :class="type === `white` ? `` : `black-box`">
      <view class="navigation" :style="computedNavigationStyle">
        <view class="navigation-container" :style="computedNavigationContainerStyle">
          <view class="left">
            <image class="icon" @click="onBack" ::src="`/static/images/public/${type === `white` ? `l_arrow_w_icon` : `l_arrow_icon`}.png`"/>
          </view>
          <view class="center">{{ title }}</view>
          <view class="right"></view>
        </view>
      </view>
    </view>
  </view>
  type 通过这个字段进行区分

动态导航栏主题

我们见过很多应用中滚动一个页面时,顶部导航栏会变色,这种情况我们应该怎么样实现呢

  // BaseNavigation.vue
  computed: {
    navigationFixedStyle: function() {
      const bg = this.navigationActive ? this.activeBg : this.defaultBg;
      return `background: ${bg}`;
    },
    navigationStyle: function() { // 无效占位高度时设置
      const height = this.fixed ? 0.1 : this.navigationHeight;
      return `height: ${height}px`;
    }
  },
  
  通过 navigationActive 来判断是否页面滚动了并随之更新主题色

navigationActive从何而来?

  // 定义一个滚动相关的函数
  // navigationMixin
  onPageScroll({ scrollTop }) {
    this.navigationActive = scrollTop > this.scollHeight; // scollHeight = 46
  },

定义props和返回函数

  props: {
    fixed: { type: Boolean, default: false }, // 判定是否需要导航栏高度 沉浸式背景图使用自定义导航时无需高度
    title: { type: String, default: "" },
    type: { type: String, default: "white" },
    defaultBg: { type: String, default: "" }, // #FFFFFF
    activeBg: { type: String, default: "" },
    navigationActive: { type: Boolean }
  },
  mixins: [navigationMixin],
  methods: {
    onBack() {
      uni.navigateBack({
        delta: 1,
        success: (success) => {
          console.log("navigateBack success", success);
        },
        fail: (fail) => {
          console.log("navigateBack fail", fail);
          uni.switchTab({ url: `/pages/tabBarHome/index` });
        }
      });
    },
  }

class style

<style lang="scss" scoped>

  .black-box{
    .center{
      color: #000000 !important;
    }
  }
  .navigation-box{
    flex-shrink: 0; // base-layout flex 模式下 高度不被挤压
    // height: calc(88rpx + var(--status-bar-height));
    background: none;
  }
  .fixed-navigation{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    transition: all 200ms;
    z-index: 98;
    .navigation{
      display: flex;
    }
    .navigation-container{
      width: 100%;
      display: flex;
      justify-content: space-between;
      align-items: center;
      .left{
        flex: 1;
        display: flex;
        align-items: center;
        .icon{ width: 18rpx; height: 30rpx; padding: 12rpx 32rpx; }
      }
      .center{
        flex: 1;
        display: flex;
        align-items: center;
        font-weight: 700;
        font-size: 32rpx;
        color: #fff;
        justify-content: center;
      }
      .right{ // 占位
        display: flex;
        flex: 1;
      }
    }
  }
</style>

实际使用和效果预览

<BaseNavigation
  :navigationActive="navigationActive"
  title="确认订单"
  type="white"
  defaultBg="#43A9FE"
  activeBg="#6DD0F9"
/>
navigationActive 需要在你用这个组件的页面导入navigationMixin, 否则navigationActive不存在

预览图

uniapp之如何开发微信小程序中自适应的导航栏

滚动图

uniapp之如何开发微信小程序中自适应的导航栏

uniapp之如何开发微信小程序中自适应的导航栏

总结

关于微信上如何自定义导航栏就分享结束了,这个组件还有很多扩展的空间, 比如在没有胶囊按钮的小程序中如何定义,编译成APP后这个导航栏又该如何定义,有需求的小伙伴可自行探索。我这里给出一个小设计思路希望对大家有用。

  1. 定义一个文件夹BaseNavigation,在下面创建多个场景的导航栏文件,比如 wxNavigation, appNavigation
  2. 定义一个index.vue; 导入你编写的所有环境的导航栏,通过uniapp的条件编译在dom中分别写好即可
<view class="all-navigation-box">
<!-- #ifdef MP-WEIXIN -->
    <WxNavigation props="props"/>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
    <AppNavigation props="props"/>
<!-- #endif -->
</view>