微信小程序自定义导航栏组件,完美适配所有手机,可实现各种功能和情况
背景
在做小程序时,关于默认导航栏,我们遇到了以下的问题:
- 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 按钮中的竖线颜色重了,并且感觉太粗
如果您看到了此篇文章,请赶快修改自己的代码,并运用在实践中吧
扫码体验我的小程序:
测试信息
手机型号 | 胶囊位置信息 | 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 | 通过 |
微信小程序自定义导航栏组件,完美适配所有手机,可实现各种功能和情况的更多相关文章
- 微信小程序自定义导航栏组件
1.首先,要在json文件中设置为自定义的形式 "navigationStyle": "custom" 2.计算相关值 导航栏分为状态栏和标题栏,只要能算出每台 ...
- 微信小程序——自定义导航栏
微信头部导航栏可能通过json配置: 但是有时候我们项目需求可能需要自定义头部导航栏,如下图所示: 现在具体说一下实现步骤及方法: 步骤: 1.在 app.json 里面把 "navigat ...
- 微信小程序自定义导航栏
微信小程序需要自定义导航栏,特别是左上角的自定义设置,可以设置返回按钮,菜单按钮,配置如下: 1.在app.json的window属性中增加: navigationStyle:custom 顶部导航栏 ...
- 微信小程序 - 自定义导航栏(提示)
点击下载: 自定义导航栏示例
- 微信小程序 自定义导航组件 nav头部 全面屏设计
nav-dynamic 微信小程序自定义nav头部组件:适配全面屏设计: 实现功能 初始进入页面时,展示初始状态下的nav样式: 页面滚动时,监听页面滚动事件,展示滚动状态下的nav样式: 根据配置字 ...
- 获取不同机型微信小程序状态栏+导航栏高度
获取不同机型微信小程序状态栏+导航栏高度 一. 前言 很多时候我们开发微信小程序,都需要先知道状态栏和导航栏的高度,才能去做其他功能 二. 获取微信小程序状态栏高度 用wx.getSystemInfo ...
- Taro 小程序 自定义导航栏
在小程序中,有的页面需求可能需要我们做一个自定义的导航栏, 今天就来踩一踩坑 首先需要在app.js 中给全局的导航栏隐藏, // app.js window: { navigationStyle: ...
- 微信小程序底部导航栏部署
在微信小程序开发app.json(app.json它是定义全局页面) 只是用来部署微信底部的图标,最多不能大于五个 "tabBar":{ "selectedColor&q ...
- 微信小程序底部导航栏(tabbar)
在app.json处设置“tabBar”,例子如下: { "pages": [ "pages/index/index", "pages/pages1/ ...
随机推荐
- 初撩Django-RESTful-rest_framework序列化(将模型序列化为JSON)
官方网站: https://www.django-rest-framework.org/ 翻译网站:https://q1mi.github.io/Django-REST-framework-docum ...
- oracle hint 强制索引(转)
oracle 1.建议建立一个以paytime,id,cost的复合索引.光是在paytime上建立索引会产生很多随机读.2.就算建立了索引,如果你查询的数据量很大的话,也不一定会用索引,有时候全表扫 ...
- Galaxy
Galaxy 在一维坐标轴上给出n个点,第i个点坐标为\(x_i\),现在你可以任意移动k个点的,最小化它们的方差,\(n\leq 50000\). 解 感觉以前写的太乱了,补一篇可以供快速阅读的题解 ...
- 【错误总结】Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
大致意思是因为模板里面应该包含一个根元素,使用组件的时候应该用div或p标签包起来
- Cross platform
值得学习的C/C++语言开源项目 (1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html ...
- Android中对消息机制(Handler)的再次解读
今天遇到一些关于在子线程中操作Handler的问题,感觉又要研究源代码了,但是关于Handler的话,我之前研究过,可以参考这篇文章:http://blog.csdn.net/jiangwei0910 ...
- python bs4解析网页时 bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to inst(转)
Python小白,学习时候用到bs4解析网站,报错 bs4.FeatureNotFound: Couldn't find a tree builder with the features you re ...
- python实现线程池(2.4)
线程池 什么是线程池? 诸如web服务器.数据库服务器.文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务. 构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就 ...
- fiddler抓包工具详解
转自:http://www.cnblogs.com/yyhh/p/5140852.html Fiddler 抓包工具总结 阅读目录 1. Fiddler 抓包简介 1). 字段说明 2). Sta ...
- STM32库中自定义的数据类型
在头文件 <stdint.h> 中 1 /* exact-width signed integer types */ typedef signed char int8_t; typedef ...