手机屏幕回到多形态时代:适配要重算的不只是断点
适配这件事被重新提出,不是因为层叠样式表又添了新语法,而是因为”同一档宽度对应同一种布局”这个前提没了。窄长高屏把可用高度压到很紧,阔屏与小屏同时存在,折叠形态和平板横屏让窗口比例在一个连续区间里变动。于是同一份页面在两台都算”窄屏”的设备上表现完全不同,而差别来源根本不是宽度那一栏。
我们后来把验收标准的写法换掉了:不按机型写,按维度写。维度是逻辑宽度、宽高比、可用高度、系统字号档位、深浅色模式、字重、安全区与开孔这几条。每条维度给出取值区间,测试矩阵就是它们的组合。组合不需要全部跑完,但必须能说清楚漏掉了哪一格、那一格的风险归谁。

一、决定布局分支的是宽高比,不是宽度
最容易被忽略的一条是可用高度。同一个逻辑宽度下,偏窄长的屏幕与接近方形的屏幕,纵向能放的内容条目差得很远。首屏能看到几条列表、吸底操作栏会不会把最后一条盖住、长表单是否需要滚动才能到提交按钮,这三件事都由高度决定,而我们的样式表里几乎只按宽度写分支。
处理方式是让高度成为一级变量。媒体查询里 aspect-ratio 与 orientation 可以按比值分支,比只按宽度分支更接近真实;更稳的做法是别让布局依赖”屏高多少”,而是依赖”这块区域能长多高”——用弹性轨道把可变部分交给内容,首屏条目数由容器高度决定而不是固定数值。
移动浏览器还有一层麻烦:工具栏会随滚动收放,100vh 不等于用户实际看到的可视高度。吸底栏用 100vh 定位时,常被工具栏压住一条边,或在两者之间留出一条穿帮的缝。相对可用的写法是动态视口单位配合安全区内边距,需要精确时读 visualViewport 的高度与偏移,写进自定义属性再由样式消费。软键盘弹出后底部内容被遮挡,不同浏览器对”键盘是否压缩可视区域”的默认行为不一致,凡是底部有输入框的流程都要实测,不能当作已统一的能力。
二、设备白名单换成布局约束
按机型或厂商做白名单,问题不在于判断不准,而在于它描述的是”设备是什么”,不是”容器有多大”。新形态出现的那天,白名单就得回归一次;同一台设备分屏、半屏、悬浮窗状态下,白名单还会给出错误答案。
替代手段是把判断权交给约束。
- 容器查询让组件按自己所在容器的行内尺寸分支。卡片放进主栏还是侧栏,由容器宽度决定,同一份组件代码不用为页面位置各写一套断点。
clamp()与min()/max()把字号、间距、区域高度写成随视口连续变化的函数,控制增长斜率而不是跳档。斜率两端要用最小值和最大值收住,否则极窄时文字小到不可读,极宽时段落行长失控。- 栅格轨道用自动填充加最小轨道宽度,列数由可用宽度算出来。这样列数永远不为零,也不需要为某个具体比例补一条分支。
- 换行与间距交给弹性布局和
gap,靠flex-wrap让内容自己找行,比手写”低于某个宽度时改为竖排”少一条判断。
断点本身要当成成本记账。每加一个断点,就多一条需要维护的分支、多一套截图基线、多一处只在特定宽度复现的缺陷。只在信息密度或操作位置真的改变时才加分支,其余情况让它连续变化。判断逻辑尽量留在样式层,确实需要测量时用观察器回调,别把宽度数值烧进脚本常量。
三、单手可达区与底部操作栏
窄长屏带来的不是”屏幕变大”,而是拇指可达范围相对屏幕变小。拇指自然张开时的活动范围是一个弧,弧的右下部分是热区,屏幕左上角是冷区。把主操作放在顶部或屏幕角落,在窄长屏上就是让用户体验一次够不着。
阔屏的反向问题更隐蔽。为了利用宽度,常规做法是从单列改成左右分栏,结果主行动按钮被推到左栏顶部或右栏边角,正好落进拇指死角;更糟的是左右手用户的可达区是镜像的,固定放在任何一侧都有一半用户不方便。分栏本身不是问题,把工具栏做成一整条通栏、所有行动入口都在上方,才是问题。
对应的做法:主操作收进吸底操作条,并在条内加上安全区内边距,避免被底部手势区吃掉一半高度;每个分栏保留自己的底部入口,而不是共用顶部通栏;次级功能放顶部图标,接受它们需要另一只手;滑动与长按操作避开屏幕左右边缘,让出系统手势的起始区域;命中区域按下限设计而不是按图形大小,相邻按钮之间留可分辨的间隔;只在真正支持悬停的设备上依赖悬停提示,触屏上这类信息永远不出现。
四、字号缩放、粗体、深色模式三个变量
这三个变量都不改变宽度断点,却能单独撑破一套调好的布局,而它们大多是用户在系统设置里主动开的,不是异常状态。
字号:系统字号调大后会同时影响原生与网页文本。行高随之变长,写死高度的按钮和标签开始裁字;标题从两行变三行,把整块内容顶出可视区;带行数上限的截断在高档位把关键信息吃干净。对策是把高度还给内容——用最小高度而不是固定高度,文本行高用相对单位,字号基准放在根元素上以便整体缩放,截断处保留展开入口。
字重:系统的加粗文本设置会把整站字重上调,行长增加几个百分点,恰好触发换行或溢出。薄弱点集中在单行标题、表格列头、按钮文案和带图标的标签。表格里给列设最小宽度并允许横向滚动,比强迫单行显示更稳;按钮文案要允许两行或按内容伸缩。
深浅色:除了跟随偏好切换配色,还要声明表单控件的颜色方案,否则原生输入框、滚动条和下拉列表仍保持亮色底。带白底的截图与扫描件在深色界面上刺眼,需要容器化的降亮策略而不是直接显示。半透明描边与浅灰分割线在深色下几乎不可见,层级关系不能只靠阴影表达,深色要靠描边或背景明度差。
测试办法是固定组合而不是随机点测:三个字号档位、两种颜色模式、两档字重,乘上两到三个宽高比,只挑五到八个信息密度最高的页面跑截图比对。表单页、单证列表页、详情页通常就是最脆弱的那几个。
五、开孔、圆角、手势区与媒体裁切
屏幕开孔、圆角与系统手势是三类遮挡,共同点是它们不出现在可视尺寸里,只能靠运行时环境变量取到。
顶部一行内容在开孔区域被切掉是最常见的现象,尤其当开孔偏在一侧时,居中的标题会被啃掉半个字。圆角会把贴角的点击目标裁掉一角,右上角的关闭按钮看起来还在,但命中区域缺了外侧那块。系统侧滑返回占用屏幕左右边缘的一条窄带,页面里自绘的左滑删除、抽屉和返回按钮会和它抢同一片区域。
对策并不复杂:顶部固定条带按安全区补内边距,并且不拿整行高亮当点击区;贴角控件离角至少一个安全区的距离;左边缘的滑动操作把起始判定从边缘向内让开;不要在开孔区域放任何不可丢失的信息,包括倒计时和状态徽标。
媒体在比例差异下的策略要写成产品规则,而不是让每个页面各自决定。给图片容器声明宽高比,既占位又避免内容回来后重排;object-fit 的覆盖模式是裁切而非缩放,主体在画面里的位置要靠定位属性显式约定,构图敏感的图(含人脸、含表格左上角、含印章)更适合等比容纳或按比例出多套图。需要服务端出图时,按设备像素比与目标比例生成,前端只负责声明需求,不要靠拉伸补尺寸。同一张横图在竖屏卡片里保留哪一块,这是内容规则,写进组件文档,比在样式表里散落十来个不同的定位值好维护。
六、按维度写验收而不是按机型:洹拓视角
真机数量永远不够,所以顺序要反过来:先用便宜的虚拟组合覆盖面,再用少量真机覆盖极值,最后把人工走查留给截图比不出来的项。
| 形态维度 | 应对手段 | 出问题时用户看到什么 |
|---|---|---|
| 逻辑宽度 | 自动填充栅格、容器查询 | 列数挤爆或只剩一列,横向出现滚动条 |
| 宽高比与可用高度 | 按比值分支、弹性轨道、动态视口单位 | 首屏只有一条内容,提交按钮看不见 |
| 工具栏收放 | 动态视口单位、可视区域 API | 吸底栏被压住一条边或露出缝隙 |
| 系统字号档位 | 最小高度、相对行高、可展开截断 | 按钮里的字被切半,标题吃掉整屏 |
| 系统加粗 | 列最小宽度、按钮按内容伸缩 | 文案换行撑破行、表头错位 |
| 深浅色模式 | 颜色方案声明、描边分层、降亮容器 | 输入框仍是亮色底,分割线消失 |
| 安全区与开孔 | 环境变量内边距、贴角避让 | 标题被啃半个字,关闭按钮点不中 |
| 边缘手势 | 让出起始区、改长按或右侧滑动 | 侧滑返回与滑动删除互相打架 |
| 媒体比例 | 宽高比占位、按 DPR 与比例出图 | 图被裁掉主体或出现拉伸变形 |
组织顺序上,我们固定六步。
- 把维度与取值区间写进测试计划。宽度取几档、比例取几档、字号取几档、深浅与加粗各两档,这张表评审时就是风险清单。
- 先跑全虚拟组合。浏览器与模拟器改视口很快,截图比对覆盖全部宽度与比例交叉,这一步不需要真机。
- 真机只买形态代表,各代表一个维度极值:一台窄长的、一台偏阔或可折叠半屏的、一台性能与字重基线偏低的低端设备。数量不足要写明它们各自代表哪条维度的边界。
- 关键流程人工走查,只查截图查不出的四项:单手可达性、键盘弹起遮挡、深色下的对比度、截断后的可读性。
- 缺陷归因回写维度表。每条问题标注它落在哪一格,长期反复出问题的格子,说明那里还锁在媒体查询里,应该改成容器或流式写法。
- 交付文档写维度声明,不写机型清单。写”在上述区间内保证首屏条目数与操作可达”,机型清单会随一次上新作废,维度不会。
洹拓视角:企业应用与活动页交付里,返工最多的场景是甲方把一台设备当成全部验收条件,换一台就重开一轮。把验收条款按维度写,两边都省事——需求方拿到的是可判定的边界,交付方拿到的是不必反复自证的清单。形态碎片化不会收敛,能收敛的只有我们描述它的方式。