背景
我们在一个小程序页面里做"空态页"(例如"暂无订单/暂无内容")时,常见做法是放一张全屏背景图,然后在其上叠加文案与入口卡片(两个按钮/卡片入口)。在大多数手机上展示正常,但在部分折叠屏设备上出现"构图跑位、主体被遮挡"的问题。
这篇文章总结一次真实排障过程:为什么折叠屏上会出现"看起来是大屏,但页面宽度却不大"的情况,以及为什么最终选择"改样式铺法"而不是"再加一张背景图"。
现象与复现
现象
- 在某些折叠屏设备上:
- 背景图看起来"没有铺满"或"构图比例不对"
- 背景中关键主体(比如车/人物/大标题)落到前景入口卡片的背后,被遮挡
- 同一张页面在普通手机上正常。
复现提示
复现的关键不是"设备是不是折叠屏",而是"小程序是否运行在折叠屏的窗口化/兼容模式"。
这种模式下常见特征:
- 设备物理上是大屏(甚至左右留白明显)
- 但小程序实际渲染的 viewport 宽度(
windowWidth)却处于手机区间(比如 4xx 左右),没有达到你为折叠/大屏准备的断点。
第一性定位:先确认"媒体查询看到的宽度"
很多人第一反应是"背景图资源选错了",但更稳的第一步是确认布局条件:
- 打印系统信息(示例):
js
const info = wx.getSystemInfoSync()
console.log({
windowWidth: info.windowWidth,
windowHeight: info.windowHeight,
screenWidth: info.screenWidth,
screenHeight: info.screenHeight,
pixelRatio: info.pixelRatio,
safeArea: info.safeArea
})
你会发现一个关键事实:
- 触发"折叠/大屏样式"的条件通常写成
min-width: 某个阈值 - 但窗口化/兼容模式下
windowWidth可能长期低于该阈值,因此媒体查询根本不会命中,页面会一直走"手机单屏"那套样式分支。
为什么"换对背景图"也可能无效?
在这个案例里,我们尝试了两张背景图(分别面向单屏与折叠/大屏),结果在某个宽度段(例如 4xx)两张图都不好看。这类现象通常指向:问题不在"资源选错",而在"铺图策略"本身。
常见根因:用 widthFix 做全屏背景
很多小程序会这样写背景:
html
<image class="bg" src="..." mode="widthFix" />
widthFix 的语义是:
- 宽度按容器撑满
- 高度按图片比例自动计算
这会带来一个不稳定点:
- viewport 宽度变化(375 → 430 → 480 → 500+)时,背景图展示高度会跟着变化
- 但前景元素(文案、入口卡片)往往是
position: fixed/absolute或按固定 bottom/top 定位 - 于是背景与前景的相对关系会漂移:在某个宽度段,背景主体自然可能"跑到卡片后面"。
换句话说:背景是"按宽等比缩放",前景是"按屏幕固定定位",两者对"屏幕宽度变化"的响应机制不同,必然存在不友好的中间态。
为什么"再加一张背景图"是次优选择?
增加一张"4xx 专用背景图"确实能救急,但会带来维护风险:
- 你解决了 440,可能还会遇到 420、460、480......
- 最终会变成"每个宽度段一套素材"的无限扩张
- 视觉资产、版本管理、CDN 缓存、灰度发布都会变复杂
更关键的是:这个问题本质是"铺图策略不稳",素材扩展只能掩盖,不会从根上解决。
解决思路:把铺图从"按宽缩放"改为"铺满裁切"
在 Web 里最常见的稳定方案是:
background-size: coverbackground-position: top center(或 center)
在小程序 <image> 里,最接近 cover 的是 mode="aspectFill":
- 保持比例缩放
- 保证短边完全显示
- 超出容器的部分裁切(达到"铺满"的效果)
最小改动方案(不改变结构,只改 mode + 补全高度)
假设你原来是按断点切换两套背景(单屏/折叠),结构类似:
html
<view class="bg-wrap small">
<image class="bg" src="PHONE_BG" mode="widthFix" />
</view>
<view class="bg-wrap fold">
<image class="bg" src="FOLD_BG" mode="widthFix" />
</view>
最小改动:
- 把
widthFix改为aspectFill - 明确背景容器(或图片本身)是全屏高宽,让
aspectFill的"容器"有确定尺寸
示例(脱敏):
html
<view class="bg-wrap small">
<image class="bg" src="PHONE_BG" mode="aspectFill" />
</view>
<view class="bg-wrap fold">
<image class="bg" src="FOLD_BG" mode="aspectFill" />
</view>
css
.bg {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
}
这样做的效果是:
- 背景永远铺满视口
- 不再因为
widthFix导致高度随宽度变化而"构图漂移" - 在窗口化 4xx 宽度段更稳定,前景更不容易被背景主体"顶到背后"。
注意:
aspectFill默认更像"居中裁切"。如果你的需求是"从顶部优先保留",通常需要进一步调整方案(例如改用背景层 view +background-position: top center),但这会带来更大结构变动。本次以"最小改动"为目标,因此先用aspectFill + 100vh。
验证清单(建议)
为了避免"修好一种设备,伤到另一种设备",建议按这几个维度回归:
- 单屏手机:375/390/414 等宽度
- 折叠屏展开:大宽度(>= 断点)
- 折叠屏窗口化:看起来大屏但
windowWidth处于 4xx(关键复现场景) - 关注点:
- 背景是否铺满
- 入口卡片是否遮挡背景关键主体
- 文案是否仍可读(裁切是否过多)
总结:如何快速判断"该改样式还是加素材?"
经验规则(很实用):
- 如果"换任意一张背景图都不好看":
- 优先怀疑铺图策略(
widthFix、定位方式、容器高度不确定) - 先改样式(cover 思路)通常收益最高
- 优先怀疑铺图策略(
- 如果产品要求"背景关键元素必须完整露出且位置固定":
- 才考虑多套素材(art direction)
- 但要尽量控制断点数量,避免进入"无限补图"的维护地狱
这次案例里,问题的本质是折叠屏窗口化导致的"宽度落入尴尬区间",widthFix 在该区间让背景构图漂移;因此"改铺法"是最稳且可维护的解。