适配安全区域原因
从苹果iPhone X开始,刘海屏已经是一种常见的机型,物理Home键被取消,改为底部小黑条采取向上滑动手势替代home键功能。微信小程序和移动端网页需要针对这种情况进行适配,否则可能会遇到底部按钮或选项卡栏与底部黑线重叠所造成本身按钮和系统手势可能存在的冲突以及设计上的美观。
安全区域
安全区域为中间蓝色部分,在页面布局时应该保证页面内容在蓝色安全区域内
如何适配安全距离
iOS中增加了css函数来支持安全距离的边距
- safe-area-inset-left:安全区域距离左边边界距离
- safe-area-inset-right:安全区域距离右边边界距离
- safe-area-inset-top:安全区域距离顶部边界距离
- safe-area-inset-bottom:安全区域距离底部边界距离
padding-bottom: constant(safe-area-inset-bottom); /*兼容 IOS<11.2*/
padding-bottom: env(safe-area-inset-bottom); /*兼容 IOS>11.2*/
正常设置之后是这样
但是在安卓上,很多屏幕底部是刘海机型但是并没有兼容这个css API,就会导致如果只放一个按钮很有可能显示不完全,这是截图实际上在手机底部是刘海屏底部所见的区域更少
所以一般采用方式也是和UI沟通,不要单单就一个按钮在最底部,默认价格背景色和边距
看着就比较自然,但是有些机型是奇葩机型,毕竟安卓那么多机型,wx.getSystemInfo获取到的safeArea有可能为0甚至是负数(之前在小程序社区中看到过计算后负数),所以单使用safe-area-inset-bottom只能解决iOS机型问题。
安卓底部安全距离统一处理
方案一 page-meta(推荐)
通过计算获取底部距离
getDeviceHeight() {
// 各区域高度
const menuButtonObject = wx.getMenuButtonBoundingClientRect();
const windowInfo = wx.getWindowInfo();
wx._systemInfo = windowInfo;
const safeArea = windowInfo.safeArea;
// 状态栏高度
const statusBarHeight = windowInfo.statusBarHeight;
const bottomSafetyDistance = windowInfo.screenHeight - safeArea.bottom;
wx._systemInfo.bottomSafetyDistance = bottomSafetyDistance;
app.globalData.platform = windowInfo.platform;
app.globalData.windowInfo = windowInfo;
const deviceInfo = wx.getDeviceInfo();
const ios = !!(deviceInfo.system.toLowerCase().search("ios") + 1);
const ratio = ios ? 3 : 2;
const navigationHeight =
statusBarHeight +
menuButtonObject.height +
(menuButtonObject.top - statusBarHeight) * ratio; // 导航高度
app.globalData.navTop = menuButtonObject.top;
// 状态栏+导航高度
app.globalData.navigationHeight = navigationHeight;
// 底部安全区域
app.globalData.bottomSafeArea = bottomSafetyDistance;
// 可视区域高度 - 适配横竖屏场景
const screenHeight = Math.max(
windowInfo.screenHeight,
windowInfo.screenWidth
);
const height = Math.max(safeArea.height, safeArea.width);
// 获取底部安全区域高度(全面屏手机)
if (safeArea && height && screenHeight) {
windowInfo.bottomSafeHeight = screenHeight - height - statusBarHeight;
if (windowInfo.bottomSafeHeight < 0) {
windowInfo.bottomSafeHeight = 0;
}
}
// 实际能使用的内容高度
app.globalData.contentHeight =
windowInfo.screenHeight -
app.globalData.navigationHeight -
app.globalData.bottomSafeArea;
app.globalData.statusBarHeight = statusBarHeight;
app.globalData.windowHeight = windowInfo.windowHeight;
app.globalData.screenHeight = windowInfo.screenHeight;
console.log("getSystemInfo", windowInfo);
console.log("system", deviceInfo.system);
// 底部tabBar (如果首页没有自定义导航栏则需要再减去 navigationHeight) 如果首页没有底部tabbar则注释
let tabBar =
windowInfo.screenHeight -
windowInfo.windowHeight -
app.globalData.bottomSafeArea; // - navigationHeight
tabBar = tabBar > 0 ? tabBar : 0;
app.globalData.tabBar = tabBar;
console.log("app.globalData", app.globalData);
},
onLoad() {
this.getDeviceHeight();
this.setData({
safeBottomStyle: `--safe-area-inset-bottom:${wx._systemInfo.bottomSafetyDistance}px`,
});
},
在wxml中添加page-meta组件设置页面根节点样式
<page-meta page-style="{{safeBottomStyle}}">
</page-meta>
相当于设置动态css变量
然后就可以在wxss中使用
padding-bottom: var(--safe-area-inset-bottom);
方案二 私有API (不推荐)
直接使用wx.setPageStyle,但是由于微信文档没有公开api,如果一旦更改迁移成本过高
wx.setPageStyle({
style: {
"--safe-area-inset-bottom":`${wx._systemInfo.bottomSafetyDistance}px`,
}
})
总结
采用动态css变量设置后主要是可以方便特殊的安卓机型可以针对性的对单个机型进行底部安全距离的增加和减少,同时也支持iOS的底部安全距离。在设计和使用api的时候我们往往需要多思考有没有更好的解决办法和为了预防以后带来的动态修改而做出的兼容方案,因为如果一旦发生某个设备在所有页面上的底部安全距离都要增加一段高度,所带来的时间成本会随着项目变大而增长。
https://developers.weixin.qq.com/s/tG9TkCmG7zSs
最后贴上,本次的代码片段链接