横竖屏适配的实战技巧与避坑指南

轩辕慧娜 移动 阅读 1,574
赞 10 收藏
二维码
手机扫码查看
反馈

先上代码,再讲别的

我最近在搞一个移动端视频播放页,客户非得要求横屏时全屏撑满、竖屏自动缩回去,还得适配各种安卓机和iOS。折腾了两天,踩了不少坑,但最终方案还是稳住了。今天不绕弯子,直接把最核心的实现贴出来,后面再聊细节。

横竖屏适配的实战技巧与避坑指南

// 监听设备方向变化
window.addEventListener('orientationchange', function () {
  setTimeout(() => {
    handleOrientation(window.orientation);
  }, 100); // 延迟一下,确保尺寸已更新
});

// 初始化
handleOrientation(window.orientation);

function handleOrientation(orientation) {
  const isLandscape = orientation === 90 || orientation === -90;

  if (isLandscape) {
    document.body.classList.add('fullscreen-landscape');
    document.documentElement.style.overflow = 'hidden';
  } else {
    document.body.classList.remove('fullscreen-landscape');
    document.documentElement.style.overflow = '';
  }

  // 触发重绘或通知组件调整
  window.dispatchEvent(new Event('resize'));
}
.fullscreen-landscape {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

.video-container {
  width: 100%;
  height: 0;
  padding-bottom: 56.25%; /* 16:9 比例 */
  position: relative;
  background: #000;
}

.video-container iframe,
.video-container video {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* 横屏时撑满屏幕 */
.fullscreen-landscape .video-container {
  padding-bottom: 0;
  height: 100vh;
  width: 100vw;
}
<div class="video-container">
  <video src="https://jztheme.com/video/demo.mp4" controls></video>
</div>

上面这套组合拳我在 iOS 和主流安卓机型上都亲测有效。尤其是那个 setTimeout 加 100ms 的延迟,别小看它——这是我在三个不同项目里反复验证出来的最优解。因为 orientationchange 触发时,DOM 尺寸还没来得及更新,直接读 window.innerWidth 可能不准。

这个场景最好用:视频/游戏类页面强制横屏

有些业务就是必须横屏才能看全,比如 H5 游戏、视频播放器、图表报表。这时候你可以更激进一点:

function lockLandscape() {
  if (screen.orientation && screen.orientation.lock) {
    screen.orientation.lock('landscape').catch(err => {
      console.warn('无法锁定横屏:', err);
    });
  }
}

注意!这玩意儿不是所有浏览器都支持。iOS Safari 到现在还不完全支持 screen.orientation.lock(),所以不能当主力方案依赖。建议只在 Android 上开启,并做好降级处理。我之前在一个教育类项目里用了这个,结果一堆家长投诉“手机转不过来”,最后只能 fallback 到提示用户手动旋转。

纯 CSS 实现?也行,但有局限

如果你只是想做点简单的布局切换,其实可以用 media query 来搞定:

@media (orientation: landscape) {
  .content {
    flex-direction: row;
    font-size: 16px;
  }
}

@media (orientation: portrait) {
  .content {
    flex-direction: column;
    font-size: 14px;
  }
}

这种方式轻量、无 JS 干预,适合信息展示类页面。但它有个致命问题:不会触发 JS 逻辑更新。比如你有个轮播图,在横屏时想改显示数量,光靠 CSS 是做不到的。所以我现在的做法是 JS + CSS 结合:JS 控制类名切换,CSS 负责样式表现。

踩坑提醒:这三点一定注意

  • 安卓 WebView 兼容性差到离谱:很多老版本安卓机根本不触发 orientationchange,或者返回的 window.orientation 值永远是 0。我的解决方案是加一个 resize 事件兜底:
let lastWidth = window.innerWidth;

window.addEventListener('resize', () => {
  const newWidth = window.innerWidth;
  if (Math.abs(newWidth - lastWidth) > 50) { // 宽高变化明显
    handleOrientation(window.orientation);
    lastWidth = newWidth;
  }
});
  • iOS 输入框弹起会干扰判断:键盘弹出时也会触发 resize,而且 orientation 没变,但 viewport 高度变了。这里一定要加过滤条件,避免误判成横竖屏切换。我现在习惯在处理前先判断是否聚焦了输入框:
if (document.activeElement && ['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
  return; // 忽略键盘引起的 resize
}
  • 三星部分机型横屏下状态栏占空间:某些 Galaxy 手机横屏时顶部会有系统 UI 占据高度,导致内容被挤压。解决办法是在 meta 中加上:
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
    特别是 viewport-fit=cover 这个属性,能让内容延伸到安全区域外,配合 CSS env() 使用效果更好。

高级技巧:模拟 orientation 值,方便调试

你总不能每次测试都手动转手机吧?Chrome DevTools 虽然可以切设备类型,但有时候也不够灵。我写了个调试工具函数,方便本地快速验证:

// 开发环境注入
function simulateOrientation(angle) {
  Object.defineProperty(window, 'orientation', {
    value: angle,
    writable: true,
    configurable: true
  });
  window.dispatchEvent(new Event('orientationchange'));
}

// 使用时:
// simulateOrientation(90);  // 模拟横屏
// simulateOrientation(0);   // 模拟竖屏

结合本地 localStorage 开关,甚至可以做个浮动按钮挂在页面角落,点击就切换,极大提升调试效率。这个技巧我已经在多个项目中复用,省了不少时间。

还有一个隐藏问题:页面加载时的初始状态

很多人忘了处理页面刚打开时的方向判断。如果用户一开始就横着拿手机,orientationchange 是不会触发的。所以一定要在初始化时主动调一次:

// 页面加载完成后立即检测当前方向
document.addEventListener('DOMContentLoaded', () => {
  handleOrientation(window.orientation);
});

不然就会出现“必须转一圈才正常”的诡异现象,用户体验极差。这个坑我第一次上线时就栽了,线上反馈一堆“刚打开黑屏”,排查半天才发现是没初始化。

拓展思路:不只是布局,还能控制行为

除了视觉层面的适配,其实还可以用横竖屏状态来控制交互逻辑。比如:

  • 横屏时启用双指缩放查看大图
  • 竖屏时隐藏复杂菜单,只留核心操作按钮
  • 横屏自动播放视频,竖屏暂停

这些都可以通过监听方向变化来动态绑定/解绑事件,灵活得很。

总结一下我现在的标准流程

我现在做移动端横竖屏适配,基本按这个套路来:

  1. HTML 加 viewport-fit=cover
  2. JS 初始化时检测一次方向
  3. 监听 orientationchange + resize(兼容安卓)
  4. 加 setTimeout 等尺寸稳定
  5. CSS 用类名驱动,JS 只负责状态
  6. 排除 input 聚焦干扰
  7. 开发环境加模拟函数

这套组合下来,目前覆盖了我手上所有项目的适配需求,包括一些奇葩的定制 ROM 机型。

以上是我踩坑后的总结,希望对你有帮助。这个技巧的拓展用法还有很多,比如结合 gyroscope API 做更精细的方向感知,后续会继续分享这类博客。有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论