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" // 固定横屏
}
}
}
]
}
创建 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()
创建 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>
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>
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": "需要保存截图到相册"
}
}
}
}
微信小程序限制:
wx.setPageOrientationapp.json 中配置 "pageOrientation": "auto"App 端注意事项:
兼容性处理:
用户体验:
这个实现方案提供了完整的横屏支持,包括自动检测、手动切换、方向监听等功能,并且在小程序和 App 端都能正常工作。