feat: 限制平台不能竖屏

This commit is contained in:
zhanghao 2026-07-23 10:22:51 +08:00
parent 312ed063c3
commit c09f05ac8d
5 changed files with 227 additions and 28 deletions

View File

@ -1,12 +1,58 @@
<template>
<router-view />
<LandscapeGuard>
<router-view />
</LandscapeGuard>
</template>
<script setup>
// useRouter / useRoute
const router = useRouter()
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import LandscapeGuard from '@/components/LandscapeGuard.vue'
import autofit from 'autofit.js'
const autoFitOptions = {
dh: 1080,
dw: 1920,
el: '#app',
// resize
resize: false
}
let viewportWidth = document.documentElement.clientWidth
const initAutoFit = () => {
autofit.init(autoFitOptions, false)
}
const resetAutoFit = () => {
autofit.off('#app')
initAutoFit()
viewportWidth = document.documentElement.clientWidth
}
const handleResize = () => {
const nextWidth = document.documentElement.clientWidth
//
if (Math.abs(nextWidth - viewportWidth) <= 2) return
resetAutoFit()
}
onMounted(() => {
initAutoFit()
window.addEventListener('resize', handleResize)
window.addEventListener('orientationchange', resetAutoFit)
document.addEventListener('fullscreenchange', resetAutoFit)
document.addEventListener('webkitfullscreenchange', resetAutoFit)
})
onUnmounted(() => {
autofit.off('#app')
window.removeEventListener('resize', handleResize)
window.removeEventListener('orientationchange', resetAutoFit)
document.removeEventListener('fullscreenchange', resetAutoFit)
document.removeEventListener('webkitfullscreenchange', resetAutoFit)
})
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,153 @@
<template>
<div v-show="!isPortraitTablet" class="landscape-guard__content">
<slot />
</div>
<Teleport to="body">
<div
v-if="isPortraitTablet"
class="landscape-guard__mask"
role="alert"
aria-live="assertive"
>
<div class="landscape-guard__icon" aria-hidden="true">
<span class="landscape-guard__device"></span>
<span class="landscape-guard__arrow"></span>
</div>
<p>请将平板横屏使用</p>
</div>
</Teleport>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'
const isPortraitTablet = ref(false)
let portraitMediaQuery
const isTablet = () => {
const userAgent = navigator.userAgent
const isIPad = /iPad/i.test(userAgent)
|| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
const isAndroidTablet = /Android/i.test(userAgent) && !/Mobile/i.test(userAgent)
const shortestScreenSide = Math.min(screen.width, screen.height)
const isTouchTabletSize = navigator.maxTouchPoints > 1
&& shortestScreenSide >= 600
&& shortestScreenSide <= 1366
return isIPad || isAndroidTablet || isTouchTabletSize
}
const updateOrientation = () => {
const isPortrait = portraitMediaQuery?.matches ?? window.innerHeight > window.innerWidth
isPortraitTablet.value = isTablet() && isPortrait
document.documentElement.classList.toggle('is-portrait-tablet', isPortraitTablet.value)
}
const tryLockLandscape = async () => {
if (!isTablet() || !screen.orientation?.lock) return
try {
await screen.orientation.lock('landscape')
} catch {
//
}
}
onMounted(() => {
portraitMediaQuery = window.matchMedia('(orientation: portrait)')
updateOrientation()
tryLockLandscape()
portraitMediaQuery.addEventListener?.('change', updateOrientation)
window.addEventListener('resize', updateOrientation)
window.addEventListener('orientationchange', updateOrientation)
document.addEventListener('fullscreenchange', tryLockLandscape)
})
onBeforeUnmount(() => {
document.documentElement.classList.remove('is-portrait-tablet')
portraitMediaQuery?.removeEventListener?.('change', updateOrientation)
window.removeEventListener('resize', updateOrientation)
window.removeEventListener('orientationchange', updateOrientation)
document.removeEventListener('fullscreenchange', tryLockLandscape)
})
</script>
<style scoped lang="scss">
.landscape-guard__content {
width: 100%;
height: 100%;
}
.landscape-guard__mask {
position: fixed;
inset: -2px;
z-index: 2147483647;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
box-sizing: border-box;
width: auto;
height: auto;
min-width: calc(100vw + 4px);
min-height: calc(100vh + 4px);
min-height: calc(100dvh + 4px);
padding: calc(32px + env(safe-area-inset-top))
calc(32px + env(safe-area-inset-right))
calc(32px + env(safe-area-inset-bottom))
calc(32px + env(safe-area-inset-left));
overflow: hidden;
color: #fff;
background: #0b1526;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: clamp(24px, 4vw, 42px);
font-weight: 600;
text-align: center;
}
.landscape-guard__icon {
position: relative;
width: 150px;
height: 120px;
}
.landscape-guard__device {
position: absolute;
top: 11px;
left: 46px;
box-sizing: border-box;
width: 58px;
height: 98px;
border: 6px solid currentColor;
border-radius: 10px;
}
.landscape-guard__device::after {
position: absolute;
bottom: 4px;
left: 50%;
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
content: '';
transform: translateX(-50%);
}
.landscape-guard__arrow {
position: absolute;
right: 0;
bottom: 0;
color: #58b7ff;
font-size: 68px;
line-height: 1;
}
.landscape-guard__mask p {
margin: 0;
}
</style>

View File

@ -29,6 +29,7 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
@ -37,3 +38,21 @@ body,
width: 100%;
height: 100%;
}
body {
overflow: hidden;
}
html.is-portrait-tablet,
html.is-portrait-tablet body {
overflow: hidden !important;
background: #0b1526;
}
html.is-portrait-tablet #app {
visibility: hidden;
}
html.is-portrait-tablet .landscape-guard__mask {
visibility: visible;
}

View File

@ -144,6 +144,8 @@ const testConnect = async () => {
:deep(.el-input__inner) {
height: 40px;
// iPad Safari
font-size: 16px;
}
.connect {

View File

@ -36,14 +36,6 @@ import MechanicalArm from './arm/MechanicalArm.vue';
import Camera from './camera/index.vue'
import { useRouter } from 'vue-router';
import autofit from 'autofit.js'
autofit.init({
designHeight: 1080,
designWidth: 1920,
renderDom: '#app',
resize: true
})
const router = useRouter()
const currentTime = ref('')
@ -83,25 +75,12 @@ const back = () => {
router.back()
}
const resetAutoFit = () => {
autofit.destroy() //
autofit.init({
designHeight: 1080,
designWidth: 1920,
renderDom: '#app',
resize: true
})
}
let clockTimer, binaryTimer;
onMounted(() => {
updateClock();
clockTimer = setInterval(updateClock, 1000);
animateBinary()
binaryTimer = setInterval(animateBinary, 400);
document.addEventListener('fullscreenchange', resetAutoFit)
document.addEventListener('webkitfullscreenchange', resetAutoFit)
})
onUnmounted(() => {