草庐IT

微信小程序自定义navigationBar、自定义标题栏

weixin_46265708 2023-04-14 原文

最近在做小程序项目,需要用到自定义navigationBar,之前用过colorUI等组件库的navigationBar组件,但是现在想自己写一个,所以有了今天的小记

在做navigationBar之前我们先了解一下navigationBar是什么 ?

微信小程序一般来说有两个bar,一个导航栏,一个tabbar(小程序下方一排切换按钮),实现下方自定义tabbar的方法一般来说较为简单,现在着重叙述上方自定义导航栏的实现。

为什么要自定义navigationBar?

原生导航栏的限制

  • 除了胶囊按钮以外,原生导航栏只会出现返回按钮和当用户打开的小程序最底层页面是非首页时,默认展示的“返回首页”按钮 。
  • 原生导航栏的标题文字的颜色只有黑白。
  • 布局无法改变,不能做定制。

产品需求

  • 如果说原生导航栏的限制还不足以让你加入自定义导航栏,那么产品需求绝对是更大的推动力。

  • 自定义导航栏除了不能自定义胶囊按钮以外,其他的范围都是程序员的掌控范围,所以使用自定义导航栏无疑可以满足产品的各种需求。

 

 实际的开发过程中我们会用到以下方法:

  • wx.getSystemInfo()  用于获取系统信息(如设备品牌、设备型号、屏幕大小等,具体参数在文章末尾)
  • wx.getMenuButtonBoundingClientRect()  用于获取小程序的胶囊信息 

 了解了自定义NAVIGATIONBAR之后我们需要知道怎么做?

 

  1. 去掉原生导航栏。

  1. 将需要自定义navigationBar页面的page.json的navigationBarTitleText去掉。
  2. 加上"navigationStyle":"custom",这样原生的导航栏就已经消失,甚至后退键也不会出现,需要自定义。
  3. 另外,早在2016年微信已经开始适配沉浸式状态栏,目前几乎所有的机型里微信都是沉浸式状态栏,也就是说去掉原生导航栏的同时,整个屏幕已经成为可编程区域

 

2.计算navigationBar高度。

  • 原生的胶囊按钮当然存在,那么下一步就需要你去定位出自定义的导航栏高度以及位置。
  • 对于不同的机型,对于不同的系统,状态栏以及胶囊按钮的位置都不确定,所以需要用到一定的计算,从而面对任何机型都可以从容判定。
  1. 使用"wx.getSystemInfo()“获取到"statusBarHeight”,这样就确定了导航栏最基本的距离屏幕上方的距离。
  2. 使用"wx.getMenuButtonBoundingClientRect()"获取到小程序的胶囊信息(注意这个api存在各种问题,在不同端表现不一致,后面会叙述这个api调用失败的处理情况),如下图,以下坐标信息以屏幕左上角为原点。

 

3.以下图为例,上面的红色框是statusBar,高度已知;下面的红色框是正文内容,夹在中间的就是求解之一navigationBarHeight;而黄色的是原生胶囊按钮也是在垂直居中位置,高度为胶囊按钮基于左上角的坐标信息已知,不难得出,navigationBarHeight = 蓝色框高度 × 2 + 胶囊按钮.height。(蓝色框高度 = 胶囊按钮.top - statusBarHeight)

 

最后的计算公式为:navigationBarHeight = (胶囊按钮.top - statusBarHeight) × 2 + 胶囊按钮.height。navigationBar 距屏幕上方的距离即为navigationBarHeight。

这种计算方法在各种机型以及安卓ios都适用。

针对"wx.getMenuButtonBoundingClientRect()"获取错误或者获取数据为0的极少数情况,只能够去模拟,对于android,一般navigationBarHeight为48px,而对于ios一般为40px,所有机型的胶囊按钮高度是32px笔者也是通过网上很多的文章和自测得出的,这种误差一般可以忽略。当然最理想的就是微信可以hold住所有机型,呵呵。最后提醒一下仅以真机为准,开发者工具的bug就更多不说了。

 

代码实现 

  •  获取本机信息,笔者一般写在App的onLaunch中。

 app.js

//计算导航栏高度
    const { statusBarHeight, platform } = wx.getSystemInfoSync()
    const { top, height } = wx.getMenuButtonBoundingClientRect()

    // 状态栏高度
    wx.setStorageSync('statusBarHeight', statusBarHeight)
    // 胶囊按钮高度 一般是32 如果获取不到就使用32
    wx.setStorageSync('menuButtonHeight', height ? height : 32)
    
    // 判断胶囊按钮信息是否成功获取
    if (top && top !== 0 && height && height !== 0) {
//获取成功进行计算
        const navigationBarHeight = (top - statusBarHeight) * 2 + height
		// 导航栏高度
        wx.setStorageSync('navigationBarHeight', navigationBarHeight)
    } else {
//获取失败使用默认的高度
        wx.setStorageSync(
          'navigationBarHeight',
          platform === 'android' ? 48 : 40
        )
    }
  • 笔者将这几个高度信息储存在stroage中(也可以存到全局变量中),之后创建navigationBar自定义组件,在组件中将会运用到这些数据。

navigationBar.js

    // 状态栏高度
    statusBarHeight: wx.getStorageSync('statusBarHeight') + 'px',
    // 导航栏高度
    navigationBarHeight: wx.getStorageSync('navigationBarHeight') + 'px',
    // 胶囊按钮高度
    menuButtonHeight: wx.getStorageSync('menuButtonHeight') + 'px',
    // 导航栏和状态栏高度
    navigationBarAndStatusBarHeight: wx.getStorageSync('statusBarHeight') +
      wx.getStorageSync('navigationBarHeight') +
      'px',
    //标题
    title: '积分游戏'

 navigationBar.wxml

<view class="navigation-container" style="{{'height: ' + navigationBarAndStatusBarHeight}}">
    <!--空白来占位状态栏-->
    <view style="{{'height: ' + statusBarHeight}}"></view>
	<!--自定义导航栏-->
    <view class="navigation-bar" style="{{'height:' + navigationBarHeight}}">
        <!-- 这里就是导航栏 你可以将你想要的样式放到这里-->
    	<view class="navigation-buttons" style="{{'height:' + menuButtonHeight}}">
            <image class="nav-img" src='/images/back.svg'/>
            ...其余自定义button
        </view>

        <view class="navigation-title" style="{{'line-height:' + navigationBarHeight}}">{{title}}</view>
    </view>    
</view>
<!--空白占位fixed空出的位置-->
<view style="{{'height: ' + navigationBarAndStatusBarHeight}}; background: #ffffff"></view>

 navigationBar.wxss

/* navigationBar.wxss */
.navigation-container {
  position: fixed;
  width: 100%;
  z-index: 99;
  top: 0;
  left: 0;
  background-color: #ffffff;
}
.navigation-bar {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
}
.navigation-buttons {
  display: flex;
  align-items: center;
  margin-left: 20rpx;
  border: 1px solid rgba(0, 0, 0, 0.05);
  box-sizing: border-box;
  border-radius: 30rpx;
  background-color: transparent;
}
.nav-img{
  height: 32rpx;
  width: 32rpx;
}
.navigation-title {
  position: absolute;
  left: 208rpx;
  right: 208rpx;
  text-align: center;
  font-size: 32rpx;
  font-weight: bold;
  color: #000000;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

 

总结

  • 自定义导航栏核心在于导航栏的高度定位,这样才能准确定位自定义的返回按钮以及其他按钮的位置,与原生胶囊保持一致。至于wxml的布局方法多种多样,上面只是列出了笔者的一种写法。
  • 学习小程序,自定义导航栏是很重要的技能,其间的逻辑并不复杂,还是和学习前端一样,需要非常细心耐心,才能做好细节工作

 

wx.getSystemInfo()获取到的参数

 

 

有关微信小程序自定义navigationBar、自定义标题栏的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>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

  3. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  5. ruby-on-rails - 使用 Rmagick 或 ImageMagick 在背景上放置标题 - 2

    我有一张背景图片,我想在其中添加一个文本框。我想弄清楚如何将标题放置在其顶部的正确位置。(我使用标题是因为我需要自动换行功能)。现在,我只能让文本显示在左上角,但我需要能够手动定位它的开始位置。require'RMagick'require'Pry'includeMagicktext="Loremipsumdolorsitamet"img=ImageList.new('template001.jpg')img 最佳答案 这是使用convert的ImageMagick命令行的答案。如果你想在Rmagick中使用这个方法,你必须自己移植

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用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"=>

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值: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

  10. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

随机推荐