相信很多小伙伴在使用uniapp进行多端开发的时候呢,在面对一些奇葩的业务需求的时候,uniapp给我们提供的默认导航栏已经不能满足我们的业务需求了,这个时候就需要我们自己自定义导航栏使用啦。
当然uniapp也给我们提供了很多的自定义导航栏的插件供大家使用,今天也给大家分享一个我自己写的导航栏啦,希望大家多多指点
首先我们在自定义导航栏的时候,我们需要知道头部的导航栏有哪几部分组成,那么我们以微信小程序为例

可以看到在微信小程序中,我们的头部导航栏其实受到右上角胶囊的限制比较大,这个时候我们自定义的导航栏,需要做到标题于胶囊水平对齐,那其实这个时候整个头部其实主要又:状态栏的高度+标题栏的高度组成。
状态栏的高度我们可以通过uni.getSystemInfoSync().statusBarHeight来获取。
那么标题栏的高度我们怎么获取呢?
其实要想定义标题栏的高度,我们需要知道这个胶囊的位置,在小程序中我们可以使用wx.getMenuButtonBoundingClientRect()获取关于胶囊的信息

获取到的胶囊的top,left,right分别对应胶囊的上边界,左边界,右边界相对于屏幕左上角的起点的位置,所以我们是不是可以用(胶囊上边界距离顶部的距离 - 状态栏的高度)*2+胶囊的高度,就是标题栏的高度呢?然后再在标题栏里面添加一个文本区让他的高度等于胶囊的高度,实现flex布局的上下居中是不是就搞定了呢?
以上呢其实针对小程序平台的导航栏讲解,那么既然使用uniapp,就会考虑到多端情况
那么其实我们使用uniapp获取到的状态栏在h5,小程序和app原生都是有效的,h5网页中一般我们都是直接采用浏览器内核给我们内置的网页导航栏,就是一个头部,没有过多的要求,而且浏览器不同,给我们提供的头部导航也不一样。
而对于app端,我们没有了像微信小程序中那种让人心烦的胶囊之后,我们只需要知道状态栏的高度,然后加上自己业务需求的标题栏样式和标题栏高度就行啦
所以我们在进行自定义导航栏封装的时候就要对代码进行条件编译啦。那么我这里呢是把微信小程序做了单独的处理,微信小程序之外的平台看作是统一状态
献上源码:
首先我们把获取设备信息的代码封装到一个统一的js文件里面,这样方便我们在组件中获取也方便我们在页面中获取。

/**
* 此js文件管理关于当前设备的机型系统信息
*/
const systemInfo = function() {
/****************** 所有平台共有的系统信息 ********************/
// 设备系统信息
let systemInfomations = uni.getSystemInfoSync()
// 机型适配比例系数
let scaleFactor = 750 / systemInfomations.windowWidth
// 当前机型-屏幕高度
let windowHeight = systemInfomations.windowHeight * scaleFactor //rpx
// 当前机型-屏幕宽度
let windowWidth = systemInfomations.windowWidth * scaleFactor //rpx
// 状态栏高度
let statusBarHeight = (systemInfomations.statusBarHeight) * scaleFactor //rpx
// 导航栏高度 注意:此导航栏高度只针对微信小程序有效 其他平台如自定义导航栏请使用:状态栏高度+自定义文本高度
let navHeight = 0 //rpx
// console.log(windowHeight,'哈哈哈哈哈');
/****************** 微信小程序头部胶囊信息 ********************/
// #ifdef MP-WEIXIN
const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
// 胶囊高度
let menuButtonHeight = menuButtonInfo.height * scaleFactor //rpx
// 胶囊宽度
let menuButtonWidth = menuButtonInfo.width * scaleFactor //rpx
// 胶囊上边界的坐标
let menuButtonTop = menuButtonInfo.top * scaleFactor //rpx
// 胶囊右边界的坐标
let menuButtonRight = menuButtonInfo.right * scaleFactor //rpx
// 胶囊下边界的坐标
let menuButtonBottom = menuButtonInfo.bottom * scaleFactor //rpx
// 胶囊左边界的坐标
let menuButtonLeft = menuButtonInfo.left * scaleFactor //rpx
// 微信小程序中导航栏高度 = 胶囊高度 + (顶部距离 - 状态栏高度) * 2
navHeight = menuButtonHeight + (menuButtonTop - statusBarHeight) * 2
// #endif
// #ifdef MP-WEIXIN
return {
scaleFactor,
windowHeight,
windowWidth,
statusBarHeight,
menuButtonHeight,
menuButtonWidth,
menuButtonTop,
menuButtonRight,
menuButtonBottom,
menuButtonLeft,
navHeight
}
// #endif
// #ifndef MP-WEIXIN
return {
scaleFactor,
windowHeight,
windowWidth,
statusBarHeight
}
// #endif
}
export {
systemInfo
}
然后我们定义一个导航栏组件

/*
* 注意:
* 1、在传入宽度或者高度时,如果是Number数据,传入的值为px大小,无需带单位,组件自动计算
* 2、在使用此导航栏时,建议传入UI规定的导航栏高度,此高度只针对除微信小程序的其他平台有效,微信小程序的导航栏高度,组件自计算
*/
<template>
<view>
<!-- 微信小程序头部导航栏 -->
<!-- #ifdef MP-WEIXIN -->
<view class="wx-head-mod" :style="{height:navHeight+'rpx',backgroundColor:navBackgroundColor}">
<view class="wx-head-mod-nav" :style="{height:navigationBarHeight+'rpx',top:statusBarHeight+'rpx'}">
<view class="wx-head-mod-nav-content"
:style="{height:customHeight+'rpx',justifyContent:textAlign === 'center'?'center':'left'}">
<!-- 文本区 -->
<view class="wx-head-mod-nav-content-mian"
:style="{width:navTextWidth,lineHeight:customHeight + 'rpx',paddingLeft:textPaddingLeft*scaleFactor+'rpx',fontSize:fontSize*scaleFactor+'rpx',fontWeight:fontWeight,color:titleColor}">
{{textContent}}
</view>
<!-- 返回按钮 -->
<view class="wx-head-mod-nav-content-back" :style="{display:isBackShow?'flex':'none'}"
@click="backEvent">
<view class="wx-head-mod-nav-content-back-img"
:style="{width:backImageWidth*scaleFactor+'rpx',height:backImageHeight*scaleFactor+'rpx'}">
<image :src="backImageUrl" mode="" style="width: 100%;height: 100%;"></image>
</view>
</view>
</view>
</view>
</view>
<!-- #endif -->
<!-- 除微信小程序之外的其他设备 -->
<!-- #ifndef MP-WEIXIN -->
<view class="other-head-mod"
:style="{height:navHeightValue*scaleFactor+statusBarHeight+'rpx',backgroundColor:navBackgroundColor}">
<view class="other-head-mod-mian"
:style="{height:navHeightValue*scaleFactor+'rpx',justifyContent:textAlign === 'center'?'center':'left'}">
<!-- 返回按钮 -->
<view class="other-head-mod-mian-back" v-show="isBackShow" @click="backEvent">
<view class="other-head-mod-mian-back-img"
:style="{width:backImageWidth*scaleFactor+'rpx',height:backImageHeight*scaleFactor+'rpx'}">
<image :src="backImageUrl" mode="" style="width: 100%;height: 100%;"></image>
</view>
</view>
<!-- 标题 -->
<view class="other-head-mod-mian-title" :style="{width:windowWidth - 184+'rpx',lineHeight:navHeightValue*scaleFactor+'rpx',
paddingLeft:textPaddingLeft*scaleFactor+'rpx',fontSize:fontSize*scaleFactor+'rpx',
fontWeight:fontWeight,color:titleColor}">
{{textContent}}
</view>
</view>
</view>
<!-- #endif -->
</view>
</template>
<script>
const app = getApp()
import {systemInfo} from '@/common/system-info.js'
export default {
name: "HeadView",
props: {
// 文本区域位置 left:左 center:中
textAlign: {
type: String,
default: 'center'
},
// 文本区内容
textContent: {
type: String,
default: '哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈就啊哈哈好借好还'
},
// 文本区离左边的距离
textPaddingLeft: {
type: Number,
default: 16
},
// 是否需要返回按钮
isBackShow: {
type: Boolean,
default: true
},
// 文本区字体大小
fontSize: {
type: Number,
default: 20 //px
},
// 文本区字体粗细
fontWeight: {
type: Number,
default: 700
},
// 文本区返回按钮图片宽
backImageWidth: {
type: Number,
default: 12 //px
},
// 文本区返回按钮图片高
backImageHeight: {
type: Number,
default: 24 //px
},
// 返回按钮图标路径
backImageUrl: {
type: String,
default: '/static/backImage.svg'
},
// 导航栏整体背景颜色
navBackgroundColor: {
type: String,
default: '#2476F9'
},
// 标题字体颜色
titleColor: {
type: String,
default: '#ffffff',
},
/******** h5端,app端需要传入自定义导航栏高度 *******/
navHeightValue: {
type: Number,
default: 44 //px
}
},
computed: {
// 文本区宽度
navTextWidth() {
if (this.textAlign === 'center') {
return (this.windowWidth - (this.windowWidth - this.menubarLeft) * 2) + 'rpx'
} else {
return this.menubarLeft + 'rpx'
}
},
// 文本区paddingLeft
textPaddingleft() {
if (this.textAlign === 'center') {
return '0'
} else {
return this.textPaddingLeft + 'rpx'
}
}
},
data() {
return {
statusBarHeight: app.globalData.statusBarHeight, //状态栏高度
navHeight: app.globalData.navHeight, //头部导航栏总体高度
navigationBarHeight: app.globalData.navigationBarHeight, //导航栏高度
customHeight: app.globalData.customHeight, //胶囊高度
scaleFactor: app.globalData.scaleFactor, //比例系数
menubarLeft: app.globalData.menubarLeft, //胶囊定位的左边left
windowWidth: app.globalData.windowWidth * app.globalData.scaleFactor
};
},
methods: {
backEvent() {
uni.navigateBack({
delta: 1
})
}
},
created() {
/* 获取设备信息 */
const SystemInfomations = systemInfo()
/* 通用平台 */
this.statusBarHeight = SystemInfomations.statusBarHeight //状态栏高度
this.scaleFactor = SystemInfomations.scaleFactor //比例系数
this.windowWidth = SystemInfomations.windowWidth //当前设备的屏幕宽度
/* 微信小程序平台 */
// #ifdef MP-WEIXIN
this.navHeight = SystemInfomations.navHeight + SystemInfomations.statusBarHeight //头部导航栏总高度
this.navigationBarHeight = SystemInfomations.navHeight //头部导航栏高度
this.customHeight = SystemInfomations.menuButtonHeight //胶囊高度
this.menubarLeft = SystemInfomations.menuButtonLeft //胶囊左边界距离左上角的距离
// #endif
}
}
</script>
<style>
/* #ifdef MP-WEIXIN */
.wx-head-mod {
box-sizing: border-box;
width: 100%;
position: fixed;
top: 0;
left: 0;
}
.wx-head-mod-nav {
box-sizing: border-box;
width: 100%;
position: absolute;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.wx-head-mod-nav-content {
box-sizing: border-box;
width: 100%;
display: flex;
justify-content: left;
align-items: center;
position: relative;
}
/* 文本区 */
.wx-head-mod-nav-content-mian {
box-sizing: border-box;
height: 100%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
/* 返回按钮 */
.wx-head-mod-nav-content-back {
box-sizing: border-box;
width: 60rpx;
height: 100%;
/* background-color: aqua; */
position: absolute;
top: 0;
left: 32rpx;
display: flex;
align-items: center;
justify-content: left;
}
.wx-head-mod-nav-content-back-img {
box-sizing: border-box;
}
/* #endif */
/* #ifndef MP-WEIXIN */
.other-head-mod {
box-sizing: border-box;
width: 100%;
position: fixed;
top: 0;
left: 0;
}
.other-head-mod-mian {
box-sizing: border-box;
width: 100%;
display: flex;
align-items: center;
justify-content: left;
position: absolute;
left: 0;
bottom: 0;
}
/* 返回按钮 */
.other-head-mod-mian-back {
box-sizing: border-box;
height: 100%;
width: 60rpx;
position: absolute;
left: 32rpx;
top: 0;
display: flex;
align-items: center;
}
/* 标题 */
.other-head-mod-mian-title {
box-sizing: border-box;
height: 100%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
/* #endif */
</style>
组件使用:
引入组件:
import HeadNav from '@/components/HeadNav.vue'
组册组件:
components:{
HeadNav
},
<template>
<view class="content">
<head-nav></head-nav>
<view class="content-main"></view>
</view>
</template>
效果图:
微信小程序:

h5:

app:

在项目里面没有针对h5时候需要导航栏做特别的限制,如果实际开发中在h5端不需要此导航栏,请在模版上面针对h5页面加入条件编译即可。
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是
我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些