推荐用 matchMedia("(orientation: landscape)") 检测横屏,配合 resize 事件监听,避免依赖已废弃的 window.orientation 或兼容性差的 screen.orientation;提示层应使用 fixed 定位 + display: none 控制显隐,禁用 transform 旋转页面。
浏览器没有原生 API 直接返回“横屏”或“竖屏”字符串,但 window.orientation 已被废弃,现在得靠 window.matchMedia 或 screen.orientation 配合 resize 事件来判断。关键不是“当前角度”,而是“宽高关系是否符合横屏逻辑”——因为有些设备(比如折叠屏、桌面浏览器缩放)根本不触发 orientation 变化。
推荐用媒体查询检测:matchMedia("(orientation: landscape)"),它响应 viewport 宽高比变化,兼容性好(Chrome 38+、Firefox 32+、Safari 9+、Edge 12+),且不依赖设备陀螺仪。
matchMedia("(orientation: landscape)").matches 返回 true 表示当前符合横屏条件(即 viewport 宽 ≥ 高)orientationchange 事件——iOS Safari 在某些版本里不触发,安卓 WebView 表现也不一致resize 事件,因为用户拖拽浏览器窗口、分屏、PWA 切换全屏时,orientation 不变但布局已变提示层不能靠 CSS transform: rotate(90deg) 强转整个页面,那会强制重绘、触发 layout thrashing,尤其在低端安卓机上容易卡死。正确做法是:用固定定位浮层 + 独立样式控制,只在需要时显示,且避免影响主内容流。
示例结构:
立即学习“前端免费学习笔记(深入)”;
<div id="landscape-tip" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; justify-content: center; align-items: center; flex-direction: column;"> <div>请将设备旋转为竖屏使用</div> <button onclick="document.getElementById('landscape-tip').style.display='none'">我知道了</button></div>
display: none 控制显隐,别用 opacity: 0 + visibility: hidden 组合——那样 DOM 仍参与渲染流程px 或 em,防止 resize 过程中文字跳动mounted 或 useEffect 里直接监听 resize——需手动 removeEventListener,否则内存泄漏iOS Safari 对 screen.orientation 的支持极弱(Safari 16.4 才开始部分支持,且仅限 PWA 全屏模式),window.orientation 在 iOS 13+ 已完全不可用。所以别碰这两个 API,纯靠 matchMedia + resize 是唯一稳定路径。
matchMedia 可能误判setTimeout 延迟 150ms 再执行提示逻辑,避免 resize 连续触发多次resize,有时延迟高达 500ms,可额外加一次 setTimeout(() => checkOrientation(), 0) 做兜底不要。自动调用 screen.orientation.lock("portrait") 在大多数浏览器里会被拒绝(需用户手势触发 + HTTPS + 全屏权限),而且强行锁屏会破坏用户操作习惯,尤其对辅助功能使用者不友好。
screen.orientation.lock,但仅限用户点击后 5 秒内,超时即失败TypeError: screen.orientation.lock is not a function
横屏检测真正的难点不在代码,而在各种 WebView 容器的行为差异和用户真实操作路径的覆盖——比如分屏、画中画、PWA 安装后首次启动,这些场景下 media query 触发时机都不同,得实机多测。