横屏错位主因是浏览器在orientationchange时CSS已按新方向重绘但clientWidth/clientHeight未更新,导致100vw/100vh临时反向计算;应改用matchMedia监听+--vh变量动态修正,辅以100dvh@supports回退,并确保html/body高度基础设置。

vw/vh在横屏时突然错位,不是单位写错了
移动端横屏切换后,100vw 可能变成“屏幕高度值”,100vh 反而缩成“屏幕宽度值”——这不是你 CSS 写错了,是浏览器把横竖屏的“视口定义”临时反过来了。尤其在 iOS Safari 中,orientationchange 事件触发时,document.documentElement.clientWidth/clientHeight 还没更新,但 CSS 已按新方向重绘,导致布局跳变一帧甚至卡住。
常见错误现象包括:全屏弹层只盖半屏、轮播图底部被截、position: fixed 按钮沉底失效、height: 100vh 的 section 高度忽大忽小。
- 不要依赖
window.orientation(已废弃,iOS 15+ 返回始终为 0) - 别在
orientationchange回调里直接改 class 或 inline style,容易和 CSSOM 渲染节奏冲突 -
matchMedia('(orientation: landscape)')比事件更可靠,但需手动监听mediaQueryList.addEventListener('change', ...)
用 matchMedia + --vh CSS 变量动态修正高度
核心思路是绕过浏览器对 vh 的错误计算,用 JS 读取真实的可用高度,存进自定义属性,再在 CSS 中引用。这比监听 resize 更精准——因为横屏时 resize 触发太晚,且在地址栏收起/展开时也会误触发。
实操步骤:
- 初始化时执行一次
setVH(),读取window.innerHeight并设为:root { --vh: ${innerHeight}px; } - 用
matchMedia('(orientation: portrait)')和'(orientation: landscape)'分别监听,每次 change 时重新执行setVH() - CSS 中写
height: calc(var(--vh, 100vh) * 1);(加* 1是为了强制重绘,避免部分安卓机型缓存变量)
注意:window.innerHeight 在横屏下返回的是当前可视高度(含地址栏状态),它比 100vh 真实,但比 100dvh 少了自动响应滚动的能力——所以只用于 orientation 切换这种离散场景。
为什么不用 100dvh 直接替代?100dvh 确实是标准解法,但它不解决横屏瞬间的错位问题。原因在于:CSS 单位解析发生在样式计算阶段,而横屏动画过程中,浏览器会先按旧尺寸渲染一帧,再应用新 dvh 值——这一帧就是用户看到的“闪一下”或“错半屏”。
更关键的是兼容性断层:
立即学习“前端免费学习笔记(深入)”;
- iOS Safari 16.4+ 才支持
dvh,而当前仍有约 12% 的活跃设备运行 iOS 15.x(Safari 完全忽略该声明) - Samsung Internet 22.x 及更早版本、部分企业 WebView(如钉钉内置容器)根本不识别
dvh -
@supports (height: 100dvh)检测本身在 Safari 15 中会返回false,但若不加检测,老设备会直接丢弃整条规则,导致高度塌陷
所以 dvh 应作为首选,但必须搭配 min-height: 100vh 回退,且不能用于需要精确像素对齐的动画起点。
横屏后 fixed 元素错位,大概率是父容器高度没撑开
很多开发者给 .modal 加 height: 100vh + position: fixed,横屏后发现它只占半高。这不是 vh 计算错了,而是 fixed 元素脱离文档流,其高度参考的是初始视口,而它的父级(比如 body)可能根本没有设置 min-height: 100% 或 height: 100vh。
修复要点:
-
html, body { height: 100%; }是基础,缺了这句,100vh在某些安卓 WebView 中会按 0 计算 - 对
fixed容器,优先用min-height: 100dvh而非height,防止内容超长时被截断 - 如果用了
env(safe-area-inset-bottom),必须确保它参与的是calc()运算,且单位一致:height: calc(100dvh - env(safe-area-inset-bottom)),不能漏掉env()后面的单位(它返回的是px)
横屏适配真正的难点不在“怎么写”,而在“什么时候写”——CSS 解析时机、JS 读取时机、浏览器重排重绘节奏,三者稍有错位,就会在真机上露出破绽。建议所有横屏关键路径都用真机录屏验证,而不是靠模拟器判断。


















