欢迎光临中国葬花网
详情描述

1. 全局横屏配置

pages.json 中配置页面方向

{
  "globalStyle": {
    // App 全局配置
    "app-plus": {
      "orientation": [
        "portrait-primary",
        "landscape-primary",
        "portrait-secondary",
        "landscape-secondary"
      ]
    }
  },
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页",
        // 小程序配置
        "mp-weixin": {
          "pageOrientation": "auto" // auto, portrait, landscape
        },
        // App 配置
        "app-plus": {
          "orientation": "landscape-primary" // 固定横屏
        }
      }
    }
  ]
}

2. 横屏工具类

创建 utils/screen.js

/**
 * 屏幕方向管理工具类
 */
class ScreenManager {
  constructor() {
    this.currentOrientation = 'portrait'
  }

  /**
   * 检测当前设备平台
   */
  getPlatform() {
    // #ifdef APP-PLUS
    return 'app'
    // #endif

    // #ifdef MP-WEIXIN
    return 'wx'
    // #endif

    return 'h5'
  }

  /**
   * 锁定横屏模式(App端)
   */
  lockLandscape() {
    const platform = this.getPlatform()

    switch(platform) {
      case 'app':
        this.lockAppLandscape()
        break
      case 'wx':
        this.lockWxLandscape()
        break
      default:
        this.lockH5Landscape()
    }
  }

  /**
   * 锁定竖屏模式
   */
  lockPortrait() {
    const platform = this.getPlatform()

    switch(platform) {
      case 'app':
        this.lockAppPortrait()
        break
      case 'wx':
        this.lockWxPortrait()
        break
      default:
        this.lockH5Portrait()
    }
  }

  /**
   * 允许自动旋转
   */
  unlockOrientation() {
    const platform = this.getPlatform()

    switch(platform) {
      case 'app':
        this.unlockAppOrientation()
        break
      case 'wx':
        this.unlockWxOrientation()
        break
      default:
        this.unlockH5Orientation()
    }
  }

  /**
   * App 端锁定横屏
   */
  lockAppLandscape() {
    // #ifdef APP-PLUS
    plus.screen.lockOrientation('landscape-primary')
    // #endif
    this.currentOrientation = 'landscape'
  }

  /**
   * App 端锁定竖屏
   */
  lockAppPortrait() {
    // #ifdef APP-PLUS
    plus.screen.lockOrientation('portrait-primary')
    // #endif
    this.currentOrientation = 'portrait'
  }

  /**
   * App 端解锁方向
   */
  unlockAppOrientation() {
    // #ifdef APP-PLUS
    plus.screen.unlockOrientation()
    // #endif
  }

  /**
   * 微信小程序横屏处理
   */
  lockWxLandscape() {
    // #ifdef MP-WEIXIN
    // 微信小程序通过页面配置实现
    // 需要在页面配置中设置 pageOrientation: 'landscape'
    // 动态修改需要使用 wx.setPageOrientation
    if (typeof wx !== 'undefined' && wx.setPageOrientation) {
      wx.setPageOrientation({
        orientation: 'landscape',
        success: () => {
          console.log('已切换到横屏')
        },
        fail: (err) => {
          console.error('切换横屏失败:', err)
        }
      })
    }
    // #endif
    this.currentOrientation = 'landscape'
  }

  lockWxPortrait() {
    // #ifdef MP-WEIXIN
    if (typeof wx !== 'undefined' && wx.setPageOrientation) {
      wx.setPageOrientation({
        orientation: 'portrait',
        success: () => {
          console.log('已切换到竖屏')
        }
      })
    }
    // #endif
    this.currentOrientation = 'portrait'
  }

  unlockWxOrientation() {
    // #ifdef MP-WEIXIN
    if (typeof wx !== 'undefined' && wx.setPageOrientation) {
      wx.setPageOrientation({
        orientation: 'auto'
      })
    }
    // #endif
  }

  /**
   * H5 端方向控制
   */
  lockH5Landscape() {
    if (window.screen.orientation && window.screen.orientation.lock) {
      window.screen.orientation.lock('landscape')
        .then(() => {
          this.currentOrientation = 'landscape'
        })
        .catch(err => {
          console.error('H5横屏锁定失败:', err)
        })
    }
  }

  lockH5Portrait() {
    if (window.screen.orientation && window.screen.orientation.lock) {
      window.screen.orientation.lock('portrait')
        .then(() => {
          this.currentOrientation = 'portrait'
        })
    }
  }

  unlockH5Orientation() {
    if (window.screen.orientation && window.screen.orientation.unlock) {
      window.screen.orientation.unlock()
    }
  }

  /**
   * 监听屏幕方向变化
   * @param {Function} callback 回调函数
   */
  onOrientationChange(callback) {
    const platform = this.getPlatform()

    if (platform === 'app') {
      // #ifdef APP-PLUS
      plus.screen.onOrientationChange((res) => {
        callback && callback(res.orientation)
      })
      // #endif
    } else if (platform === 'wx') {
      // #ifdef MP-WEIXIN
      wx.onWindowResize(() => {
        this.getScreenInfo((info) => {
          callback && callback(info.orientation)
        })
      })
      // #endif
    } else {
      // H5
      window.addEventListener('orientationchange', () => {
        setTimeout(() => {
          const orientation = window.orientation
          callback && callback(orientation === 90 || orientation === -90 ? 'landscape' : 'portrait')
        }, 300)
      })
    }
  }

  /**
   * 获取屏幕信息
   */
  getScreenInfo(callback) {
    const platform = this.getPlatform()

    if (platform === 'app') {
      // #ifdef APP-PLUS
      const res = {
        width: plus.screen.resolutionWidth,
        height: plus.screen.resolutionHeight,
        orientation: this.currentOrientation
      }
      callback && callback(res)
      // #endif
    } else if (platform === 'wx') {
      // #ifdef MP-WEIXIN
      wx.getSystemInfo({
        success: (res) => {
          callback && callback({
            width: res.screenWidth,
            height: res.screenHeight,
            orientation: res.screenWidth > res.screenHeight ? 'landscape' : 'portrait'
          })
        }
      })
      // #endif
    } else {
      // H5
      callback && callback({
        width: window.innerWidth,
        height: window.innerHeight,
        orientation: window.innerWidth > window.innerHeight ? 'landscape' : 'portrait'
      })
    }
  }

  /**
   * 检查是否支持横屏
   */
  isLandscapeSupported() {
    const platform = this.getPlatform()

    if (platform === 'app') {
      // #ifdef APP-PLUS
      return true
      // #endif
    } else if (platform === 'wx') {
      // #ifdef MP-WEIXIN
      return typeof wx !== 'undefined' && !!wx.setPageOrientation
      // #endif
    } else {
      return !!(window.screen.orientation && window.screen.orientation.lock)
    }

    return false
  }
}

export default new ScreenManager()

3. 横屏页面组件

创建 components/landscape-container.vue

<template>
  <view class="landscape-container" :class="orientationClass">
    <!-- 横屏内容 -->
    <slot v-if="isLandscape || orientation === 'landscape'"></slot>

    <!-- 竖屏提示 -->
    <view v-else class="portrait-tip" :style="tipStyle">
      <view class="tip-content">
        <image 
          v-if="tipIcon" 
          :src="tipIcon" 
          class="tip-icon"
        />
        <text class="tip-text">{{ tipText }}</text>
        <button 
          v-if="showRotateBtn" 
          @click="rotateScreen" 
          class="rotate-btn"
        >
          旋转屏幕
        </button>
      </view>
    </view>
  </view>
</template>

<script>
import screenManager from '@/utils/screen.js'

export default {
  name: 'LandscapeContainer',

  props: {
    // 是否强制横屏
    forceLandscape: {
      type: Boolean,
      default: true
    },
    // 竖屏提示文本
    tipText: {
      type: String,
      default: '请将手机横屏以获得最佳体验'
    },
    // 提示图标
    tipIcon: {
      type: String,
      default: '/static/rotate-icon.png'
    },
    // 是否显示旋转按钮
    showRotateBtn: {
      type: Boolean,
      default: true
    },
    // 横屏模式背景色
    landscapeBgColor: {
      type: String,
      default: '#ffffff'
    },
    // 竖屏提示背景色
    tipBgColor: {
      type: String,
      default: '#f5f5f5'
    }
  },

  data() {
    return {
      orientation: 'portrait',
      screenWidth: 0,
      screenHeight: 0,
      isLandscape: false
    }
  },

  computed: {
    orientationClass() {
      return {
        'landscape-mode': this.isLandscape,
        'portrait-mode': !this.isLandscape
      }
    },

    tipStyle() {
      return {
        backgroundColor: this.tipBgColor,
        width: `${this.screenWidth}px`,
        height: `${this.screenHeight}px`
      }
    },

    containerStyle() {
      return {
        backgroundColor: this.landscapeBgColor
      }
    }
  },

  mounted() {
    this.initScreen()
    this.bindOrientationChange()
  },

  beforeDestroy() {
    if (this.forceLandscape) {
      screenManager.unlockOrientation()
    }
  },

  methods: {
    // 初始化屏幕状态
    initScreen() {
      screenManager.getScreenInfo((info) => {
        this.screenWidth = info.width
        this.screenHeight = info.height
        this.orientation = info.orientation
        this.isLandscape = info.orientation === 'landscape'

        // 如果需要强制横屏
        if (this.forceLandscape && !this.isLandscape) {
          this.lockLandscape()
        }
      })
    },

    // 绑定方向变化监听
    bindOrientationChange() {
      screenManager.onOrientationChange((orientation) => {
        this.orientation = orientation
        this.isLandscape = orientation === 'landscape'

        // 更新屏幕尺寸
        screenManager.getScreenInfo((info) => {
          this.screenWidth = info.width
          this.screenHeight = info.height
        })
      })
    },

    // 锁定横屏
    lockLandscape() {
      if (screenManager.isLandscapeSupported()) {
        screenManager.lockLandscape()
        this.isLandscape = true
        this.orientation = 'landscape'
      } else {
        uni.showToast({
          title: '当前设备不支持横屏模式',
          icon: 'none'
        })
      }
    },

    // 旋转屏幕按钮点击
    rotateScreen() {
      this.lockLandscape()
    },

    // 获取当前屏幕信息
    getScreenInfo() {
      return {
        width: this.screenWidth,
        height: this.screenHeight,
        orientation: this.orientation,
        isLandscape: this.isLandscape
      }
    }
  }
}
</script>

<style scoped>
.landscape-container {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  transition: all 0.3s ease;
}

/* 横屏模式 */
.landscape-mode {
  transform: rotate(0deg);
}

/* 竖屏提示 */
.portrait-tip {
  display: flex;
  justify-content: center;
  align-items: center;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
}

.tip-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40rpx;
  text-align: center;
}

.tip-icon {
  width: 200rpx;
  height: 200rpx;
  margin-bottom: 40rpx;
  animation: rotate 2s linear infinite;
}

.tip-text {
  font-size: 32rpx;
  color: #666;
  margin-bottom: 40rpx;
  line-height: 1.5;
}

.rotate-btn {
  background-color: #007aff;
  color: white;
  padding: 20rpx 40rpx;
  border-radius: 50rpx;
  font-size: 28rpx;
  border: none;
  outline: none;
}

.rotate-btn:active {
  background-color: #0056cc;
  transform: scale(0.95);
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* 横屏时隐藏提示 */
.landscape-mode .portrait-tip {
  display: none;
}
</style>

4. 使用示例

示例页面 pages/landscape-demo/index.vue

<template>
  <landscape-container 
    ref="landscapeContainer"
    :force-landscape="true"
    tip-text="请将设备横屏以继续游戏"
    @orientation-change="onOrientationChange"
  >
    <!-- 横屏内容 -->
    <view class="game-container">
      <!-- 游戏标题 -->
      <view class="game-header">
        <text class="game-title">横屏游戏示例</text>
        <button class="orientation-btn" @click="toggleOrientation">
          {{ isLandscape ? '切换竖屏' : '切换横屏' }}
        </button>
      </view>

      <!-- 游戏内容区域 -->
      <view class="game-content">
        <view class="game-board">
          <!-- 游戏内容 -->
          <text class="game-text">横屏游戏内容区域</text>
        </view>

        <!-- 游戏控制 -->
        <view class="game-controls">
          <button class="control-btn" @click="handleAction('left')">左</button>
          <button class="control-btn" @click="handleAction('action')">行动</button>
          <button class="control-btn" @click="handleAction('right')">右</button>
        </view>
      </view>

      <!-- 屏幕信息显示 -->
      <view class="screen-info">
        <text>屏幕尺寸: {{ screenWidth }} × {{ screenHeight }}</text>
        <text>方向: {{ orientation }}</text>
        <text>支持横屏: {{ isLandscapeSupported ? '是' : '否' }}</text>
      </view>
    </view>
  </landscape-container>
</template>

<script>
import landscapeContainer from '@/components/landscape-container.vue'
import screenManager from '@/utils/screen.js'

export default {
  components: {
    landscapeContainer
  },

  data() {
    return {
      screenWidth: 0,
      screenHeight: 0,
      orientation: 'portrait',
      isLandscape: false,
      isLandscapeSupported: false
    }
  },

  onLoad() {
    this.checkLandscapeSupport()
    this.getScreenInfo()
  },

  onReady() {
    // 页面加载完成后锁定横屏
    this.$nextTick(() => {
      setTimeout(() => {
        this.lockLandscape()
      }, 500)
    })
  },

  onUnload() {
    // 页面卸载时恢复自动旋转
    screenManager.unlockOrientation()
  },

  methods: {
    // 检查是否支持横屏
    checkLandscapeSupport() {
      this.isLandscapeSupported = screenManager.isLandscapeSupported()
    },

    // 获取屏幕信息
    getScreenInfo() {
      screenManager.getScreenInfo((info) => {
        this.screenWidth = info.width
        this.screenHeight = info.height
        this.orientation = info.orientation
        this.isLandscape = info.orientation === 'landscape'
      })
    },

    // 锁定横屏
    lockLandscape() {
      screenManager.lockLandscape()
      this.isLandscape = true
      this.orientation = 'landscape'
    },

    // 锁定竖屏
    lockPortrait() {
      screenManager.lockPortrait()
      this.isLandscape = false
      this.orientation = 'portrait'
    },

    // 切换方向
    toggleOrientation() {
      if (this.isLandscape) {
        this.lockPortrait()
      } else {
        this.lockLandscape()
      }
    },

    // 方向变化回调
    onOrientationChange(orientation) {
      this.orientation = orientation
      this.isLandscape = orientation === 'landscape'

      // 更新屏幕尺寸
      this.getScreenInfo()

      // 触发自定义事件
      this.$emit('orientation-change', orientation)
    },

    // 游戏操作
    handleAction(action) {
      uni.showToast({
        title: `执行: ${action}`,
        icon: 'none'
      })

      // 根据横竖屏执行不同操作
      if (this.isLandscape) {
        this.handleLandscapeAction(action)
      } else {
        this.handlePortraitAction(action)
      }
    },

    handleLandscapeAction(action) {
      console.log('横屏操作:', action)
    },

    handlePortraitAction(action) {
      console.log('竖屏操作:', action)
    }
  }
}
</script>

<style scoped>
.game-container {
  width: 100vw;
  height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  flex-direction: column;
  padding: 40rpx;
  box-sizing: border-box;
}

.game-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx 0;
  border-bottom: 2rpx solid rgba(255, 255, 255, 0.2);
  margin-bottom: 40rpx;
}

.game-title {
  font-size: 48rpx;
  font-weight: bold;
  color: white;
  text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.3);
}

.orientation-btn {
  background-color: rgba(255, 255, 255, 0.2);
  color: white;
  border: 2rpx solid white;
  border-radius: 40rpx;
  padding: 16rpx 32rpx;
  font-size: 28rpx;
  backdrop-filter: blur(10rpx);
}

.orientation-btn:active {
  background-color: rgba(255, 255, 255, 0.3);
}

.game-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.game-board {
  width: 80%;
  height: 300rpx;
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 20rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  backdrop-filter: blur(20rpx);
  border: 2rpx solid rgba(255, 255, 255, 0.2);
  margin-bottom: 60rpx;
}

.game-text {
  font-size: 36rpx;
  color: white;
  font-weight: bold;
}

.game-controls {
  display: flex;
  justify-content: center;
  gap: 40rpx;
  width: 100%;
}

.control-btn {
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.2);
  color: white;
  font-size: 32rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 2rpx solid white;
}

.control-btn:active {
  background-color: rgba(255, 255, 255, 0.3);
  transform: scale(0.95);
}

.screen-info {
  position: absolute;
  bottom: 40rpx;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10rpx;
}

.screen-info text {
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.8);
}

/* 横屏专用样式 */
@media (orientation: landscape) {
  .game-container {
    padding: 20rpx 60rpx;
  }

  .game-board {
    height: 400rpx;
    width: 90%;
  }

  .game-controls {
    justify-content: space-around;
    padding: 0 100rpx;
  }

  .control-btn {
    width: 150rpx;
    height: 150rpx;
    font-size: 40rpx;
  }
}

/* 竖屏专用样式 */
@media (orientation: portrait) {
  .game-board {
    height: 200rpx;
  }

  .game-controls {
    gap: 20rpx;
  }
}
</style>

5. 项目配置

manifest.json 配置:

{
  "app-plus": {
    "screenOrientation": [
      "portrait-primary",
      "landscape-primary",
      "portrait-secondary",
      "landscape-secondary"
    ],
    "modules": {
      "Orientation": {}
    }
  },
  "mp-weixin": {
    "setting": {
      "urlCheck": false,
      "es6": true,
      "postcss": true,
      "minified": true
    },
    "requiredPrivateInfos": ["getSystemInfo"],
    "permission": {
      "scope.writePhotosAlbum": {
        "desc": "需要保存截图到相册"
      }
    }
  }
}

注意事项

微信小程序限制

  • 需要基础库 2.4.0+ 支持 wx.setPageOrientation
  • 需要在 app.json 中配置 "pageOrientation": "auto"
  • 部分 API 在小程序中可能需要用户授权

App 端注意事项

  • iOS 可能需要配置 Info.plist 的 Supported interface orientations
  • Android 需要在 manifest.xml 中配置 activity 的 screenOrientation

兼容性处理

  • 始终检查 API 是否可用
  • 提供 fallback 方案(如显示旋转提示)
  • 测试不同设备和系统版本

用户体验

  • 提供清晰的横屏提示
  • 考虑横竖屏布局差异
  • 保存用户的方向偏好设置

这个实现方案提供了完整的横屏支持,包括自动检测、手动切换、方向监听等功能,并且在小程序和 App 端都能正常工作。

相关帖子
Vue + Android WebView实现大文件PDF预览完整解决方案
Vue + Android WebView实现大文件PDF预览完整解决方案
未来几年,学区划片的政策导向可能会有哪些趋势值得提前了解?
未来几年,学区划片的政策导向可能会有哪些趋势值得提前了解?
配偶名下购房,另一方能否提取自己的公积金账户余额?
配偶名下购房,另一方能否提取自己的公积金账户余额?
针垫花的传粉奇观:蜜腺位置如何吸引特定鸟类完成授粉
针垫花的传粉奇观:蜜腺位置如何吸引特定鸟类完成授粉
花菜的储存小妙招:这样做能让花菜保持鲜嫩很久
花菜的储存小妙招:这样做能让花菜保持鲜嫩很久
白银市网站开发设计#erp系统开发,一站式服务
白银市网站开发设计#erp系统开发,一站式服务
在异地提交了换证申请后,因故需要取消或修改信息该如何操作?
在异地提交了换证申请后,因故需要取消或修改信息该如何操作?
山南市网站优化公司#精准获客助手,模板建站
山南市网站优化公司#精准获客助手,模板建站
农业遥感如何帮助农民了解作物长势并预估产量,2026年有哪些新应用?
农业遥感如何帮助农民了解作物长势并预估产量,2026年有哪些新应用?
在选择医保定点药店时,我们应该重点考虑和对比哪些因素?
在选择医保定点药店时,我们应该重点考虑和对比哪些因素?
为什么说智能水表和电表的数据,正在成为评估个人信用的潜在依据?
为什么说智能水表和电表的数据,正在成为评估个人信用的潜在依据?
南充市安卓系统app开发@品牌网站定制开发,专业建站
南充市安卓系统app开发@品牌网站定制开发,专业建站
恋爱期间共同出资买房但未结婚,房产的归属问题应该如何妥善处理?
恋爱期间共同出资买房但未结婚,房产的归属问题应该如何妥善处理?
如果未来想买下政府持有的产权份额,具体的评估和购买流程是怎样的?
如果未来想买下政府持有的产权份额,具体的评估和购买流程是怎样的?
在2026年,面对越来越逼真的AI生成内容,我们该如何保持警惕?
在2026年,面对越来越逼真的AI生成内容,我们该如何保持警惕?
员工主动辞职时,当年未休的年休假工资应该如何正确计算和支付?
员工主动辞职时,当年未休的年休假工资应该如何正确计算和支付?
2026年想将家里的高额宽带套餐降级,运营商却设置重重障碍该怎么办?
2026年想将家里的高额宽带套餐降级,运营商却设置重重障碍该怎么办?
汉中市正规殡葬公司|丧葬服务公司,搭设灵堂
汉中市正规殡葬公司|丧葬服务公司,搭设灵堂
在停工停课期间,企业是否有权安排员工使用带薪年假或其他假期?
在停工停课期间,企业是否有权安排员工使用带薪年假或其他假期?
延安市网站建设推广服务#外贸网站建设,高端网站开发设计
延安市网站建设推广服务#外贸网站建设,高端网站开发设计