style: 调整pad端横屏竖屏的问题
This commit is contained in:
parent
520811e883
commit
4602ec9e74
22
src/App.vue
22
src/App.vue
@ -18,6 +18,7 @@ const autoFitOptions = {
|
||||
}
|
||||
|
||||
let viewportWidth = document.documentElement.clientWidth
|
||||
let autoFitResetTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const initAutoFit = () => {
|
||||
autofit.init(autoFitOptions, false)
|
||||
@ -29,29 +30,36 @@ const resetAutoFit = () => {
|
||||
viewportWidth = document.documentElement.clientWidth
|
||||
}
|
||||
|
||||
const scheduleAutoFitReset = () => {
|
||||
clearTimeout(autoFitResetTimer)
|
||||
// 等待旋转动画和视口尺寸稳定,避免 resize 期间反复重建 autofit。
|
||||
autoFitResetTimer = setTimeout(resetAutoFit, 50)
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
const nextWidth = document.documentElement.clientWidth
|
||||
|
||||
// 软键盘通常只改变可视高度;宽度不变时保持原缩放比例。
|
||||
if (Math.abs(nextWidth - viewportWidth) <= 2) return
|
||||
|
||||
resetAutoFit()
|
||||
scheduleAutoFitReset()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initAutoFit()
|
||||
window.addEventListener('resize', handleResize)
|
||||
window.addEventListener('orientationchange', resetAutoFit)
|
||||
document.addEventListener('fullscreenchange', resetAutoFit)
|
||||
document.addEventListener('webkitfullscreenchange', resetAutoFit)
|
||||
window.addEventListener('orientationchange', scheduleAutoFitReset)
|
||||
document.addEventListener('fullscreenchange', scheduleAutoFitReset)
|
||||
document.addEventListener('webkitfullscreenchange', scheduleAutoFitReset)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
autofit.off('#app')
|
||||
clearTimeout(autoFitResetTimer)
|
||||
window.removeEventListener('resize', handleResize)
|
||||
window.removeEventListener('orientationchange', resetAutoFit)
|
||||
document.removeEventListener('fullscreenchange', resetAutoFit)
|
||||
document.removeEventListener('webkitfullscreenchange', resetAutoFit)
|
||||
window.removeEventListener('orientationchange', scheduleAutoFitReset)
|
||||
document.removeEventListener('fullscreenchange', scheduleAutoFitReset)
|
||||
document.removeEventListener('webkitfullscreenchange', scheduleAutoFitReset)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -25,6 +25,8 @@ import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
const isPortraitTablet = ref(false)
|
||||
|
||||
let portraitMediaQuery
|
||||
let orientationFrame
|
||||
const orientationTimers = new Set()
|
||||
|
||||
const isTablet = () => {
|
||||
const userAgent = navigator.userAgent
|
||||
@ -40,11 +42,32 @@ const isTablet = () => {
|
||||
}
|
||||
|
||||
const updateOrientation = () => {
|
||||
const isPortrait = portraitMediaQuery?.matches ?? window.innerHeight > window.innerWidth
|
||||
const viewport = window.visualViewport
|
||||
const viewportWidth = viewport?.width || window.innerWidth
|
||||
const viewportHeight = viewport?.height || window.innerHeight
|
||||
const isPortrait = viewportHeight > viewportWidth
|
||||
isPortraitTablet.value = isTablet() && isPortrait
|
||||
document.documentElement.classList.toggle('is-portrait-tablet', isPortraitTablet.value)
|
||||
}
|
||||
|
||||
const scheduleOrientationUpdate = () => {
|
||||
updateOrientation()
|
||||
|
||||
cancelAnimationFrame(orientationFrame)
|
||||
orientationFrame = requestAnimationFrame(updateOrientation)
|
||||
|
||||
orientationTimers.forEach(clearTimeout)
|
||||
orientationTimers.clear()
|
||||
// iPad 旋转动画期间视口尺寸会分阶段更新,短时间复检避免停留在旧方向。
|
||||
for (const delay of [80, 180, 350]) {
|
||||
const timer = setTimeout(() => {
|
||||
orientationTimers.delete(timer)
|
||||
updateOrientation()
|
||||
}, delay)
|
||||
orientationTimers.add(timer)
|
||||
}
|
||||
}
|
||||
|
||||
const tryLockLandscape = async () => {
|
||||
if (!isTablet() || !screen.orientation?.lock) return
|
||||
|
||||
@ -60,17 +83,22 @@ onMounted(() => {
|
||||
updateOrientation()
|
||||
tryLockLandscape()
|
||||
|
||||
portraitMediaQuery.addEventListener?.('change', updateOrientation)
|
||||
window.addEventListener('resize', updateOrientation)
|
||||
window.addEventListener('orientationchange', updateOrientation)
|
||||
portraitMediaQuery.addEventListener?.('change', scheduleOrientationUpdate)
|
||||
window.addEventListener('resize', scheduleOrientationUpdate)
|
||||
window.addEventListener('orientationchange', scheduleOrientationUpdate)
|
||||
window.visualViewport?.addEventListener('resize', scheduleOrientationUpdate)
|
||||
document.addEventListener('fullscreenchange', tryLockLandscape)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.documentElement.classList.remove('is-portrait-tablet')
|
||||
portraitMediaQuery?.removeEventListener?.('change', updateOrientation)
|
||||
window.removeEventListener('resize', updateOrientation)
|
||||
window.removeEventListener('orientationchange', updateOrientation)
|
||||
cancelAnimationFrame(orientationFrame)
|
||||
orientationTimers.forEach(clearTimeout)
|
||||
orientationTimers.clear()
|
||||
portraitMediaQuery?.removeEventListener?.('change', scheduleOrientationUpdate)
|
||||
window.removeEventListener('resize', scheduleOrientationUpdate)
|
||||
window.removeEventListener('orientationchange', scheduleOrientationUpdate)
|
||||
window.visualViewport?.removeEventListener('resize', scheduleOrientationUpdate)
|
||||
document.removeEventListener('fullscreenchange', tryLockLandscape)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -94,6 +94,9 @@ const containerRef = ref(null)
|
||||
const mapCanvasRef = ref(null)
|
||||
let resizeObserver
|
||||
let eventsBound = false
|
||||
let orientationObserver
|
||||
let animationFrameId = null
|
||||
let isRendering = false
|
||||
|
||||
const init = () => {
|
||||
mapCanvas = mapCanvasRef.value;
|
||||
@ -214,6 +217,8 @@ async function loadMapIntoApplication(parsedMap) {
|
||||
* @param {number} timestamp - requestAnimationFrame 提供的时间戳(毫秒)
|
||||
*/
|
||||
function renderFrame(timestamp) {
|
||||
if (!isRendering || !canvasCtx) return;
|
||||
|
||||
// 更新动画时间
|
||||
applicationState.animationTimeSeconds = (timestamp || 0) / 1000;
|
||||
|
||||
@ -223,7 +228,7 @@ function renderFrame(timestamp) {
|
||||
|
||||
// 如果地图未加载,跳过绘制
|
||||
if (!applicationState.isMapLoaded) {
|
||||
requestAnimationFrame(renderFrame); // 继续请求下一帧
|
||||
animationFrameId = requestAnimationFrame(renderFrame); // 继续请求下一帧
|
||||
return;
|
||||
}
|
||||
|
||||
@ -273,7 +278,36 @@ function renderFrame(timestamp) {
|
||||
drawMapBoundary(canvasCtx, map, applicationState);
|
||||
|
||||
// 请求下一帧渲染
|
||||
requestAnimationFrame(renderFrame);
|
||||
animationFrameId = requestAnimationFrame(renderFrame);
|
||||
}
|
||||
|
||||
const stopRendering = () => {
|
||||
isRendering = false
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId)
|
||||
animationFrameId = null
|
||||
}
|
||||
}
|
||||
|
||||
const startRendering = () => {
|
||||
if (isRendering || !applicationState.isMapLoaded || document.documentElement.classList.contains('is-portrait-tablet')) return
|
||||
isRendering = true
|
||||
animationFrameId = requestAnimationFrame(renderFrame)
|
||||
}
|
||||
|
||||
const handleOrientationVisibility = () => {
|
||||
if (document.documentElement.classList.contains('is-portrait-tablet')) {
|
||||
stopRendering()
|
||||
return
|
||||
}
|
||||
|
||||
// v-show 恢复后等待 Vue 和浏览器完成布局,再重设画布并恢复渲染。
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
resizeCanvasToContainer()
|
||||
startRendering()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const drawAllRobots = (ctx) => {
|
||||
@ -495,6 +529,11 @@ onMounted(() => {
|
||||
resizeObserver = new ResizeObserver(resizeCanvasToContainer)
|
||||
resizeObserver.observe(containerRef.value)
|
||||
window.addEventListener('resize', resizeCanvasToContainer)
|
||||
orientationObserver = new MutationObserver(handleOrientationVisibility)
|
||||
orientationObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => props.mapName,
|
||||
@ -502,7 +541,7 @@ watch(() => props.mapName,
|
||||
if (newVal) {
|
||||
await loadSourceMap(newVal)
|
||||
addRobot(robotStore.ip, robotStore.ip, robotStore.position.x, robotStore.position.y, robotStore.position.theta, getThemeColor('--color-canvas-robot'))
|
||||
requestAnimationFrame(renderFrame);
|
||||
startRendering()
|
||||
}
|
||||
}, {
|
||||
immediate: true
|
||||
@ -515,8 +554,10 @@ defineExpose({
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopRendering()
|
||||
window.removeEventListener('resize', resizeCanvasToContainer)
|
||||
resizeObserver?.disconnect()
|
||||
orientationObserver?.disconnect()
|
||||
canvasCtx = null
|
||||
})
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user