mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 00:36:41 +08:00
refactor: optimize useAvailableHeight to improve resize event handling and performance
This commit is contained in:
@@ -4,12 +4,10 @@ import { usePWA } from '@/composables/usePWA'
|
|||||||
/**
|
/**
|
||||||
* 计算页面内容的可用高度,自动适配 iOS 安全区域和底部 Dock 栏。
|
* 计算页面内容的可用高度,自动适配 iOS 安全区域和底部 Dock 栏。
|
||||||
*
|
*
|
||||||
* 在 appMode(PWA 小屏)下,底部 Dock(Footer)通过 Teleport 挂载到 body,
|
* 通过 DOM 测量获取布局的实际 padding(含 safe-area-inset-top/bottom),
|
||||||
* 始终可见并悬浮在底部。本 composable 会测量 Dock 的实际 DOM 高度(已包含
|
* 以及 Footer Dock 的实际高度,确保在任何设备上都不会被 Dock 遮挡。
|
||||||
* safe-area-inset-bottom),从而自适应不同 iOS 设备的安全区域。
|
|
||||||
*
|
*
|
||||||
* 计算公式: viewport - navbarHeight - layoutPadding - footerDock - componentOffset
|
* 计算公式: viewport - layoutPaddingTop - layoutPaddingBottom - footerDock - componentOffset
|
||||||
* 其中 componentOffset 是调用方指定的组件内部额外占用空间(如工具栏、分页栏等)
|
|
||||||
*
|
*
|
||||||
* @param componentOffset - 组件内部额外占用的空间(工具栏、分页栏等,默认 64)
|
* @param componentOffset - 组件内部额外占用的空间(工具栏、分页栏等,默认 64)
|
||||||
* @param minHeight - 最小高度(默认 300)
|
* @param minHeight - 最小高度(默认 300)
|
||||||
@@ -20,18 +18,26 @@ export function useAvailableHeight(
|
|||||||
) {
|
) {
|
||||||
const { appMode } = usePWA()
|
const { appMode } = usePWA()
|
||||||
|
|
||||||
// 响应式的视口高度,监听 resize 事件
|
// 响应式测量值
|
||||||
const viewportHeight = ref(window.innerHeight || document.documentElement.clientHeight)
|
const viewportHeight = ref(window.innerHeight || document.documentElement.clientHeight)
|
||||||
|
const layoutPaddingTop = ref(72)
|
||||||
// Footer Dock 测量高度(响应式)
|
const layoutPaddingBottom = ref(24)
|
||||||
const footerDockMeasuredHeight = ref(0)
|
const footerDockMeasuredHeight = ref(0)
|
||||||
|
|
||||||
function updateMeasurements() {
|
function updateMeasurements() {
|
||||||
viewportHeight.value = window.innerHeight || document.documentElement.clientHeight
|
viewportHeight.value = window.innerHeight || document.documentElement.clientHeight
|
||||||
|
|
||||||
|
// 测量 .layout-page-content 的实际 padding(含 env(safe-area-inset-top) 等)
|
||||||
|
const layoutEl = document.querySelector('.layout-page-content') as HTMLElement | null
|
||||||
|
if (layoutEl) {
|
||||||
|
const style = getComputedStyle(layoutEl)
|
||||||
|
layoutPaddingTop.value = parseFloat(style.paddingTop) || 72
|
||||||
|
layoutPaddingBottom.value = parseFloat(style.paddingBottom) || 24
|
||||||
|
}
|
||||||
|
|
||||||
// 测量 Footer Dock 的实际高度
|
// 测量 Footer Dock 的实际高度
|
||||||
// footer-nav-container 的 CSS 中已包含 env(safe-area-inset-bottom),
|
// .footer-nav-container 是 position:fixed, padding-block-end 含 env(safe-area-inset-bottom)
|
||||||
// 所以 offsetHeight 是包含安全区域的完整高度
|
// offsetHeight 是元素自身的渲染高度(含 padding),即 Dock 遮挡的区域大小
|
||||||
if (appMode.value) {
|
if (appMode.value) {
|
||||||
const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
|
const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
|
||||||
footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 70
|
footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 70
|
||||||
@@ -41,11 +47,10 @@ export function useAvailableHeight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始测量
|
// 初始测量(nextTick 确保 DOM 已渲染)
|
||||||
updateMeasurements()
|
nextTick(updateMeasurements)
|
||||||
|
|
||||||
window.addEventListener('resize', updateMeasurements)
|
window.addEventListener('resize', updateMeasurements)
|
||||||
// iOS 虚拟键盘弹出/收起时 visualViewport 会变化
|
|
||||||
if (window.visualViewport) {
|
if (window.visualViewport) {
|
||||||
window.visualViewport.addEventListener('resize', updateMeasurements)
|
window.visualViewport.addEventListener('resize', updateMeasurements)
|
||||||
}
|
}
|
||||||
@@ -61,16 +66,16 @@ export function useAvailableHeight(
|
|||||||
const availableHeight = computed(() => {
|
const availableHeight = computed(() => {
|
||||||
const vh = viewportHeight.value
|
const vh = viewportHeight.value
|
||||||
|
|
||||||
// navbar 固定高度(对应 layout-page-content 的 padding-block-start: 4.5rem)
|
// 布局顶部 padding(含 safe-area-inset-top + navbar 高度)
|
||||||
const navbarHeight = 72
|
const topPadding = layoutPaddingTop.value
|
||||||
|
|
||||||
// layout-page-content 的 padding-block-end (1.5rem = 24px)
|
// 布局底部 padding
|
||||||
const layoutBottomPadding = 24
|
const bottomPadding = layoutPaddingBottom.value
|
||||||
|
|
||||||
// 底部 Dock 栏高度(appMode 下通过 DOM 测量,已含 safe-area-inset-bottom)
|
// 底部 Dock 栏遮挡高度(appMode 下通过 DOM 测量,含 safe-area-inset-bottom)
|
||||||
const footerDockHeight = footerDockMeasuredHeight.value
|
const footerDockHeight = footerDockMeasuredHeight.value
|
||||||
|
|
||||||
const available = vh - navbarHeight - layoutBottomPadding - footerDockHeight - componentOffset
|
const available = vh - topPadding - bottomPadding - footerDockHeight - componentOffset
|
||||||
|
|
||||||
return Math.max(available, minHeight)
|
return Math.max(available, minHeight)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user