电话咨询
QQ咨询
微信咨询
返回顶部

做小程序最头疼的事:明明设置了高度,页面还是乱七八糟的

很多做微信小程序的朋友,尤其是刚接触电商、预约、或者内容展示类项目的,十有八九都卡在“高度设置”这个看似简单、实则坑特别多的环节上。你明明在代码里写死了高度,换了一台手机就变形;你用百分比,页面直接乱套;你用vh单位,结果底部弹出键盘时布局飞了。今天这篇文章,就拿这个“高度”问题开刀,像培训班讲课一样,把背后的逻辑、实战的坑、以及能帮你成交客户的解决方案,一层层剥开。

咱们得先理解一个核心矛盾:微信小程序不是网页。网页的视口高度是固定的,但小程序运行在微信这个容器里,它上面有导航栏、胶囊按钮、底部tabBar,甚至还有可能弹出的键盘、自定义的顶部组件。你写一个“height: 100vh”,在网页里是满屏,在小程序里,这个“100vh”会直接顶到屏幕最上方,把微信的导航栏和胶囊按钮给盖住。更麻烦的是,不同手机的导航栏高度不一样,刘海屏、挖孔屏、全面屏手势,都会影响实际的可视区域。如果你只是抄网上的代码,大概率会翻车。

我见过最典型的例子:一个做本地家政预约的小程序,团队把服务列表页的高度写死了750rpx。在iPhone 12上显示完美,到了小米11 Ultra上,底部按钮被刘海挡住了一半,用户点不到“立即预约”。转化率直接掉了30%。后来我们排查发现,问题就出在高度没有适配异形屏。这个案例说明,高度设置不是技术问题,是直接影响你收钱的问题。

那怎么解决?先说一个不知道的底层逻辑:小程序里真正可靠的高度,不是屏幕高度,而是“剩余可用高度”。什么意思?就是屏幕总高度,减去顶部导航栏、底部tabBar、自定义占位组件之后,剩下的那块区域。这块区域才是你放内容的地方。获取这个值,不能用wx.getSystemInfoSync().windowHeight,因为那个返回的是整个屏幕高度,不包括危险区域。正确做法是结合wx.getMenuButtonBoundingClientRect()获取胶囊按钮的位置,再算出导航栏的准确高度。这个API返回的是胶囊按钮左上角和右下角的坐标,你拿它的top值减去状态栏高度,就是导航栏的实际高度。状态栏高度可以用wx.getSystemInfoSync().statusBarHeight拿到。这样算出来的剩余高度,才是真正安全的。

操作步骤来了。第一步,在app.js的onLaunch里,或者页面的onLoad里,调用这两个API:

const systemInfo = wx.getSystemInfoSync();
const menuButton = wx.getMenuButtonBoundingClientRect();
const statusBarHeight = systemInfo.statusBarHeight;
const navBarHeight = menuButton.top - statusBarHeight + menuButton.height + (menuButton.top - statusBarHeight);
const safeAreaHeight = systemInfo.windowHeight - navBarHeight - (底部tabBar高度,如果有的话);

注意,这里底部tabBar高度,如果你的小程序有原生tabBar,可以用systemInfo.windowHeight - systemInfo.screenHeight + systemInfo.statusBarHeight + navBarHeight? 不对,更直接的办法是:如果你的tabBar是自定义的,那就直接在全局变量里记录它的高度;如果是原生的,官方文档说tabBar高度是50px(rpx换算要看屏幕宽度),但不同机型有差异,建议用wx.getSystemInfoSync().screenHeight - wx.getSystemInfoSync().windowHeight - statusBarHeight - navBarHeight,这个差值就是底部非安全区域的高度。不过这个方法在部分安卓机上会返回负数,所以更稳妥的是在页面onReady里用wx.createSelectorQuery().select('.tabBar-class').boundingClientRect()去获取真实高度。

第二步,把这个计算好的safeAreaHeight存入全局变量或者globalData,然后在页面的wxml里,用style="height: {{safeHeight}}px" 来设置容器高度。注意单位是px,不是rpx。因为windowHeight返回的就是px。如果你非要用rpx,那得自己换算:rpx值 = px值 * (750 / screenWidth)。但建议直接用px,避免二次计算误差。

第三步,如果你页面里还有滚动需求,比如列表很长,那就要用scroll-view组件,并给scroll-view设置height: 100%。但这个100%是相对于父容器。所以父容器必须有一个明确的高度。用我们刚才算出的safeAreaHeight给父容器,然后scroll-view用100%继承,这样滚动区域就精准了。

这里有一个独特性很强的技巧:很多教程会让你在scroll-view上直接写死高度,但那样做在键盘弹出时,输入框会被键盘挡住。比如用户填写地址时,键盘弹出来,scroll-view的高度没有变,输入框就跑到键盘下面去了。解决方案是监听键盘事件。在页面的onLoad里,用wx.onKeyboardHeightChange监听键盘高度变化。当键盘弹出时,动态减去键盘高度,重新设置scroll-view的高度。代码大概是:

wx.onKeyboardHeightChange(res => {
this.setData({
safeHeight: safeAreaHeight - res.height
})
})

这样,键盘弹出来时,内容区域自动缩小,输入框始终可见。这个细节,很多大厂的小程序都没做好,你做好了,用户体验直接提升一个档次,用户更愿意在你这下单。

再对比一下常见的坑。网上说用calc(100vh - 某个固定值),比如calc(100vh - 100px)。这个在安卓部分机型上会失效,因为100vh包含了地址栏高度,而地址栏在滚动时会隐藏,导致高度跳动。小程序里没有浏览器地址栏,但100vh依然不准确,因为它没考虑胶囊按钮和导航栏。所以,永远不要用vh单位来设置小程序容器高度,除非你是在一个全屏的canvas或者webview里。

还有一种情况是,你的页面需要自适应内容高度,不需要滚动。比如一个详情页,内容撑开多高就多高。这时候不要设置固定高度,让内容自然撑开。但要注意,如果内容很少,页面底部会露出白色区域,很难看。解决方法是给page标签设置min-height: 100%,但这里的100%是相对于什么?page标签的父容器是微信的视图层,没有明确高度。所以你需要给page标签设置一个背景色,或者用flex布局让内容至少撑满一屏。推荐做法:在app.wxss里,给page设置display: flex; flex-direction: column; min-height: 100vh; 注意这里用100vh是可以的,因为page标签不会被导航栏遮挡,它是整个屏幕的容器。然后你的内容区用flex:1,这样内容少时也能占满屏幕,内容多时自动撑高。

说到本地化,不同城市用户用的手机型号差异很大。比如一线城市iPhone用户多,iPhone的safe area比较规范;三四线城市安卓千元机多,屏幕比例奇怪,且很多有虚拟按键。虚拟按键会占用底部高度,导致你计算的tabBar高度不准。解决办法是在页面onShow里重新获取一次系统信息,因为虚拟按键的显示状态可能会变。另外,安卓机型上,状态栏高度可能不固定,有些手机可以隐藏状态栏。建议在获取statusBarHeight后,判断一下如果小于20px,就当成20px处理,因为大部分状态栏最小高度是20px。

还有一个实战技巧:如果你做的是本地生活类小程序,比如同城配送、社区团购,用户经常需要在地图上选择位置。地图组件的高度设置更讲究。地图本身需要固定高度,但底部如果有操作按钮,要确保按钮不被地图遮挡。我一般把地图放在一个容器里,容器高度用safeAreaHeight减去底部操作栏的高度。这样地图和按钮各占一块,互不干扰。同时,地图的marker点击弹窗也要考虑高度,弹窗内容不要超出地图区域。

最后,说一个能直接帮你挖掘潜在客户的点。很多小程序的“联系我们”或“客服”按钮,因为高度没设置好,在部分手机上被截断或者被键盘挡住,用户想点却点不到。你把高度适配做好后,这些关键转化按钮始终可见,用户随时随地都能联系你。尤其是晚上,用户躺在床上用手机,单手操作,如果按钮位置不对,他可能就放弃咨询了。你损失的不只是一个咨询,而是一个可能的成交客户。所以,高度设置这件事,往小了说是技术细节,往大了说是用户体验的命门。

总结一下核心操作:不要相信任何固定值,用API动态获取胶囊按钮位置和状态栏高度,算出安全区域;监听键盘高度变化动态调整;不同机型做兼容测试,尤其是千元安卓机;关键按钮固定在安全区域内。把这些落地了,你的小程序在高度适配这块,就能超过90%的同行。用户用着顺手,下单自然就顺了。

上一篇
家里扫拖一半手机没电了,小程序能直接唤醒机器人继续干活吗?
下一篇
月薪过万还是“码农民工”?揭秘微信小程序开发者的真实薪资与成长困局