From 76c7ac47c52b07f4d8ee24c29042d8b980ed5a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=BA=9A=E7=90=AA?= Date: Tue, 21 Jan 2025 17:41:38 +0800 Subject: [PATCH] =?UTF-8?q?wip(uni-app-x=20web):=20=E6=94=AF=E6=8C=81safeA?= =?UTF-8?q?reaInset=20css=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/components/page/pageBody.tsx | 41 +++++++++++++++- packages/uni-h5/src/helpers/safeArea.ts | 39 +++++++++++++++ packages/uni-h5/src/x/framework/setup/page.ts | 47 ++++--------------- 3 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 packages/uni-h5/src/helpers/safeArea.ts diff --git a/packages/uni-h5/src/framework/components/page/pageBody.tsx b/packages/uni-h5/src/framework/components/page/pageBody.tsx index 17eee4c998c..9d607500cc2 100644 --- a/packages/uni-h5/src/framework/components/page/pageBody.tsx +++ b/packages/uni-h5/src/framework/components/page/pageBody.tsx @@ -1,4 +1,4 @@ -import { type Ref, ref, renderSlot, watch } from 'vue' +import { type Ref, onMounted, onUnmounted, ref, renderSlot, watch } from 'vue' import { defineSystemComponent } from '@dcloudio/uni-components' @@ -7,6 +7,7 @@ import { usePageMeta } from '../../setup/provide' import PageRefresh from './page-refresh/component.vue' import { usePageRefresh } from './page-refresh' +import { getSafeAreaInsets } from '../../../helpers/safeArea' export default /*#__PURE__*/ defineSystemComponent({ name: 'PageBody', @@ -17,6 +18,8 @@ export default /*#__PURE__*/ defineSystemComponent({ const refreshRef = (__UNI_FEATURE_PULL_DOWN_REFRESH__ && ref(null)) as Ref + const wrapperRef = ref(null) + const _pageRefresh = !__NODE_JS__ && __UNI_FEATURE_PULL_DOWN_REFRESH__ && @@ -36,6 +39,40 @@ export default /*#__PURE__*/ defineSystemComponent({ } ) + if (__X__ && !__NODE_JS__) { + // TODO 兼容低版本浏览器 + let observer: ResizeObserver | null = null + + onMounted(() => { + if (typeof ResizeObserver === 'undefined') { + return + } + observer = new ResizeObserver((entries) => { + const { top, left, right, bottom } = getSafeAreaInsets( + wrapperRef.value! + ) + // TODO dialogPage + const vars = { + '--uni-safe-area-inset-top': `${top}px`, + '--uni-safe-area-inset-left': `${left}px`, + '--uni-safe-area-inset-right': `${right}px`, + '--uni-safe-area-inset-bottom': `${bottom}px`, + } + for (const key in vars) { + wrapperRef.value!.style.setProperty(key, vars[key]) + } + }) + observer.observe(document.querySelector('uni-page-wrapper')!) + }) + + onUnmounted(() => { + if (!observer) { + return + } + observer.disconnect() + }) + } + return () => { const pageRefreshTsx = __UNI_FEATURE_PULL_DOWN_REFRESH__ && @@ -43,7 +80,7 @@ export default /*#__PURE__*/ defineSystemComponent({ return ( <> {pageRefreshTsx} - + {renderSlot(ctx.slots, 'default')} diff --git a/packages/uni-h5/src/helpers/safeArea.ts b/packages/uni-h5/src/helpers/safeArea.ts new file mode 100644 index 00000000000..914a163ed4c --- /dev/null +++ b/packages/uni-h5/src/helpers/safeArea.ts @@ -0,0 +1,39 @@ +import safeAreaInsets from 'safe-area-insets' +export function getPageWrapperInfo(pageBody?: HTMLElement) { + const pageWrapper = + pageBody || (document.querySelector('uni-page-wrapper') as HTMLElement) + const pageWrapperRect = pageWrapper.getBoundingClientRect() + + const bodyRect = document.body.getBoundingClientRect() + return { + top: pageWrapperRect.top, + left: pageWrapperRect.left, + right: bodyRect.right - pageWrapperRect.right, + bottom: bodyRect.bottom - pageWrapperRect.bottom, + width: pageWrapperRect.width, + height: pageWrapperRect.height, + } +} + +export const getSystemSafeAreaInsets = function () { + return { + top: safeAreaInsets.top, + right: safeAreaInsets.right, + bottom: safeAreaInsets.bottom, + left: safeAreaInsets.left, + } +} + +/** + * 注意web端页面安全区域较为特殊,如下四个值主要为满足fixed定位时能避开系统安全区域及页面top-window、left-window、nav-bar、tab-bar等的边界 + */ +export function getSafeAreaInsets(pageBody?: HTMLElement) { + const pageWrapperEdge = getPageWrapperInfo(pageBody) + const systemSafeAreaInsets = getSystemSafeAreaInsets() + return { + top: Math.max(pageWrapperEdge.top, systemSafeAreaInsets.top), + left: Math.max(pageWrapperEdge.left, systemSafeAreaInsets.left), + right: Math.max(pageWrapperEdge.right, systemSafeAreaInsets.right), + bottom: Math.max(pageWrapperEdge.bottom, systemSafeAreaInsets.bottom), + } +} diff --git a/packages/uni-h5/src/x/framework/setup/page.ts b/packages/uni-h5/src/x/framework/setup/page.ts index d797e51e921..9c800bb63ef 100644 --- a/packages/uni-h5/src/x/framework/setup/page.ts +++ b/packages/uni-h5/src/x/framework/setup/page.ts @@ -13,7 +13,10 @@ import type { } from '@dcloudio/uni-app-x/types/page' //#if !_NODE_JS_ import { closeDialogPage } from '../../service/api/route/closeDialogPage' -import safeAreaInsets from 'safe-area-insets' +import { + getPageWrapperInfo, + getSafeAreaInsets, +} from '../../../helpers/safeArea' //#endif import { currentPagesMap, @@ -24,17 +27,6 @@ import { isDialogPageInstance } from '../helpers/utils' import type { UniSafeAreaInsets } from '@dcloudio/uni-app-x/types/native/UniSafeAreaInsets' import type { UniPageBody } from '@dcloudio/uni-app-x/types/UniPage' -//#if !_NODE_JS_ -const getSystemSafeAreaInsets = function () { - return { - top: safeAreaInsets.top, - right: safeAreaInsets.right, - bottom: safeAreaInsets.bottom, - left: safeAreaInsets.left, - } -} -//#endif - let escBackPageNum = 0 type PageStyle = { navigationBarBackgroundColor?: string @@ -50,21 +42,6 @@ type PageStyle = { export const homeDialogPages: UniDialogPage[] = [] export const homeSystemDialogPages: UniDialogPage[] = [] -function getPageWrapperInfo(container: Document | Element) { - const pageWrapper = container.querySelector('uni-page-wrapper') as HTMLElement - const pageWrapperRect = pageWrapper.getBoundingClientRect() - - const bodyRect = document.body.getBoundingClientRect() - return { - top: pageWrapperRect.top, - left: pageWrapperRect.left, - right: bodyRect.right - pageWrapperRect.right, - bottom: bodyRect.bottom - pageWrapperRect.bottom, - width: pageWrapperRect.width, - height: pageWrapperRect.height, - } -} - function isDialogPageImpl(page: UniPage): boolean { return page instanceof UniDialogPageImpl } @@ -91,7 +68,8 @@ class UniPageImpl implements UniPage { } else if (this !== currentPage) { throw new Error("Can't get pageBody of other page") } - const pageWrapperInfo = getPageWrapperInfo(container) + const pageBody = container.querySelector('uni-page-wrapper') as HTMLElement + const pageWrapperInfo = getPageWrapperInfo(pageBody) return { top: pageWrapperInfo.top, left: pageWrapperInfo.left, @@ -118,17 +96,8 @@ class UniPageImpl implements UniPage { } else if (this !== currentPage) { throw new Error("Can't get safeAreaInsets of other page") } - const pageWrapperEdge = getPageWrapperInfo(container) - const systemSafeAreaInsets = getSystemSafeAreaInsets() - /** - * 注意web端页面安全区域较为特殊,如下四个值主要为满足fixed定位时能避开系统安全区域及页面top-window、left-window、nav-bar、tab-bar等的边界 - */ - return { - top: Math.max(pageWrapperEdge.top, systemSafeAreaInsets.top), - left: Math.max(pageWrapperEdge.left, systemSafeAreaInsets.left), - right: Math.max(pageWrapperEdge.right, systemSafeAreaInsets.right), - bottom: Math.max(pageWrapperEdge.bottom, systemSafeAreaInsets.bottom), - } + const pageBody = container.querySelector('uni-page-wrapper') as HTMLElement + return getSafeAreaInsets(pageBody) } getPageStyle(): UTSJSONObject { const pageMeta = this.vm?.$basePage.meta