评论

微信小程序自定义导航栏组件(完美适配所有手机),可自定义实现任何你想要的功能

小程序自定义导航栏,完美适配所有手机,测试涵盖中低高端,全面屏,刘海屏各种类型手机,完美解决内容上下不居中,左右不对称,高度不协调等问题,demo提供8种类型可选择,可以自定义实现任何你想要的功能

背景

在做小程序时,关于默认导航栏,我们遇到了以下的问题:

  • Android、IOS手机对于页面title的展示不一致,安卓title的显示不居中
  • 页面的title只支持纯文本级别的样式控制,不能够做更丰富的title效果
  • 左上角的事件无法监听、定制
  • 路由导航单一,只能够返回上一页,深层级页面的返回不够友好

探索

小程序自定义导航栏已开放许久>>了解一下,相信不少小伙伴已使用过这个功能,同时不少小伙伴也会发现一些坑:

  • 机型多如牛毛:自定义导航栏高度在不同机型始终无法达到视觉上的统一
  • 调皮的胶囊按钮:导航栏元素(文字,图标等)怎么也对不齐那该死的胶囊按钮
  • 各种尺寸的全面屏,奇怪的刘海屏,简直要抓狂

一探究竟

为了搞明白原理,我先去翻了官方文档,>>飞机,点过去是不是很惊喜,很意外,通篇大文尽然只有最下方的一张图片与这个问题有关,并且啥也看不清,汗汗汗…

我特意找了一张图片来

分析上图,我得到如下信息:

  • Android跟iOS有差异,表现在顶部到胶囊按钮之间的距离差了6pt
  • 胶囊按钮高度为32pt, iOS和Android一致

动手分析

我们写一个状态栏,通过wx.getSystemInfoSync().statusBarHeight设置高度

Android:

iOS:

可以看出,iOS胶囊按钮与状态栏之间距离为:4px, Android为8px,是不是所有手机都是这种情况呢?
答案是:苹果手机确实都是4px,安卓大部分都是7和8 也会有其他的情况(可以自己打印getSystemInfo验证)如何快速便捷算出这个高度,请接着往下看

如何计算

导航栏分为状态栏和标题栏,只要能算出每台手机的导航栏高度问题就迎刃而解

  • 导航栏高度 = 胶囊按钮高度 + 状态栏到胶囊按钮间距 * 2 + 状态栏高度

注:由于胶囊按钮是原生组件,为表现一致,其单位在各种手机中都为px,所以我们自定义导航栏的单位都必需是px(切记不能用rpx),才能完美适配。

解决问题

现在我们明白了原理,可以利用胶囊按钮的位置信息和statusBarHeight高度动态计算导航栏的高度,贴一个实现此功能最重要的方法

let systemInfo = wx.getSystemInfoSync();
let rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null; //胶囊按钮位置信息
    wx.getMenuButtonBoundingClientRect();
    let navBarHeight = (function() { //导航栏高度
            let gap = rect.top - systemInfo.statusBarHeight; //动态计算每台手机状态栏到胶囊按钮间距
            return 2 * gap + rect.height;
          })();

gap信息就是不同的手机其状态栏到胶囊按钮间距,具体更多代码实现和使用demo请移步下方代码仓库,代码中还会有输入框文字跳动解决办法,安卓手机输入框文字飞出解决办法,左侧按钮边框太粗解决办法等等

胶囊信息报错和获取不到

问题就在于 getMenuButtonBoundingClientRect 这个方法,在某些机子和环境下会报错或者获取不到,对于此种情况完美可以模拟一个胶囊位置出来

try {
  rect = Taro.getMenuButtonBoundingClientRect ? Taro.getMenuButtonBoundingClientRect() : null;
  if (rect === null) {
    throw 'getMenuButtonBoundingClientRect error';
  }
  //取值为0的情况
  if (!rect.width) {
    throw 'getMenuButtonBoundingClientRect error';
  }
} catch (error) {
  let gap = ''; //胶囊按钮上下间距 使导航内容居中
  let width = 96; //胶囊的宽度,android大部分96,ios为88
  if (systemInfo.platform === 'android') {
    gap = 8;
    width = 96;
  } else if (systemInfo.platform === 'devtools') {
    if (ios) {
      gap = 5.5; //开发工具中ios手机
    } else {
      gap = 7.5; //开发工具中android和其他手机
    }
  } else {
    gap = 4;
    width = 88;
  }
  if (!systemInfo.statusBarHeight) {
    //开启wifi的情况下修复statusBarHeight值获取不到
    systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
  }
  rect = {
    //获取不到胶囊信息就自定义重置一个
    bottom: systemInfo.statusBarHeight + gap + 32,
    height: 32,
    left: systemInfo.windowWidth - width - 10,
    right: systemInfo.windowWidth - 10,
    top: systemInfo.statusBarHeight + gap,
    width: width
  };
  console.log('error', error);
  console.log('rect', rect);
}

以上代码主要是借鉴了拼多多的默认值写法,android 机子中 gap 值大部分为 8,ios 都为 4,开发工具中 ios 为 5.5,android 为 7.5,这样处理之后自己模拟一个胶囊按钮的位置,这样在获取不到胶囊信息的情况下,可保证绝大多数机子完美显示导航头

吐槽

这么重要的问题,官方尽然没有提供解决方案…竟然提供了一张看不清的图片???

网上有很多ios设置44,android设置48,还有根据不同的手机型号设置不同高度,通过长时间的开发和尝试,本人发现以上方案并不完美,并且bug很多

代码库

  • Taro组件gitHub地址详细用法请参考README
  • 原生组件npm构建版本gitHub地址详细用法请参考README
  • 原生组件简易版gitHub地址详细用法请参考README
  • 由于本人精力有限,目前只计划发布维护好这2种组件,其他组件请自行修改代码,有问题请联系

备注

  • 上方2种组件在最下方30多款手机测试情况表现良好
  • iPhone手机打电话和开热点导致导航栏样式错乱,问题已经解决啦,请去demo里测试,这里特别感谢moments网友提出的问题
  • 本文章并无任何商业性质,如有侵权请联系本人修改或删除
  • 文章少量部分内容是本人查询搜集而来
  • 如有问题可以下方留言讨论,微信zhijunxh

比较

斗鱼:

虎牙:

微博:

酷狗:

知乎:

知乎是这里边做的最好的,但是我个人认为有几个可以优化的小问题

  • 打电话或者开启热点导致样式错落,这也是大部门小程序的问题
  • 导航栏下边距太小,看起来不舒服
  • 搜索框距离2侧按钮组距离不对等
  • 自定义返回和home按钮中的竖线颜色重了,并且感觉太粗

如果您看到了此篇文章,请赶快修改自己的代码,并运用在实践中吧

扫码体验我的小程序:

创作不易,如果对你有帮助,请移步Taro组件gitHub原生组件gitHub给个星星 star✨✨ 谢谢

测试信息

手机型号 胶囊位置信息 statusBarHeight 测试情况
iPhoneX 80 32 281 369 48 88 44 通过
iPhone8 plus 56 32 320 408 24 88 20 通过
iphone7 56 32 281 368 24 87 20 通过
iPhone6 plus 56 32 320 408 24 88 20 通过
iPhone6 56 32 281 368 24 87 20 通过
HUAWEI SLA-AL00 64 32 254 350 32 96 24 通过
HUAWEI VTR-AL00 64 32 254 350 32 96 24 通过
HUAWEI EVA-AL00 64 32 254 350 32 96 24 通过
HUAWEI EML-AL00 68 32 254 350 36 96 29 通过
HUAWEI VOG-AL00 65 32 254 350 33 96 25 通过
HUAWEI ATU-TL10 64 32 254 350 32 96 24 通过
HUAWEI SMARTISAN OS105 64 32 326 422 32 96 24 通过
XIAOMI MI6 59 28 265 352 31 87 23 通过
XIAOMI MI4LTE 60 32 254 350 28 96 20 通过
XIAOMI MIX3 74 32 287 383 42 96 35 通过
REDMI NOTE3 64 32 254 350 32 96 24 通过
REDMI NOTE4 64 32 254 350 32 96 24 通过
REDMI NOTE3 55 28 255 351 27 96 20 通过
REDMI 5plus 67 32 287 383 35 96 28 通过
MEIZU M571C 65 32 254 350 33 96 25 通过
MEIZU M6 NOTE 62 32 254 350 30 96 22 通过
MEIZU MX4 PRO 62 32 278 374 30 96 22 通过
OPPO A33 65 32 254 350 33 96 26 通过
OPPO R11 58 32 254 350 26 96 18 通过
VIVO Y55 64 32 254 350 32 96 24 通过
HONOR BLN-AL20 64 32 254 350 32 96 24 通过
HONOR NEM-AL10 59 28 265 352 31 87 24 通过
HONOR BND-AL10 64 32 254 350 32 96 24 通过
HONOR duk-al20 64 32 254 350 32 96 24 通过
SAMSUNG SM-G9550 64 32 305 401 32 96 24 通过
360 1801-A01 64 32 254 350 32 96 24 通过
最后一次编辑于  2019-11-17  
点赞 42
收藏
评论

39 个评论

  • 龙@CYBERSPACE
    龙@CYBERSPACE
    2019-11-08

    之前看过touchWX的UI封装  然后我把它抽出来沿用到现在,我感觉那个不错~没有用你这个方法也避免了上面说的那些微信的bug那种问题~

    2019-11-08
    赞同
    回复 1
    • 志军
      志军
      2019-11-09
      对于getMenuButtonBoundingClientRect 胶囊按钮信息获取不到或者此方法报错问题已修复,获取不到就设置默认值
      2019-11-09
      回复
  • Long
    Long
    2019-10-24

    👍

    2019-10-24
    赞同
    回复
  • 在劫难逃的天真
    在劫难逃的天真
    2019-10-24

    你好 请问下 我页面上的自定义导航栏用的fixed定位 同时页面上还有一个输入框 现在问题是 输入框一旦得焦 会上顶页面 这样的话  自定义的导航栏就被上顶到看不到的地方了 怎么解决呢

    2019-10-24
    赞同
    回复 7
    • 志军
      志军
      2019-10-24
      输入框文字上移 安卓机子文字弹出输入框 https://developers.weixin.qq.com/community/develop/article/doc/000e421782c1c0f57609caf865bc13
      2019-10-24
      回复
    • 在劫难逃的天真
      在劫难逃的天真
      2019-10-24回复志军
      呃 不是输入框文字上移  而是整个页面被上顶 导致自定义的导航栏随着页面上移 移出到一屏外了
      2019-10-24
      回复
    • 志军
      志军
      2019-10-24
      我的好像没碰到 你可以下载我的demo测一下
      2019-10-24
      回复
    • ye
      ye
      2019-11-18
      你好,请问导航栏上顶的问题解决了吗?
      2019-11-18
      回复
    • ye
      ye
      2019-11-18回复志军
      输入的时候,键盘会把整个页面往上顶,导航栏就被顶到屏幕外了,fixed不管用
      2019-11-18
      回复
    查看更多(2)
  • The-fighter-
    The-fighter-
    2019-10-11

    您好,看您用到了wx.getMenuButtonBoundingClientRect()这个API ,我在真机测试的时候,这个API返回的数值都是0,很多人都说是官方bug,那用你这个方法会不会也出现这个问题

    2019-10-11
    赞同
    回复 3
    • 志军
      志军
      2019-10-13
      一般都是微信版本问题
      2019-10-13
      回复
    • 志军
      志军
      2019-10-13
      你先升级版本试下,如果没问题看看这个版本用户有多少
      2019-10-13
      回复
    • 志军
      志军
      2019-11-09
      对于getMenuButtonBoundingClientRect 胶囊按钮信息获取不到或者此方法报错问题已修复,获取不到就设置默认值
      2019-11-09
      回复
  • ...
    ...
    2019-10-09

    👍

    2019-10-09
    赞同
    回复
  • 2019-09-29

    提个建议啊,最近使用过程中遇到了一个问题,业务中有一种很常见的需求 => 吸顶。 小程序里面一般用fixed 实现。这样的话 一般  top: 0 这样就会跟自定义头部重叠。建议组件留个 自定义头部高度的属性暴露出来,可能会更加好用。 以上。方案还是很棒的。

    2019-09-29
    赞同
    回复 2
  • 子
    2019-09-28

    为什么有的iphone6手机有的时候会获取不到导航栏的高度

    2019-09-28
    赞同
    回复 6
    • 志军
      志军
      2019-09-28
      微信版本太低或者taro版本更新到1.3之后
      2019-09-28
      回复
    • 子
      2019-09-28回复志军
      我再app.js中的onshow中又获取了一遍系统头部的高度,但是发现在分享消息中头部就没有了,这个问题也不好复现,我就想问一下在app.js中的onshow中再获取一遍有用吗?
      2019-09-28
      回复
    • 志军
      志军
      2019-09-29回复
      你自己调试一下,先找见具体出现问题的原因,再考虑解决方案
      2019-09-29
      回复
    • 子
      2019-09-29回复志军
      突然发现,在app.js中的onshow再获取一遍头部的高度,发现iphonex的手机在分享消息中的头部没有,但是别的手机都有,iphonx的手机点击进去分享消息里面onshow又执行了一遍,但是有的值获取到的是0
      2019-09-29
      回复
    • 志军
      志军
      2019-09-29
      微信zhijunxh私聊
      2019-09-29
      回复
    查看更多(1)
  • 2019-09-25


    你好 有个疑问想请教一下  第二处箭头的地方 将navBarExtendHeight归零了 ,然后else分支里面ios 情况下是为4的 是故意这样处理的的吗?

    2019-09-25
    赞同
    回复 4
    • 志军
      志军
      2019-09-26
      对的 因为Android空隙很大了所以将navBarExtendHeight归零,iso下方间隙太小,所以加4,一切都是为了导航条看起来更协调
      2019-09-26
      回复
    • 2019-09-26回复志军
      我是说 if 分支里关于navBarExtendHeight的处理 没有针对ios 和Android做特殊处理,觉着很奇怪(更else 分支中不一致)
      2019-09-26
      回复
    • 志军
      志军
      2019-09-26

      差点相信你了,都准备修改代码了.... 因为上方这个判断是为了兼容iphone开启wifi热点出现新的状态栏,从而导致位置错落而写的 这种情况下下方就不需要加4px像素了,你可以找台iPhone手机开热点试试

      2019-09-26
      回复
    • 2019-09-26回复志军
      方便的话加个微信吧,我加你了有空的时候麻烦同意下。我之所以有这个疑问因为没看懂 对应的是什么场景
      2019-09-26
      回复
  • 安
    2019-09-20

    为什么不直接用

    wx.getMenuButtonBoundingClientRect()

    的bottom,top,height来做?

    2019-09-20
    赞同
    回复 1
    • 志军
      志军
      2019-09-20
      用的就是这个原理啊,另外又兼容了一些其他的问题,使导航条看起来更漂亮
      2019-09-20
      回复
  • 说来有点可笑
    说来有点可笑
    2019-09-10

    文章很赞!!!

    2019-09-10
    赞同
    回复

正在加载...

登录 后发表内容