uniapp开发微信小程序自定义导航栏

uniapp开发微信小程序自定义导航栏

在开发小程序过程中,我们经常会遇到官方提供的导航栏无法满足业务需求的情况,这个时候就需要我们自定义一个顶部导航栏了,在自定义顶部导航的时候我们要考虑对各设备的兼容性,我们都知道,每款机型的状态栏高度都不一定是相等的,还是胶囊的位置,所以在自定义导航栏组件时要充分考虑在各个设备上的兼容性

话不多说直接上代码!!!

vue 复制代码
<template>
  <view class="navigation-bar" :class="extClass">
    <view 
      class="navigation-bar__inner" 
      :class="ios ? 'ios' : 'android'"
      :style="{
        color: color,
        background: background,
        ...displayStyleObj,
        ...innerPaddingRightObj,
        ...safeAreaTopObj
      }"
    >
      <!-- 左侧按钮 -->
      <view class="navigation-bar__left" :style="leftWidthObj">
        <template v-if="back || homeButton">
          <!-- 返回上一页 -->
          <view v-if="back" class="navigation-bar__buttons navigation-bar__buttons_goback">
            <view
              @click="backHandler"
              class="navigation-bar__btn_goback_wrapper"
              :hover-class="hoverClass"
              hover-stay-time="100"
              aria-role="button"
              aria-label="返回"
            >
              <view class="navigation-bar__button navigation-bar__btn_goback"></view>
              <view v-if="showBackText" class="navigation-bar__text">{{ backText }}</view>
            </view>
          </view>
          <!-- 返回首页 -->
          <view v-if="homeButton" class="navigation-bar__buttons navigation-bar__buttons_home">
            <view
              @click="homeHandler"
              class="navigation-bar__btn_home_wrapper"
              :hover-class="hoverClass"
              aria-role="button"
              aria-label="首页"
            >
              <view class="navigation-bar__button navigation-bar__btn_home"></view>
            </view>
          </view>
        </template>
        <template v-else>
          <slot name="left"></slot>
        </template>
      </view>

      <!-- 标题 -->
      <view class="navigation-bar__center">
        <view v-if="loading" class="navigation-bar__loading" aria-role="alert">
          <view
            class="loading"
            aria-role="img"
            aria-label="加载中"
          ></view>
        </view>
        <template v-if="title">
          <text>{{ title }}</text>
        </template>
        <template v-else>
          <slot name="center"></slot>
        </template>
      </view>
      
      <!-- 右侧留空 -->
      <view class="navigation-bar__right">
        <slot name="right"></slot>
      </view>
    </view>
  </view>
</template>

<script>
import { ref, reactive, watch, onMounted } from 'vue';

export default {
  name: 'NavigationBar',
  props: {
    extClass: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    background: {
      type: String,
      default: ''
    },
    color: {
      type: String,
      default: ''
    },
    back: {
      type: Boolean,
      default: true
    },
    loading: {
      type: Boolean,
      default: false
    },
    homeButton: {
      type: Boolean,
      default: false
    },
    animated: {
      type: Boolean,
      default: true
    },
    show: {
      type: Boolean,
      default: true
    },
    delta: {
      type: Number,
      default: 1
    },
    showBackText: {
        type: Boolean,
        default: false
    },
    backText: {
        type: String,
        default: '返回'
    }
  },
  emits: ['back', 'home'],
  setup(props, { emit }) {
    const ios = ref(false);
    const innerPaddingRight = ref('');
    const leftWidth = ref('');
    const safeAreaTop = ref('');
    const displayStyle = ref('');
    const hoverClass = ref('active');
    
    const displayStyleObj = reactive({});
    const innerPaddingRightObj = reactive({});
    const leftWidthObj = reactive({});
    const safeAreaTopObj = reactive({});

    // 显示状态变化处理
    const showChange = (show) => {
      if (props.animated) {
        displayStyleObj.opacity = show ? '1' : '0';
        displayStyleObj.transition = 'opacity 0.5s';
      } else {
        displayStyleObj.display = show ? '' : 'none';
      }
    };

    // 监听 show 属性变化
    watch(() => props.show, (newVal) => {
      showChange(newVal);
    }, { immediate: true });

    // 返回按钮处理
    const backHandler = () => {
      if (props.delta) {
        uni.navigateBack({
          delta: props.delta
        });
      }
      emit('back', { delta: props.delta });
    };

    // 首页按钮处理
    const homeHandler = () => {
      emit('home');
    //   实际项目中可能需要跳转到首页
    //   uni.switchTab({ url: '/pages/index/index' });
    };

    // 初始化导航栏样式
    const initNavigationBar = () => {
      try {
        const rect = uni.getMenuButtonBoundingClientRect();
        const windowInfo = uni.getWindowInfo();
        const deviceInfo = uni.getDeviceInfo();
        
        const isAndroid = deviceInfo.platform === 'android';
        const isDevtools = deviceInfo.platform === 'devtools';
        let safeAreaTopValue = windowInfo.safeArea?.top || windowInfo.statusBarHeight;
        
        if (isAndroid) {
          if ((!windowInfo.safeArea?.top && windowInfo.statusBarHeight > 0) || 
              windowInfo.safeArea?.top === 0) {
            safeAreaTopValue = windowInfo.statusBarHeight;
          } else {
            safeAreaTopValue = 40;
          }
        }

        ios.value = !isAndroid;
        
        innerPaddingRightObj.paddingRight = `${windowInfo.windowWidth - rect.left}px`;

        leftWidthObj.minWidth = `${windowInfo.windowWidth - rect.left}px`;
        leftWidthObj.maxWidth = `${windowInfo.windowWidth - rect.width}px`;
        
        if (isDevtools || isAndroid) {
          safeAreaTopObj.height = `calc(var(--height) + ${safeAreaTopValue}px)`;
          safeAreaTopObj.paddingTop = `${safeAreaTopValue}px`;
        } else {
          safeAreaTopObj.height = '';
          safeAreaTopObj.paddingTop = '';
        }
      } catch (e) {
        console.error('NavigationBar init error:', e);
      }
    };

    onMounted(() => {
      initNavigationBar();
    });

    return {
      ios,
      innerPaddingRightObj,
      leftWidthObj,
      safeAreaTopObj,
      displayStyleObj,
      hoverClass,
      backHandler,
      homeHandler
    };
  }
};
</script>

<style scoped>
.navigation-bar {
  /* width: 100%;
  position: fixed; */
  --weui-FG-0: rgba(255, 255, 255, 1);
  --height: 44px;
  --left: 16px;
  overflow: hidden;
  color: var(--weui-FG-0);
  flex: none;
}

.navigation-bar .android {
  --height: 48px;
}

.navigation-bar__inner {
  position: relative;
  top: 0;
  left: 0;
  height: calc(var(--height) + env(safe-area-inset-top));
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding-top: env(safe-area-inset-top);
  width: 100%;
  box-sizing: border-box;
}

.navigation-bar__left {
  position: relative;
  padding-left: var(--left);
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  height: 100%;
  box-sizing: border-box;
}

.navigation-bar__btn_goback_wrapper {
  display: flex;
  align-items: center;
  padding: 11px 18px 11px 16px;
  margin: -11px -18px -11px -16px;
}

.navigation-bar__btn_goback_wrapper.active {
  opacity: 0.5;
}

.navigation-bar__btn_home_wrapper {
  display: flex;
  align-items: center;
  padding: 11px 18px 11px 16px;
  margin: -11px -18px -11px -16px;
}

.navigation-bar__btn_home_wrapper.active {
  opacity: 0.5;
}

.navigation-bar__btn_home {
  font-size: 12px;
  width: 22px;
  height: 22px;
  -webkit-mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill='none' version='1.1' width='20' height='20' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CclipPath id='master_svg0_211_16780'%3E%3Crect x='0' y='0' width='20' height='20' rx='0'/%3E%3C/clipPath%3E%3C/defs%3E%3Cg clip-path='url(%23master_svg0_211_16780)'%3E%3Cg%3E%3Cpath d='M1.914707238786316,9.37944225C1.717148598786316,9.37944225,1.522812618786316,9.28602455,1.401328358786316,9.11163045C1.204023636786316,8.82840725,1.2736720437863158,8.43885615,1.556914358786316,8.24155135L9.643497388786315,2.60825122C9.864889988786317,2.454042371,10.160245788786316,2.459295034,10.376015588786316,2.6212787L17.878693388786317,8.25455955C18.154728388786317,8.46182535,18.210469388786315,8.85360285,18.003224388786315,9.12963865C17.795977388786316,9.40567445,17.404200388786315,9.46143575,17.128145388786315,9.25415035L9.987011788786315,3.8923529500000003L2.271386558786316,9.26721625C2.166818558786316,9.34032395,2.042296948786316,9.379503249999999,1.914707238786316,9.37944225ZM16.235177388786315,18.11829075L11.885059388786315,18.11829075C11.539884388786316,18.11829075,11.260059188786316,17.838465749999997,11.260059188786316,17.49329075L11.260059188786316,13.12463875L8.752305888786317,13.12463875L8.752305888786317,17.49329075C8.752305888786317,17.838465749999997,8.472480688786316,18.11829075,8.127304888786316,18.11829075L3.683379488786316,18.11829075C3.338203288786316,18.11829075,3.058379288786316,17.838465749999997,3.058379288786316,17.49329075L3.058379288786316,10.023114249999999C3.058379288786316,9.67793895,3.338203288786316,9.398114249999999,3.683379488786316,9.398114249999999C4.028554988786317,9.398114249999999,4.308379288786316,9.67793895,4.308379288786316,10.023114249999999L4.308379288786316,16.86829075L7.502304888786316,16.86829075L7.502304888786316,12.49963875C7.502304888786316,12.15446285,7.782129688786316,11.87463855,8.127304888786316,11.87463855L11.885059388786315,11.87463855C12.230234388786316,11.87463855,12.510059388786315,12.15446285,12.510059388786315,12.49963875L12.510059388786315,16.86829075L15.610196388786315,16.86829075L15.610196388786315,10.023114249999999C15.610196388786315,9.67793895,15.890021388786316,9.398114249999999,16.235197388786318,9.398114249999999C16.580369388786316,9.398114249999999,16.860197388786318,9.67793895,16.860197388786318,10.023114249999999L16.860197388786318,17.49329075C16.860177388786315,17.83844675,16.580350388786314,18.11829075,16.235177388786315,18.11829075Z' fill='%23FFFFFF' fill-opacity='1' style='mix-blend-mode:passthrough'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") no-repeat 50% 50%;
  mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill='none' version='1.1' width='20' height='20' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CclipPath id='master_svg0_211_16780'%3E%3Crect x='0' y='0' width='20' height='20' rx='0'/%3E%3C/clipPath%3E%3C/defs%3E%3Cg clip-path='url(%23master_svg0_211_16780)'%3E%3Cg%3E%3Cpath d='M1.914707238786316,9.37944225C1.717148598786316,9.37944225,1.522812618786316,9.28602455,1.401328358786316,9.11163045C1.204023636786316,8.82840725,1.2736720437863158,8.43885615,1.556914358786316,8.24155135L9.643497388786315,2.60825122C9.864889988786317,2.454042371,10.160245788786316,2.459295034,10.376015588786316,2.6212787L17.878693388786317,8.25455955C18.154728388786317,8.46182535,18.210469388786315,8.85360285,18.003224388786315,9.12963865C17.795977388786316,9.40567445,17.404200388786315,9.46143575,17.128145388786315,9.25415035L9.987011788786315,3.8923529500000003L2.271386558786316,9.26721625C2.166818558786316,9.34032395,2.042296948786316,9.379503249999999,1.914707238786316,9.37944225ZM16.235177388786315,18.11829075L11.885059388786315,18.11829075C11.539884388786316,18.11829075,11.260059188786316,17.838465749999997,11.260059188786316,17.49329075L11.260059188786316,13.12463875L8.752305888786317,13.12463875L8.752305888786317,17.49329075C8.752305888786317,17.838465749999997,8.472480688786316,18.11829075,8.127304888786316,18.11829075L3.683379488786316,18.11829075C3.338203288786316,18.11829075,3.058379288786316,17.838465749999997,3.058379288786316,17.49329075L3.058379288786316,10.023114249999999C3.058379288786316,9.67793895,3.338203288786316,9.398114249999999,3.683379488786316,9.398114249999999C4.028554988786317,9.398114249999999,4.308379288786316,9.67793895,4.308379288786316,10.023114249999999L4.308379288786316,16.86829075L7.502304888786316,16.86829075L7.502304888786316,12.49963875C7.502304888786316,12.15446285,7.782129688786316,11.87463855,8.127304888786316,11.87463855L11.885059388786315,11.87463855C12.230234388786316,11.87463855,12.510059388786315,12.15446285,12.510059388786315,12.49963875L12.510059388786315,16.86829075L15.610196388786315,16.86829075L15.610196388786315,10.023114249999999C15.610196388786315,9.67793895,15.890021388786316,9.398114249999999,16.235197388786318,9.398114249999999C16.580369388786316,9.398114249999999,16.860197388786318,9.67793895,16.860197388786318,10.023114249999999L16.860197388786318,17.49329075C16.860177388786315,17.83844675,16.580350388786314,18.11829075,16.235177388786315,18.11829075Z' fill='%23FFFFFF' fill-opacity='1' style='mix-blend-mode:passthrough'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") no-repeat 50% 50%;
  -webkit-mask-size: cover;
  mask-size: cover;
  background-color: var(--weui-FG-0);
}

.navigation-bar__btn_goback {
  font-size: 12px;
  width: 17.5px;
  height: 17.5px;
  -webkit-mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' width='100%25' height='100%25'%3E%3Cpath d='M940.8 428.8H282.88l264.96-264.96c32-32 32-84.48 0-117.76s-84.48-32-117.76 0L24.32 453.12c-3.84 3.84-7.68 7.68-10.24 12.8-1.28 2.56-2.56 3.84-3.84 6.4l-3.84 7.68c-1.28 2.56-1.28 5.12-2.56 8.96-1.28 2.56-1.28 5.12-2.56 6.4-2.56 10.24-2.56 21.76 0 32 0 2.56 1.28 5.12 2.56 6.4 1.28 2.56 1.28 5.12 2.56 8.96l3.84 7.68c1.28 2.56 2.56 3.84 3.84 6.4 2.56 5.12 6.4 8.96 10.24 12.8l407.04 407.04c32 32 84.48 32 117.76 0 32-32 32-84.48 0-117.76L282.88 595.2h657.92c46.08 0 83.2-37.12 83.2-83.2s-37.12-83.2-83.2-83.2z' fill='%23ffffff'/%3E%3C/svg%3E") no-repeat 50% 50%;
  mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' width='100%25' height='100%25'%3E%3Cpath d='M940.8 428.8H282.88l264.96-264.96c32-32 32-84.48 0-117.76s-84.48-32-117.76 0L24.32 453.12c-3.84 3.84-7.68 7.68-10.24 12.8-1.28 2.56-2.56 3.84-3.84 6.4l-3.84 7.68c-1.28 2.56-1.28 5.12-2.56 8.96-1.28 2.56-1.28 5.12-2.56 6.4-2.56 10.24-2.56 21.76 0 32 0 2.56 1.28 5.12 2.56 6.4 1.28 2.56 1.28 5.12 2.56 8.96l3.84 7.68c1.28 2.56 2.56 3.84 3.84 6.4 2.56 5.12 6.4 8.96 10.24 12.8l407.04 407.04c32 32 84.48 32 117.76 0 32-32 32-84.48 0-117.76L282.88 595.2h657.92c46.08 0 83.2-37.12 83.2-83.2s-37.12-83.2-83.2-83.2z' fill='%23ffffff'/%3E%3C/svg%3E") no-repeat 50% 50%;
  -webkit-mask-size: cover;
  mask-size: cover;
  background-color: var(--weui-FG-0);
}

.navigation-bar__text {
    font-size: 17.5px;
    margin-left: 10px;
    font-weight: 600;
}

.navigation-bar__center {
  font-size: 17px;
  text-align: center;
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  flex: 1;
  height: 100%;
}

.navigation-bar__loading {
  margin-right: 4px;
  align-items: center;
}

.loading {
  font-size: 16px;
  width: 16px;
  height: 16px;
  display: block;
  background: transparent url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A") no-repeat;
  background-size: 100%;
  margin-left: 0;
  animation: loading linear infinite 1s;
}

@keyframes loading {
  from {
    transform: rotate(0);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>

如果样式icon不符合项目UI的话,后期可以自己进行更改

相关推荐
蓝胖子的小叮当2 分钟前
JavaScript基础(十三)函数柯里化curry
前端·javascript
孪创启航营6 分钟前
数字孪生二维热力图制作,看这篇文章就够了!
前端·three.js·cesium
宫水三叶的刷题日记9 分钟前
真的会玩,钉钉前脚辟谣高管凌晨巡查工位,小编随后深夜发文
前端·后端·面试
zzywxc78718 分钟前
AI 行业应用:金融、医疗、教育、制造业领域的落地案例与技术实现
android·前端·人工智能·chrome·金融·rxjava
Juchecar25 分钟前
Vue 3 + Naive UI 调用useMessage的方法(在Naive UI 2.42.0实测有效)
前端
前端Hardy40 分钟前
HTML&CSS:超酷炫的3D动态卡片
前端·javascript·css
RaidenLiu1 小时前
从 Provider 迈向 Riverpod 3:核心架构与迁移指南
前端·flutter
前端进阶者1 小时前
electron-vite_18Less和Sass共用样式指定
前端
数字人直播1 小时前
稳了!青否数字人分享3大精细化AI直播搭建方案!
前端·后端
江城开朗的豌豆1 小时前
我在项目中这样处理useEffect依赖引用类型,同事直呼内行
前端·javascript·react.js