唯一能精准识别物理展开态的CSS方式是用@supports检测horizontal-viewport-segments:2或vertical-viewport-segments:2,二者分别对应竖折与横折设备的真实双屏分割态,且必须单独使用、不可混写min-width,Safari/Firefox不支持需降级。

用 horizontal-viewport-segments 和 vertical-viewport-segments 判断物理展开态
目前唯一能精准识别“是否真实展开”的 CSS 方式,是检测浏览器是否在渲染两个独立视口——这由铰链物理分割决定,不是靠宽高猜的。horizontal-viewport-segments: 2 表示竖折设备(如 Z Fold)已展开,左右各一个 viewport;vertical-viewport-segments: 2 表示横折设备(如 Pocket 2)已展开,上下各一个 viewport。
必须用 @supports 包裹,否则旧浏览器会直接丢弃整条规则:
@supports (horizontal-viewport-segments: 2) {
@media (horizontal-viewport-segments: 2) {
.sidebar { flex-basis: 300px; }
.main { flex-basis: calc(100% - 300px); }
}
}-
horizontal-viewport-segments和vertical-viewport-segments值为1时代表折叠态或普通手机,只有2才表示双屏物理分割 - 不能和
min-width混写,比如@media (min-width: 768px) and (horizontal-viewport-segments: 2)是错的——前者看总宽,后者只在单侧 viewport 下为真,逻辑冲突 - Safari 和 Firefox 当前完全不支持这两个特性,降级必须另起一套规则,不能指望 fallback 自动生效
为什么 screen-spanning 不能用来识别展开状态
screen-spanning 这个媒体特征根本不存在,Chrome/Edge 从未正式实现它,MDN 已移除文档,DevTools 里也看不到匹配状态。你写 @media (screen-spanning: single-fold-vertical),浏览器直接忽略,不报错也不生效——不是语法问题,是它压根没被解析。
它描述的是“当前元素是否被强制画在两个屏幕上”,不是“设备是否展开”。即使强行启用(比如通过 chrome://flags),在 Z Fold 6、Mate X5 等真实设备上也常返回 not all 或静默失败。
立即学习“前端免费学习笔记(深入)”;
- 所有基于
screen-spanning的 polyfill(如 fold-detect v2.x)已在 2026 年全部弃用 - 别信“实验性支持”说法:Chromium 内部早已标记该特性为“废弃”,连测试覆盖率都已移除
- 用它做判断,上线后样式大概率白屏或错乱,且无法 debug
降级方案要分场景写,不能只靠 min-width
当 @supports 不成立(Safari/Firefox/旧 Chrome),得用组合条件逼近展开态,但必须区分设备类型:
- 竖折设备(Z Fold 类)降级:
@media (min-width: 720px) and (min-height: 840px) and (orientation: landscape)—— 排除平板横屏干扰 - 横折设备(Pocket 类)降级:
@media (min-width: 400px) and (min-height: 1200px) and (orientation: portrait)—— 利用其展开后高而窄的特点 - Pixel Fold 需额外加
display-mode: browser和dynamic-range: high双条件,否则内屏 HDR 样式不触发
注意:min-width: 768px 单独使用等于没适配——Z Fold 展开后单侧宽度仅 ~390px,但已是双屏;而 iPad 横屏轻松超 1024px,全被误卷进来。
JS 监听必须读 visualViewport.width 并加防抖
CSS 媒体查询只响应静态快照,但折叠过程有半开、动画帧、系统延迟上报等中间态,matchMedia 会频繁触发 false positive。直接监听会导致 UI 抖动、布局重排爆炸。
正确做法是:
const mq = window.matchMedia('(horizontal-viewport-segments: 2)');
const handleFoldChange = () => {
// 读 visualViewport.width,不是 window.innerWidth
const width = window.visualViewport?.width || window.innerWidth;
if (width > 400 && width < 500) {
document.documentElement.setAttribute('data-fold', 'horizontal');
}
};
mq.addEventListener('change', handleFoldChange);
// 必须加至少 100ms 防抖,Z Fold 展开动画约 300ms,中间态极多-
window.innerWidth在折叠动画中可能卡在中间值(如 523px),visualViewport.width更贴近真实渲染视口 - 不要用
window.onresize替代matchMedia,resize 在折叠过程中触发次数不可控 - 如果
visualViewport未定义(如旧 WebView),需 fallback 到screen.orientation+ UA 检测
真正难的不是写对那几行媒体查询,而是意识到:折叠态不是尺寸问题,是渲染管线问题。Chrome 119+ 把双视口当作两个独立渲染上下文处理,document.body.clientWidth 返回单侧宽度,position: sticky 在跨铰链时失效,这些底层行为决定了你不能把折叠屏当“大号手机”来对待。


















