feat: 限制平台不能竖屏
This commit is contained in:
parent
312ed063c3
commit
c09f05ac8d
58
src/App.vue
58
src/App.vue
@ -1,12 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<LandscapeGuard>
|
||||||
<router-view />
|
<router-view />
|
||||||
|
</LandscapeGuard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
// 按需引入会自动导入 useRouter / useRoute
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
const router = useRouter()
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
153
src/components/LandscapeGuard.vue
Normal file
153
src/components/LandscapeGuard.vue
Normal 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>
|
||||||
@ -29,6 +29,7 @@
|
|||||||
* {
|
* {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
@ -37,3 +38,21 @@ body,
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 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;
|
||||||
|
}
|
||||||
|
|||||||
@ -144,6 +144,8 @@ const testConnect = async () => {
|
|||||||
|
|
||||||
:deep(.el-input__inner) {
|
:deep(.el-input__inner) {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
// 避免 iPad Safari 聚焦小字号输入框时自动缩放页面。
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connect {
|
.connect {
|
||||||
|
|||||||
@ -36,14 +36,6 @@ import MechanicalArm from './arm/MechanicalArm.vue';
|
|||||||
import Camera from './camera/index.vue'
|
import Camera from './camera/index.vue'
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import autofit from 'autofit.js'
|
|
||||||
autofit.init({
|
|
||||||
designHeight: 1080,
|
|
||||||
designWidth: 1920,
|
|
||||||
renderDom: '#app',
|
|
||||||
resize: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const currentTime = ref('')
|
const currentTime = ref('')
|
||||||
@ -83,25 +75,12 @@ const back = () => {
|
|||||||
router.back()
|
router.back()
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetAutoFit = () => {
|
|
||||||
autofit.destroy() // 销毁旧缩放
|
|
||||||
autofit.init({
|
|
||||||
designHeight: 1080,
|
|
||||||
designWidth: 1920,
|
|
||||||
renderDom: '#app',
|
|
||||||
resize: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let clockTimer, binaryTimer;
|
let clockTimer, binaryTimer;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateClock();
|
updateClock();
|
||||||
clockTimer = setInterval(updateClock, 1000);
|
clockTimer = setInterval(updateClock, 1000);
|
||||||
animateBinary()
|
animateBinary()
|
||||||
binaryTimer = setInterval(animateBinary, 400);
|
binaryTimer = setInterval(animateBinary, 400);
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', resetAutoFit)
|
|
||||||
document.addEventListener('webkitfullscreenchange', resetAutoFit)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user