折叠屏设备 viewport 宽度不等于 CSS 媒体查询 width,因浏览器基于 layout viewport 而非物理屏幕;最可靠检测方式是 @media screen and (horizontal-viewport-segments: 2),配合 JS 监听与 fallback 判断。

折叠屏设备的 viewport 宽度不等于 CSS 媒体查询中的 width
折叠屏(如 Samsung Galaxy Z Fold 系列、Microsoft Surface Duo)在展开状态下物理屏幕宽度可能达 2160px,但浏览器报告的 width 媒体查询值往往仍是折叠态的 ~800px(取决于当前 active viewport)。这是因为多数折叠屏默认使用「单窗口模式」,且 Chrome/Edge 对 @media (width) 的取值基于当前 document 的 layout viewport,而非实际可用屏幕区域。
直接套用 Bootstrap 默认断点(md: 768px, lg: 992px)会导致展开后仍触发 md 栅格,列数不上升、内容挤压严重。
- 不要依赖
@media (min-width: 1200px)这类固定像素断点来判断“是否展开” - 优先检测
screen.width或visualViewport.width,再结合screen.orientation.type - Bootstrap 5.3+ 已支持
environment()函数(实验性),但目前仅 Safari 支持environment(screen-width),兼容性差,暂不推荐
用 screen 媒体类型 + dynamic-range 和 horizontal-viewport-segments 检测折叠态
W3C 折叠屏规范定义了两个关键媒体特性:horizontal-viewport-segments(水平分段数)和 vertical-viewport-segments。展开态下,Z Fold 系列通常返回 horizontal-viewport-segments: 2;折叠态为 1。这是目前最可靠的硬件级信号。
Bootstrap 本身不内置对此的支持,需手动扩展断点:
立即学习“前端免费学习笔记(深入)”;
@media screen and (horizontal-viewport-segments: 2) {
.container {
max-width: 1400px;
}
.row > * {
flex: 0 0 calc(50% - 1rem);
}
}- 必须用
@media screen,不能省略,否则部分 Android WebView 不识别 -
horizontal-viewport-segments在未展开或非折叠屏上为 undefined,CSS 会忽略整条规则,安全 - 避免与
min-width混用(如and (min-width: 1000px)),某些设备展开后该值仍低于阈值,导致规则失效
覆盖 Bootstrap 的 grid-breakpoints 并注入动态断点
如果你用 Sass 编译 Bootstrap,可重写 $grid-breakpoints 映射,加入折叠屏专属断点:
$grid-breakpoints: ( xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px, fold: 1800px // 手动添加,仅用于 class 生成 );
然后在 HTML 中用 col-fold-6 类控制展开态栅格。但注意:这个断点不会自动响应折叠/展开切换 —— 它只是静态类名。真正响应式需配合 JS 监听 resize 和 orientationchange,动态增删 fold-active 类到 ,再写对应 CSS:
body.fold-active .col-fold-6 {
flex: 0 0 calc(50% - 1rem);
}- Sass 变量修改后必须重新编译 CSS,无法 runtime 生效
- JS 监听要防抖,
resize在折叠瞬间可能触发多次 - 不要用
window.matchMedia查询horizontal-viewport-segments:Chrome 目前不支持该特性在 JS 中读取,只限 CSS 媒体查询
用 visualViewport + screen.orientation 做 fallback 判断
当 horizontal-viewport-segments 不可用时(如旧版系统或非 Chromium 内核),可用组合信号近似判断:
function isLikelyUnfolded() {
const vw = visualViewport?.width || window.innerWidth;
const orientation = screen.orientation?.type || 'portrait';
return vw > 1400 && orientation.includes('landscape');
}这个函数不能 100% 精确(比如大平板横屏也会满足),但比纯 width 更鲁棒。配合 class 切换后,CSS 可这样写:
.col-unfold-4 {
flex: 0 0 calc(25% - 0.75rem);
}
body.unfolded .col-unfold-4 {
flex: 0 0 calc(50% - 1rem);
}-
visualViewport.width是布局视口宽度,比window.innerWidth更贴近用户实际可见区域 -
screen.orientation在部分 Android 设备上需先请求权限(screen.orientation.lock()),但只读属性一般无需权限 - 不要在 SSR 环境中执行此逻辑,
visualViewport和screen在 Node.js 中不存在
折叠屏适配的关键不在“加更多断点”,而在于放弃把屏幕当普通宽屏处理。horizontal-viewport-segments 是唯一能区分“物理双屏”和“软件模拟宽屏”的信号,其余都是妥协方案。真要全覆盖,得接受 JS + CSS 双层判断,并预留 fallback 样式。


















