微信小程序全栈开发实践 第二章 微信小程序组件介绍及使用 -- 2.9 页面链接组件,如何自定义一个导航栏?

本文详细介绍了小程序中如何使用自定义导航栏,包括ext-class属性控制样式、组件源码解析、动画效果展示,以及如何通过外部控制组件样式和功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、小程序中的导航组件

functional-page-navigator  仅在插件中有效,用于跳转到插件功能页。
navigator 小程序标准的导航组件

在这里插入图片描述

小程序插件是对一些js接口、自定义组件或页面的封装,可以嵌入到小程序中去使用。
插件不能独立运行,它必须嵌入到其他小程序中才能被用户使用。
而使用第三方插件的时候,我们也无法看到插件的源码。
插件适合用来封装功能或者是服务,提供给第三方去使用

二、自定义导航栏

2.1 组件源码解析

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

组件配置页的styleIsolation配置字段一共有3个值
1. isolated 启用样式隔离,两者都不会相互影响
2. apply-shared 单向影响,页面的样式可以影响组件,但是组件中的样式不能反过来影响页面
3. shared  页面的样式既能影响组件,组件中的样式也能影响页面

在这里插入图片描述

在ext-class中设置的样式名写在最前面,.navigator-normal是组件的状态,重写icon-back
这样就实现了根据不同的状态,进行样式的重写

上面就是关于ext-class属性控制实现的机制


样式变量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 组件源码

/components/navigation-bar/index.wxml

<view class="weui-navigation-bar {{ext-class}}" bindtouchstart="doubleClick">
	
	<!-- 标题栏 -->
<view class="{{active ? 'navigator-active' : 'navigator-normal'}} weui-navigation-bar__inner {{ios ? 'ios' : 'android'}}" style="padding-top: {{statusBarHeight}}px;{{innerPaddingRight}};{{innerWidth}};">

		<view class='weui-navigation-bar__left' style="{{leftWidth}}">
			<slot name="left"></slot>
		</view>

		<view class='weui-navigation-bar__center'>
			<view wx:if="{{loading}}" class="weui-navigation-bar__loading"></view>
      <block wx:if="{{active}}">
			  <slot name="center"></slot>
      </block>
		</view>
	</view>
</view>

/components/navigation-bar/index.js

"use strict";
var app = getApp();

Component({
  options: {
    // 多插槽支持
    multipleSlots: true
    // ,addGlobalClass: true,开启addGlobalClass,在组件样式在外面可以重写,现在有更好的方式  index.json  "styleIsolation": "apply-shared",
  },
  properties: {
    /*
      实现这种机制,
      1.首先在自定义组件里面声明ext-class属性

       'ext-class': {
          type: String,
          value: ''
        },

      2.并且将这个属性绑定到组件的wxml代码上面

      class="weui-navigation-bar {{ext-class}}" 

      3.在json文件中开始样式穿透,允许页面上的样式可以影响组件内的样式

      {
        "styleIsolation": "apply-shared",
      }
      
    */
    'ext-class': {
      type: String,
      value: ''
    },
    loading: {
      type: Boolean,
      value: false
    },
    active: {
      type: Boolean,
      value: false
      // ,observer: '_showChange'
    }
  },
  data: {},
  /*
  attached 这个函数在组件被加载至页面节点树之后,它会自动执行 
  在这个函数里面通过查询系统信息,决定了IOS变量是否为真
  通过getSystemInfo查询系统个信息,
  然后再去查询一下当前系统是不是说IOS系统
  */

  attached: function attached() {
    // 组件被加载时,计算ios、android两个平台的尺寸差异
    var _this = this;
    //动态计算导航栏尺寸
    var isSupport = !!wx.getMenuButtonBoundingClientRect;
    var rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null;
    //  通过getSystemInfo查询系统个信息,
    wx.getSystemInfo({
      success: function success(res) {
        // 然后再去查询一下当前系统是不是说IOS系统
        var ios = !!(res.system.toLowerCase().search('ios') + 1);
        var statusBarHeight = res.statusBarHeight;
        var topBarHeight = ios ? (44 + statusBarHeight) : (48 + statusBarHeight);

        _this.setData({
          ios: ios,
          topBarHeight: topBarHeight,
          statusBarHeight: statusBarHeight,
          innerWidth: isSupport ? 'width:' + rect.left + 'px' : '',
          innerPaddingRight: isSupport ? 'padding-right:' + (res.windowWidth - rect.left) + 'px' : '',
          leftWidth: isSupport ? 'width:' + (res.windowWidth - rect.left) + 'px' : ''
        });
        // 这是为了在外面注入wxss变量,这个设计并不太好
        // _this.triggerEvent('getBarInfo', {topBarHeight,statusBarHeight});               
      }
    });

    //back箭头处理的显示
    var pages = getCurrentPages()
    if (pages.length > 1) {
      this.setData({
        showBack: true
      })
    }
  },
  methods: {
    // _showChange: function _showChange(active) {           
    //   var displayStyle = 'opacity: ' + (active ? '1' : '0') + ';-webkit-transition:opacity 0.5s;transition:opacity 0.5s;';           
    //   this.setData({
    //       displayStyle: displayStyle
    //   });
    // },
    //双击返回顶部
    doubleClick(e) {
      if (this.timeStamp && (e.timeStamp - this.timeStamp < 300)) {
        this.timeStamp = 0
        wx.pageScrollTo({
          scrollTop: 0,
          duration: 300
        })
      } else {
        this.timeStamp = e.timeStamp
      }
    }
  }
});

/components/navigation-bar/index.json

{
  "styleIsolation": "apply-shared",
  "component": true,
  "usingComponents": {}
}

/components/navigation-bar/index.wxss

/* 微信小程序wxss中使用var变量 */
page {
  /* 
  在wxs文件里面,可以使用两个连字符加一个标识符,作为名称声明一个样式变量  --height: 44px;
  px、rpx都可以

  使用
  height: var(--height);

  对于var变量还可以使用calc这个样式函数进行四则运算,加减乘除都可以,
  px、rpx、100%都可以放在calc函数里一起计算
  小程序会自动帮我们处理单位之间的差异
  
  为什么使用样式变量
  主要是因为方便实现两个不同平台之间的兼容
  有两个作用
  第一,可以控制样式中的同样的数值,在修改的时候只需要修改变量的值,所有的地方都修改
  第二,我们在自定义组件中,需要处理IOS、Android 两种机型,IOS导航栏高度是44px高度,Android则是48px

  */
  --height: 44px;
  --right: 190rpx;
}

.weui-navigation-bar {
  overflow: hidden
}
/* 
ios、android两个平台导航栏的高度不一样
ios 44px
android 48px
 */
.weui-navigation-bar .android {
  --height: 48px;
  --right: 222rpx
}

.weui-navigation-bar__placeholder {
  height: var(--height);
  background: #F8F8F8;
  position: relative;
  z-index: 50
}


.weui-navigation-bar__inner {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 5001;
  height: var(--height);
  display: flex;
  align-items: center;
  padding-right: var(--right);
  width: calc(100% - var(--right))
}

.weui-navigation-bar__inner.navigation-bar-border {
  /* border-bottom: 2rpx solid #ddd; */
}

.weui-navigation-bar__inner .weui-navigation-bar__left {
  position: relative;
  width: var(--right);
  padding-left: 15px;
  box-sizing: border-box;
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  align-items: center;
  -webkit-box-pack: center;
}

.weui-navigation-bar__inner .weui-navigation-bar__left .btn-back {
  width: 12px;
  height: 24px;
}

.weui-navigation-bar__inner .weui-navigation-bar__left .btn-back image {
  width: 100%;
  height: 100%;
}

.weui-navigation-bar__left .btn-home {
  width: 20px;
  height: 20px;
}

.weui-navigation-bar__left .btn-back+.btn-home {
  margin-left: 50rpx;
}

.weui-navigation-bar__left .btn-home image {
  width: 100%;
  height: 100%;
}

.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback:active {
  opacity: .5
}


.weui-navigation-bar__inner .weui-navigation-bar__center {
  font-size: 17px;
  text-align: center;
  position: relative;
  flex: 1;
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  justify-content: center;
  overflow: hidden;
}

.weui-navigation-bar__center text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: -2px;
}

.weui-navigation-bar__inner .weui-navigation-bar__loading {
  width: 20px;
  height: 20px;
  margin-right: 5px;
  content: " ";
  animation: loading-animate 1s steps(12, end) infinite;
  background: transparent url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iciIgd2lkdGg9JzEyMHB4JyBoZWlnaHQ9JzEyMHB4JyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICAgIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJub25lIiBjbGFzcz0iYmsiPjwvcmVjdD4KICAgIDxyZWN0IHg9JzQ2LjUnIHk9JzQwJyB3aWR0aD0nNycgaGVpZ2h0PScyMCcgcng9JzUnIHJ5PSc1JyBmaWxsPScjRTlFOUU5JwogICAgICAgICAgdHJhbnNmb3JtPSdyb3RhdGUoMCA1MCA1MCkgdHJhbnNsYXRlKDAgLTMwKSc+CiAgICA8L3JlY3Q+CiAgICA8cmVjdCB4PSc0Ni41JyB5PSc0MCcgd2lkdGg9JzcnIGhlaWdodD0nMjAnIHJ4PSc1JyByeT0nNScgZmlsbD0nIzk4OTY5NycKICAgICAgICAgIHRyYW5zZm9ybT0ncm90YXRlKDMwIDUwIDUwKSB0cmFuc2xhdGUoMCAtMzApJz4KICAgICAgICAgICAgICAgICByZXBlYXRDb3VudD0naW5kZWZpbml0ZScvPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyM5Qjk5OUEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSg2MCA1MCA1MCkgdHJhbnNsYXRlKDAgLTMwKSc+CiAgICAgICAgICAgICAgICAgcmVwZWF0Q291bnQ9J2luZGVmaW5pdGUnLz4KICAgIDwvcmVjdD4KICAgIDxyZWN0IHg9JzQ2LjUnIHk9JzQwJyB3aWR0aD0nNycgaGVpZ2h0PScyMCcgcng9JzUnIHJ5PSc1JyBmaWxsPScjQTNBMUEyJwogICAgICAgICAgdHJhbnNmb3JtPSdyb3RhdGUoOTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNBQkE5QUEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxMjAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNCMkIyQjInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxNTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNCQUI4QjknCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxODAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNDMkMwQzEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyMTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNDQkNCQ0InCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyNDAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNEMkQyRDInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyNzAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNEQURBREEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgzMDAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNFMkUyRTInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgzMzAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0Pgo8L3N2Zz4=) no-repeat;
  background-size: 100%;
}

@keyframes loading-animate {
  0% {
    transform: rotate3d(0, 0, 1, 0deg);
  }

  100% {
    transform: rotate3d(0, 0, 1, 360deg);
  }
}



/* 激活显示的状态, 非透明,有背景色 */
.navigator-active {
  -webkit-transition: opacity 0.5s;
  transition: opacity 0.5s;
  background: #FFF;
  color: #3c3c3c;
}

.navigator-normal {
  -webkit-transition: opacity 0.5s;
  transition: opacity 0.5s;
  background: transparent;
  color: white;
}
2.3 效果

透明
在这里插入图片描述
白色
在这里插入图片描述

2.4 组件的使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 组件的使用–源码

/pages/index/index.wxml


<!-- 自定义导航栏 -->
<!-- 自定义导航组件引入 -->
<!-- 使用ext-class重写样式 -->
<navigation-bar 
    ext-class="page-navigator-bar" 
    active="{{active}}" 
    loading="{{loading}}">


    <view class="left" slot="left">
        <!-- 
            返回按钮和主页按钮是在外面定义的,
            是通过ext-class这个扩展样式属性来实现的
            ext-class="page-navigator-bar"
            设置这个属性是为了可以在外部控制内部组件的样式
            只要自定义组件使用插槽,基本上这个机制就是需要的
            weui组件库所有的组件都实现了这种机制
        -->
        <icon bindtap="goBack" class="iconfont icon-back"></icon>
        <icon bindtap="goHome" class="iconfont icon-home"></icon>
    </view>

    <view slot="center">
        <view>自定义导航标题</view>
        <!-- 
            按钮、广告、搜索框等等 都可以
         -->
    </view>

</navigation-bar>


<view style="width:100%;height:400px;">
</view>

<!-- 
  .top-banner {
    width: 100%;
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    z-index: -1;
    }  
    image图片是绝对定位,top:0,left:0,z-index:-1
    它就是处于最底层,作为一个背景然后去存在的
 -->
<image class="top-banner" src="https://p.qqan.com/up/2021-6/16232893724729414.jpg" mode="widthFix" />

<!-- 自定义导航组件操作 -->
<!-- 在这里通过绑定,嵌入了一个wxss变量 -->
<view class="operate-wraper" style="background-color:#f2f2f2;--topBarHeight:{{topBarHeight}}px;">

    <button bindtap="changeLoading">显示/隐藏 loading</button>

    <!-- <button bindtap="changeBgTransparent">设置背景透明</button>
  <button bindtap="changeBGColor">改变导航背景色</button>

  <button bindtap="changeColor">改变标题文字</button>

  <button bindtap="changeHomeImage">改变home图标</button>

  <button bindtap="changeShow">显示/隐藏导航</button>


  <button bindtap="changeBorder">显示/隐藏导航边框</button> 

  <button bindtap="showLeftSlot">显示/隐藏左侧slot</button> 

  <button bindtap="showCenterSlot">显示/隐藏中间slot</button>  -->


</view>
<view wx:for="[1,2,3]" style="width:100%;height:400px;background-color:#f2f2f2;text-align:center;">
    123
</view>
<!-- 
把内容放在每个页面里面,方便我们对按钮进行精细的控制

 -->

/pages/index/index.js

// 自定义导航栏

// 该示例来源于https://developers.weixin.qq.com/community/develop/article/doc/00048e5ed784b037b959757385b413
// 有少量修改

Page({
  data: {
    loading: false,
    active: true
  },
  //点击back事件处理
  goBack: function () {
    wx.navigateBack();
    this.triggerEvent('back');
  },
  //返回首页
  goHome: function () {
    wx.reLaunch({
      url: '/pages/index/index'
    })
  },
  // 滚动到一定程度,状态自动切换
  // 
  onPageScroll(res) {
    console.log(res);

    /* 
    为什么是400为临界值
    页面上方有一块透明显示背景的区域,这块区域的高度是400
    */ 

    /* 
    active是自定义组件navigation-var的属性,在wxml代码里面通过这个属性
    控制组件在navigator-active与navigator-normal这两个类样式之间切换
    通过这种方式实现了导航栏UI效果的切换
    */ 

    if (res.scrollTop > 400) {
      if (!this.data.active) {
        this.setData({
          active: true
        })
      }
    } else {
      if (this.data.active) {
        this.setData({
          active: false
        })
      }
    }
  }

})

/pages/index/index.json

{
  "usingComponents": {
    "navigation-bar":"/components/navigation-bar/index",
  },
  "navigationStyle":"custom"
}

/pages/index/index.wxss

/* 自定义导航栏 */

page{
  background-color: aliceblue;
}
.top-banner {
  width: 100%;
  display: block;
  position: fixed;
  top: 0;
  left: 0;
  z-index: -1;
}

.nice-focus {
  position: relative;
}

.nice-focus::after {
  content: '';
  width: 70rpx;
  height: 70rpx;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -35rpx;
  margin-left: -35rpx;
}
.nice-focus image{
  width: 56rpx;
  height:56rpx;
}

@font-face {
  font-family: 'iconfont';  /* project id 1716930 */
  src: url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.eot');
  src: url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.eot?#iefix') format('embedded-opentype'),
  url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.woff2') format('woff2'),
  url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.woff') format('woff'),
  url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.ttf') format('truetype'),
  url('//at.alicdn.com/t/font_1716930_0fs7vun2ngmn.svg#iconfont') format('svg');
}
.iconfont{
  font-family: 'iconfont';
}
.icon-back::after{
  content: '\e604';
  font-size: 22px;
}
.icon-home::after{
  content: '\e6f3';
  font-size: 22px;
}
.left icon:last-child{
  padding-left: 20rpx;
}

.page-navigator-bar .navigator-normal .icon-back{
  color: white;
}
.page-navigator-bar .navigator-normal .icon-home{
  color: white;
}
.page-navigator-bar .navigator-active .icon-back{
  color: black;
}
.page-navigator-bar .navigator-active .icon-home{
  color: black;
}



.square{
  background-color: green;
  padding:4rpx 20rpx;
  color: #fff;
  border-radius:10rpx;
}

/* 
这个变量--topBarHeight
是在js中动态设置中,是为了设置向上的偏移量
目前在示例中是没有作用的,可以删除
 */
.operate-wraper {
  height:calc(100vh - var(--topBarHeight));
  display: flex;
  align-content: center;
  align-items: center;
  flex-wrap: wrap;
  text-align: center;
}
.operate-wraper button, .operate-wraper navigator{
  background-color: green;
  color: #fff;
  flex:none;
  width:60%;
  margin:18rpx auto;
  height:80rpx;
  line-height:80rpx;
  border-radius:20rpx;
}
.list-item{
  border:2rpx solid #ccc;
  line-height:180rpx;
  text-align: center;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值