如何在H5页面实现横屏主动切换功能?

如何在H5页面实现横屏主动切换功能。
注意是主动切换横屏,而非是检测横竖屏。
目前尝试了两种方案,一种是通过旋转根节点,同时交换根节点宽高,并通过定位来实现布局中心点的偏移来实现样式上的横屏切换。但是这种方案遇到了一个问题,因为项目中引入了vant组件库,横屏后会导致组件库的一些样式计算出现问题,例如气泡弹出框,横屏后的弹出框的位置计算错误。同时一些选择器,还是需要竖屏下的上下操作才能滑动。以下是实现的一个简单的html示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>横屏切换示例</title>
    <link rel="stylesheet" href="https://unpkg.com/vant@2.12/lib/index.css" />
  </head>
  <style>
    #app {
      transition: all 0.2s;
    }

    .landscape {
      width: 100vh;
      height: 100vw;
      position: absolute;
      top: calc((100vh - 100vw) / 2);
      left: calc(-1 * (100vh - 100vw) / 2);
      transform: rotate(90deg);
      transform-origin: 50% 50%;
    }
    .my-swipe .van-swipe-item {
      color: #fff;
      font-size: 20px;
      line-height: 150px;
      text-align: center;
      background-color: #39a9ed;
    }
  </style>

  <body>
    <div id="app">
      <van-button type="primary" id="toggleFullscreen" @click="switchDirection">
        {{ direction === directionObj.portrait ? "切换为横屏" : "切换为竖屏" }}
      </van-button>
      <van-popover
        v-model="showPopover"
        placement="bottom"
        trigger="click"
        :actions="actions"
        get-container="#app"
        id="popoverBtn"
      >
        <!-- :offset="[-112,-112]" -->
        <template #reference>
          <van-button type="primary">打开popover</van-button>
        </template>
      </van-popover>
      <van-button type="primary" @click="openDialog">打开dialog</van-button>
      <van-button type="primary" @click="showPopup = true">打开Popup</van-button>
      <van-tabs>
        <van-tab title="标签 1">内容 1</van-tab>
        <van-tab title="标签 2">内容 2</van-tab>
        <van-tab title="标签 3">内容 3</van-tab>
        <van-tab title="标签 4">内容 4</van-tab>
      </van-tabs>
      <van-button type="primary" @click="previewImg">预览图片</van-button>
      <div class="content">
        <p>点击按钮切换横竖屏模式。</p>
      </div>
      <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
        <van-swipe-item>1</van-swipe-item>
        <van-swipe-item>2</van-swipe-item>
        <van-swipe-item>3</van-swipe-item>
        <van-swipe-item>4</van-swipe-item>
      </van-swipe>
      <van-popup v-model="showPopup" position="bottom">
        <van-picker title="标题" show-toolbar :columns="columns" />
      </van-popup>
    </div>
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/vant@2.12/lib/vant.min.js"></script>
    <script>
      const directionObj = {
        portrait: "portrait", // 竖屏
        landscape: "landscape", // 横屏
      };
      new Vue({
        el: "#app",
        data: () => {
          return {
            directionObj: directionObj,
            direction: "portrait",
            showPopover: false,
            // 通过 actions 属性来定义菜单选项
            actions: [{ text: "选项一" }, { text: "选项二" }, { text: "选项三" }],
            showPopup: false,
            columns: ["杭州", "宁波", "温州", "绍兴", "湖州", "嘉兴", "金华", "衢州"],
          };
        },
        watch: {
          direction(newValue) {
            const appDom = document.getElementById("app");
            if (newValue === directionObj.portrait) {
              appDom.classList.remove(directionObj.landscape);
              appDom.classList.add(directionObj.portrait);
            } else {
              appDom.classList.remove(directionObj.portrait);
              appDom.classList.add(directionObj.landscape);
            }
          },
        },
        methods: {
          switchDirection() {
            if (this.direction === directionObj.portrait) {
              this.direction = directionObj.landscape;
            } else {
              this.direction = directionObj.portrait;
            }
          },
          openDialog() {
            window.vant.Dialog.confirm({
              title: "标题",
              message: "弹窗内容",
              getContainer: "#app",
            }).then(() => {
              console.log("ok");
            });
          },
          previewImg() {
            window.vant.ImagePreview({
              images: [
                "https://img01.yzcdn.cn/vant/apple-1.jpg",
                "https://img01.yzcdn.cn/vant/apple-2.jpg",
              ],
              startPosition: 0,
            });
          },
        },
      });
    </script>
  </body>
</html>

另外一种也是先通过样式旋转根节点,来实现横屏,然后在用户手动横屏后通过检测设备方向API来取消样式,使页面自然的横屏展示,但是如果用户设置了锁定屏幕,则也有一定的问题。

阅读 9.2k
2 个回答

思路

  1. 通过旋转根节点实现横屏

    • 在切换横屏时,通过旋转根节点来实现横屏效果。
  2. 检测设备方向

    • 在用户手动横屏后,通过设备方向 API 来取消样式,使页面自然横屏展示。
  3. 处理屏幕锁定

    • 如果用户锁定了屏幕方向,提示用户解锁屏幕方向。
  4. 兼容性处理

    • 确保在不同浏览器中都能正常工作。

代码实现

HTML 和 CSS

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>横屏切换示例</title>
    <link rel="stylesheet" href="https://unpkg.com/vant@2.12/lib/index.css" />
  </head>
  <style>
    #app {
      transition: all 0.2s;
    }

    .landscape {
      width: 100vh;
      height: 100vw;
      position: absolute;
      top: calc((100vh - 100vw) / 2);
      left: calc(-1 * (100vh - 100vw) / 2);
      transform: rotate(90deg);
      transform-origin: 50% 50%;
    }

    .portrait {
      /* 竖屏样式 */
    }

    .my-swipe .van-swipe-item {
      color: #fff;
      font-size: 20px;
      line-height: 150px;
      text-align: center;
      background-color: #39a9ed;
    }
  </style>
  <body>
    <div id="app">
      <van-button type="primary" id="toggleFullscreen" @click="switchDirection">
        <template v-if="direction === directionObj.portrait">切换为横屏</template>
        <template v-else>切换为竖屏</template>
      </van-button>
      <!-- 其他 Vant 组件 -->
    </div>
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/vant@2.12/lib/vant.min.js"></script>
    <script>
      const directionObj = {
        portrait: "portrait", // 竖屏
        landscape: "landscape", // 横屏
      };

      new Vue({
        el: "#app",
        data: () => ({
          directionObj: directionObj,
          direction: "portrait",
          showPopover: false,
          actions: [{ text: "选项一" }, { text: "选项二" }, { text: "选项三" }],
          showPopup: false,
          columns: ["杭州", "宁波", "温州", "绍兴", "湖州", "嘉兴", "金华", "衢州"],
        }),
        mounted() {
          const appDom = document.getElementById("app");
          appDom.classList.add(this.direction);
        },
        watch: {
          direction(newValue) {
            const appDom = document.getElementById("app");
            appDom.classList.toggle('landscape', newValue === directionObj.landscape);
            appDom.classList.toggle('portrait', newValue === directionObj.portrait);
          },
        },
        methods: {
          switchDirection() {
            if (this.direction === directionObj.portrait) {
              this.direction = directionObj.landscape;
              this.adjustVantComponentsForLandscape();
            } else {
              this.direction = directionObj.portrait;
              this.resetVantComponents();
            }
          },
          adjustVantComponentsForLandscape() {
            const popovers = document.querySelectorAll('.van-popover');
            popovers.forEach(popover => {
              popover.style.top = '50%';
              popover.style.left = '50%';
              popover.style.transform = 'translate(-50%, -50%)';
            });
          },
          resetVantComponents() {
            const popovers = document.querySelectorAll('.van-popover');
            popovers.forEach(popover => {
              popover.style.top = '';
              popover.style.left = '';
              popover.style.transform = '';
            });
          },
        },
      });

      window.addEventListener('orientationchange', () => {
        const appDom = document.getElementById('app');
        if (window.orientation === 90 || window.orientation === -90) {
          appDom.classList.remove('portrait');
          appDom.classList.add('landscape');
        } else {
          appDom.classList.remove('landscape');
          appDom.classList.add('portrait');
        }
      });

      function lockOrientation() {
        if (screen.orientation && screen.orientation.lock) {
          screen.orientation.lock('landscape').catch(err => {
            console.error('屏幕方向锁定失败:', err);
          });
        } else {
          alert('您的浏览器不支持屏幕方向锁定,请手动切换到横屏模式。');
        }
      }

      // 在切换横屏时调用
      lockOrientation();
    </script>
  </body>
</html>

总结

  1. 通过旋转根节点实现横屏:在切换横屏时,通过旋转根节点来实现横屏效果。
  2. 检测设备方向:在用户手动横屏后,通过设备方向 API 来取消样式,使页面自然横屏展示。
  3. 处理屏幕锁定:如果用户锁定了屏幕方向,提示用户解锁屏幕方向。
  4. 兼容性处理:确保在不同浏览器中都能正常工作。

用 Screen Orientation API解决:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>横屏切换示例</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            transition: all 0.3s ease;
        }
        .container {
            max-width: 100%;
        }
    </style>
</head>
<body>
    <div class="container">
        <button onclick="toggleOrientation()">切换横竖屏</button>
        <div id="content">内容区域</div>
    </div>

    <script>
        async function toggleOrientation() {
            try {
                const currentOrientation = screen.orientation.type;
                
                if (currentOrientation.includes('portrait')) {
                    await screen.orientation.lock('landscape');
                } else {
                    await screen.orientation.lock('portrait');
                }
            } catch (err) {
                console.error('切换屏幕方向失败:', err);
                fallbackRotation();
            }
        }

        function fallbackRotation() {
            const app = document.querySelector('.container');
            if (!app.style.transform) {
                app.style.transform = 'rotate(90deg)';
                app.style.transformOrigin = '50% 50%';
                app.style.position = 'fixed';
                app.style.width = '100vh';
                app.style.height = '100vw';
                app.style.top = '50%';
                app.style.left = '50%';
                app.style.transform = 'translate(-50%, -50%) rotate(90deg)';
            } else {
                app.style.transform = '';
                app.style.position = '';
                app.style.width = '';
                app.style.height = '';
                app.style.top = '';
                app.style.left = '';
            }
        }
    </script>
</body>
</html>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏