微信小程序自定义导航栏组件

相信很多小伙伴在开发微信小程序的时候都会有自定义顶部导航栏的需求,不够要说明的是小程序右上角的胶囊是不能自定义的哦,除了胶囊其他地方都是可以根据自己的项目而定了,在一次小程序开发中就需要对顶部进行自定义在此记录一下自己封装这个组件的过程。

组件编写

既然今天需要把导航栏封装为组件那么就需要以下几个步骤:

组件结构搭建

首先搭建一个如下图的结构:


1.png

然后在index中引入刚刚创建好的组件:


2.png

记得在json文件中添加引用路径如图一,此时就会看到首页效果如下:
3.png

组件基础代码编写

此时我们就需要根据官方文档查看得知如果要定制必须在配置文件中修改默认的配置文件,其实这里可以针对某个页面page进行设置,以index为例在index.json文件中配置如下代码即可看到如下效果:


4.png

这个时候我们就可以开始写自己想要的样子了,首先写一个简单的就只有一个返回按钮,当开始写的时候就会发现一个很大的问题,这块高度多少?最上面的状态栏高度又是多少?貌似被这两个问题难住了,这个时候再去看看官方的api文档你会发现可以获取系统当前更多参数里面就有状态栏高度,如图:


5.png

其实想想我们只知道状态栏高度,除了状态栏的高度其余高度呢?又是一个问题。从微信小程序的官方设计文档中可以知道这部分高度貌似一样高,如图:
6.png

貌似这块多高都可以,毕竟还可以向下延伸,经过多次测试(可能不准确)在苹果好安卓还是会有差别的,所以自己弄了一个适配一部分的(貌似旧手机不支持沉沉侵式)。可能废话太多,直接上代码吧

结构
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view>
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>标题</view>
    </view>
  </view>
</view>

这里的主要思路就是用fixed定位,后面会有内容顶到底部的布局,所以全部用定位会方便些,整体就两个部分一个是状态栏一个就是标题和按钮的部分了。

样式
.topbar {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  z-index: 9999;
}
.status {
  width: 100%;
}
.navbar {
  width: 100%;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  position: relative;
}

.navbar_back {
  padding: 0 32rpx;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  height: 100%;
}

.navbar_back image {
  width: 21rpx;
  height: 34rpx;
}

.navbar_title {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: -1;
}
.navbar_title view {
  width: 40%;
  word-break: break-all;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 38rpx;
}

样式中没有太多的东西,唯一需要说的就是大部分模块都是定位做的,唯一没考虑好的就是标题太长不好处理,这里用了简单粗暴的方法给设定一个宽度。

逻辑处理
Component({
  properties: {
    /**
     * 自定义返回事件处理
     * customBackReturn="{{true}}" bind:customBackReturn="customBackReturn"
     */
    customBackReturn: {
      type: Boolean,
      value: false
    }
  },
  data: {

  },
  methods: {
    backClick() {
      if (this.data.customBackReturn) {
        this.triggerEvent("customBackReturn")
      } else {
        if (getCurrentPages().length == 1) {
          wx.switchTab({
            url: '/pages/index/index',
          })
        } else {
          wx.navigateBack({
            delta: 1
          })
        }
      }
    }
  },
  attached() {
    var self = this;
    wx.getSystemInfo({
      success(res) {
        var isIos = res.system.indexOf('iOS') > -1;
        self.setData({
          statusHeight: res.statusBarHeight,
          navHeight: isIos ? 44 : 48
        })
      }
    })
  }
})

逻辑中主要在两个地方一个是获取系统信息时对于导航高度的判断,另外一个是返回上一页的逻辑,默认就是打开首页(注意用的api),如果有自定义事件就用自定义事件customBackReturn

效果

目前效果就和没有自定义之前差不多就是一个普通的返回按钮和标题文字,如下图:


7.png

那么这样一个简单的自定义导航栏就ok了,但是如果是这样那我还自定义干嘛用小程序自带的不是更好,所以我们要继续改进,给加一个返回主页的按钮。

带返回按钮的导航

小程序中特别是分享出去的页面,如果没有一个返回主页的按钮,用户更本不知道怎么回到小程序的主页,所以给用户带来了很大的不便,因此这样的需求就必须要有了,如果实现呢?其实很简单,毕竟已经出来一个了,需要的就是加一个上去就行了。

结构
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_home'>
      <image src='../images/black_back.png' bindtap='backClick'></image>
      <image src='../images/home_black.png' bindtap='homeClick'></image>
    </view>
    <!-- <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view> -->
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>标题</view>
    </view>
  </view>
</view>

大家注意到我把之前的代码注释了而不是直接在前面的代码上修改,其实这里留着是为了后面可以自定义想要那种导航形式。

样式
.navbar_home {
  margin-left: 32rpx;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  border-radius: 33rpx;
  border: 1px solid rgba(0, 0, 0, 0.1);
  background: rgba(0,0,0,0.2);
  box-sizing: border-box;
  padding: 10rpx 0;
}

.navbar_home image:first-child {
  width: 21rpx;
  height: 34rpx;
  padding: 0 32rpx;
  border-right: 1px solid rgba(255,255,255,0.2);
}

.navbar_home image:last-child {
  width: 37rpx;
  height: 35rpx;
  padding: 0 32rpx;
}

同样的样式也只是在之前的基础上增加了上面这些样式。

逻辑
 homeClick() {
      wx.switchTab({
        url: '/pages/index/index',
      })
    }

逻辑也只是在methods中添加一个返回主页的事件而已,这里需要大家根据自己的项目情况而定。

效果
8.png

样式大家可以根据自己项目而定。

到这两部分的功能都完成了,但是如果现在我们在首页中添加一些内容你就会发现问题了,如下:


9.png

我们的内容也跑到上面了,不过这么一想内容顶到顶部不就是这种效果吗,所以解决了这个问题,可现在的问题是不想要这种怎么办呢?想想这个应该很好办啊,我在这一部分给加一个等高的块不定位不就可以了,所以现在就来实现它,代码如下:

<view style="height:{{statusHeight+navHeight}}px" hidden='{{false}}'></view>
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_home'>
      <image src='../images/black_back.png' bindtap='backClick'></image>
      <image src='../images/home_black.png' bindtap='homeClick'></image>
    </view>
    <!-- <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view> -->
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>标题</view>
    </view>
  </view>
</view>

效果如下:


10.png

当我们把false改为true是就可以得到内容顶到顶部的效果了,所以这样就实现了两种效果,既然我们这里是封装为组件,那么我们不可能就这样写的,需要进行一些封装,然后给其他页面配置然后使用,所以需要改造。

封装导航栏使其可定制化

为了能使使用更加方便,需要对上面的导航栏进行进一步封装以适应不同需求。

结构改造

首先需要改造的就是结构,让更多的选项能够进行配置,结构改造如下:

<view style="height:{{statusHeight+navHeight}}px" hidden='{{header.hiddenBlock}}'></view>
<view class='topbar' style="background:{{header.headerbg}}">
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <block wx:if="{{header.slot}}">
      <slot></slot>
    </block>
    <block wx:else>
      <view class='navbar_home' wx:if="{{header.homeCapsule}}" style="background:{{header.capsulebg}};border:{{header.capsuleborder}}">
        <image src='../images/black_back.png' bindtap='backClick' style="border-right:{{header.capsulesep}}"></image>
        <image src='../images/home_black.png' bindtap='homeClick'></image>
      </view>
      <view class='navbar_back' bindtap='backClick' wx:else>
        <image src='../images/black_back.png'></image>
      </view>
      <view class='navbar_title' style="height:{{navHeight}}px">
        <view style="color:{{header.fontColor}};font-size:{{header.fontSize}}">{{header.title}}</view>
      </view>
    </block>
  </view>
</view>

上面所有的参数都是可以配置的,这就大大的给了开发者定制不同风格了,如果还是有不符合的建议你直接修改上面的代码或者自己封装一个更好。

逻辑改造
Component({
  properties: {
    header: {
      type: Object,
      value: {
        homeCapsule: false,
        headerbg: "#fff",
        title: "",
        fontColor: "#000",
        fontSize: '16',
        hiddenBlock: false,
        capsulebg: 'rgba(0,0,0,0.2)',
        capsuleborder: '1px solid rgba(0, 0, 0, 0.1)',
        capsulesep: '1px solid rgba(255,255,255,0.2)',
        slot: false
      }
    },
    /**
     * 自定义返回事件处理
     * customBackReturn="{{true}}" bind:customBackReturn="customBackReturn"
     */
    customBackReturn: {
      type: Boolean,
      value: false
    }
  },
  methods: {
    backClick() {
      if (this.data.customBackReturn) {
        this.triggerEvent("customBackReturn")
      } else {
        if (getCurrentPages().length == 1) {
          wx.switchTab({
            url: '/pages/index/index',
          })
        } else {
          wx.navigateBack({
            delta: 1
          })
        }
      }
    },
    homeClick() {
      wx.switchTab({
        url: '/pages/index/index',
      })
    }
  },
  attached() {
    var self = this;
    wx.getSystemInfo({
      success(res) {
        var isIos = res.system.indexOf('iOS') > -1;
        self.setData({
          statusHeight: res.statusBarHeight,
          navHeight: isIos ? 44 : 48
        })
      }
    })
  }
})

结构的改造其实就是添加一些能够传入的值来定制化。

到这里基本上这个组件就封装好了,接下来就是使用这个组件了。

常规带有一个返回按钮的案例

这种方式的应用很简单直接配置一些参数即可。

先引入组件如下:
{
  "navigationStyle":"custom",
  "usingComponents": {
    "header":"../../components/navbar/navbar"
  }
}

需要配置导航栏自定义,不然就没有效果,根据自己的路径引入即可,如果所有页面都需要用到,建议直接在app.json中配置,这样就不用每个页面去配置了。

wxml中使用组件标签
<header header='{{header}}'></header>
<view>内容区域哦</view>

在需要用到的页面使用header标签即可

js文件中配置参数
Page({
  data: {
    header:{
      homeCapsule: false,
      title: '标题',
      fontColor: "#000",
      fontSize: '36rpx',
      headerbg: '#f40',
      hiddenBlock: false,
      slot: false
    }
  },
  onLoad: function() {}
});

这里有三个参数是必须要配置的:homeCapsule,hiddenBlock,slot三个参数分别代表的含义是是否显示带有home按钮的,是否隐藏整个顶部(后面会介绍),是否需要自定义结构

效果
11.png

那么这个最基本的就实现了。

内容置顶的实例

有时候需要将内容置顶,看起来会比较好看,所以就需要对hiddenBlock进行配置了,看代码:

<header header='{{header}}'></header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>内容区域哦</view>

重要的是如何配置,如下:

Page({
  data: {
    header:{
      homeCapsule: false,
      title: '',
      headerbg: 'transparent',
      hiddenBlock: true,
      slot: false
    }
  },
  onLoad: function() {}
});

效果如下:


12.png

带有home按钮的导航

分享页需要带有返回首页的按钮,如何配置呢?有了这个组件就很容易配置了,配置如下:

Page({
  data: {
    header:{
      homeCapsule: true,
      title: '测试标题',
      headerbg: '#f40',
      hiddenBlock: false,
      slot: false
    }
  },
  onLoad: function() {}
});

效果如下:


13.png

完全自定义

如果以上都不能满足需求,那么只有自己写了,简单实例如下:

<header header='{{header}}'>
  <view>测试的标题哦</view>
</header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>内容区域哦</view>

配置如下:

Page({
  data: {
    header:{
      headerbg: 'transparent',
      hiddenBlock: false,
      slot: true
    }
  },
  onLoad: function() {}
});

效果如下:


14.png

当然啦,这里只是简单的举个栗子而已,具体靠大家自己去实现了。
到此这组件大部分的封装就完成了。谢谢大家哦!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容