diff --git a/src/App.css b/src/App.css index e138139..e6f1579 100644 --- a/src/App.css +++ b/src/App.css @@ -30,7 +30,7 @@ ul, li { .App-header { background-color: #282c34; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); display: flex; flex-direction: column; align-items: center; @@ -102,7 +102,7 @@ body { .layout { display: flex; flex-direction: column; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); } .layout-header { @@ -120,7 +120,7 @@ body { .normal-p p { font-weight: 400; - font-size: 18px; + font-size: 1.125rem; color: #222222; line-height: 34px; margin-bottom: 20px; diff --git a/src/App.tsx b/src/App.tsx index 84c7d24..99029bb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,12 +8,15 @@ import { useStore } from "@/store"; import { parsePageConfig } from "@/utils/parsePageConfig"; import type { I18nData, SupportLocale } from "@/type"; import { destroyLenis, initLenis } from "@/utils/lenis"; +import useZoomCompensation from "@/hooks/useZoomCompensation"; // function AppRoutes() { // return useRoutes(routes); // } function App() { + useZoomCompensation(); + const [init, setInit] = useState(false); const locale = useStore((s) => s.locale); const appConfig = useStore((s) => s.appConfig); diff --git a/src/components/Banner.module.css b/src/components/Banner.module.css index c9806e4..ad1be89 100644 --- a/src/components/Banner.module.css +++ b/src/components/Banner.module.css @@ -1,7 +1,7 @@ /* Hero */ .hero { position: relative; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; padding-bottom: 3.125rem; diff --git a/src/components/ScreenOpen/index.module.css b/src/components/ScreenOpen/index.module.css index 7c7ccfc..688ce9e 100644 --- a/src/components/ScreenOpen/index.module.css +++ b/src/components/ScreenOpen/index.module.css @@ -1,6 +1,6 @@ .screenOpen { width: 100%; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); position: fixed; top: 0; left: 0; @@ -21,7 +21,7 @@ .screenOpenBackground { width: 100%; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); position: absolute; top: 0; left: 0; diff --git a/src/components/layout/BottomTabsSection/index.module.css b/src/components/layout/BottomTabsSection/index.module.css index 596402d..0fa5539 100644 --- a/src/components/layout/BottomTabsSection/index.module.css +++ b/src/components/layout/BottomTabsSection/index.module.css @@ -3,7 +3,7 @@ overflow: hidden; width: 100%; /* height: 67.5rem; */ - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); padding: 6.25rem 0; background-size: cover; background-position: center; diff --git a/src/components/layout/JobPage/index.module.css b/src/components/layout/JobPage/index.module.css index f6b63b4..febaeae 100644 --- a/src/components/layout/JobPage/index.module.css +++ b/src/components/layout/JobPage/index.module.css @@ -1,6 +1,6 @@ .jobPage { width: 100%; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); padding: 7.5rem 6.25rem; .jobPageHeaderLine { diff --git a/src/components/layout/RowAccordion/index.module.css b/src/components/layout/RowAccordion/index.module.css index c093f7b..f422344 100644 --- a/src/components/layout/RowAccordion/index.module.css +++ b/src/components/layout/RowAccordion/index.module.css @@ -6,7 +6,7 @@ overflow-anchor: none; /* padding: 0 16.25rem; */ width: 100%; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); } .rowAccordionBgContainer { position: absolute; diff --git a/src/components/layout/SwiperCardSection/index.module.css b/src/components/layout/SwiperCardSection/index.module.css index 4fb1afb..4c8dad8 100644 --- a/src/components/layout/SwiperCardSection/index.module.css +++ b/src/components/layout/SwiperCardSection/index.module.css @@ -1,5 +1,5 @@ .swiperCardSection { - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); padding: 6.25rem 16.25rem; background: #D8D8D8; } diff --git a/src/components/layout/TopTabsSection/index.module.css b/src/components/layout/TopTabsSection/index.module.css index ac0b717..1717f20 100644 --- a/src/components/layout/TopTabsSection/index.module.css +++ b/src/components/layout/TopTabsSection/index.module.css @@ -1,7 +1,7 @@ .topTabsSection { padding: 6.25rem 16.25rem 9.375rem 16.25rem; width: 100%; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; background-repeat: repeat; diff --git a/src/hooks/useZoomCompensation.ts b/src/hooks/useZoomCompensation.ts new file mode 100644 index 0000000..1f52862 --- /dev/null +++ b/src/hooks/useZoomCompensation.ts @@ -0,0 +1,54 @@ +import { useEffect } from 'react'; + +const INITIAL_DPR = window.devicePixelRatio || 1; + +/** + * 监听浏览器缩放(Ctrl+/Ctrl-),通过 CSS zoom 反向补偿, + * 使页面视觉比例始终保持与初始加载时一致。 + * + * 原理:浏览器缩放会改变 window.devicePixelRatio, + * 据此计算补偿系数 1/(currentDPR/initialDPR) 设置到 html 元素上。 + */ +export default function useZoomCompensation() { + useEffect(() => { + let cancelled = false; + + function applyCompensation() { + const currentDPR = window.devicePixelRatio || 1; + const zoomFactor = currentDPR / INITIAL_DPR; + const html = document.documentElement; + const htmlStyle = html.style as CSSStyleDeclaration & { zoom: string }; + const compensation = 1 / zoomFactor; + if (Math.abs(zoomFactor - 1) > 0.01) { + htmlStyle.zoom = `${compensation}`; + html.style.setProperty('--zoom-compensation', `${compensation}`); + } else { + htmlStyle.zoom = ''; + html.style.setProperty('--zoom-compensation', '1'); + } + } + + function watchDPR() { + const mq = window.matchMedia( + `(resolution: ${window.devicePixelRatio}dppx)`, + ); + mq.addEventListener( + 'change', + () => { + if (cancelled) return; + applyCompensation(); + watchDPR(); + }, + { once: true }, + ); + } + + applyCompensation(); + watchDPR(); + + return () => { + cancelled = true; + (document.documentElement.style as CSSStyleDeclaration & { zoom: string }).zoom = ''; + }; + }, []); +} diff --git a/src/index.css b/src/index.css index 996a954..9afc74d 100644 --- a/src/index.css +++ b/src/index.css @@ -2,6 +2,7 @@ :root { --design-rem-base: 16px; --ui-scale: 1; + --zoom-compensation: 1; } html { diff --git a/src/pages/About/About.module.css b/src/pages/About/About.module.css index 95b965e..dfbab10 100644 --- a/src/pages/About/About.module.css +++ b/src/pages/About/About.module.css @@ -1,7 +1,7 @@ /* Section block - full-width with background */ .section { position: relative; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; display: flex; diff --git a/src/pages/About/Founder.module.css b/src/pages/About/Founder.module.css index 9219e3b..293935d 100644 --- a/src/pages/About/Founder.module.css +++ b/src/pages/About/Founder.module.css @@ -1,7 +1,7 @@ .section { background: rgba(255, 255, 255, 0.6); border-radius: 0; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); width: 100%; padding: 6.25rem 16.25rem; display: flex; @@ -109,7 +109,7 @@ .sectionFounder { padding: 6.25rem 0 9.375rem; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); } .founderIntroduction h2 { diff --git a/src/pages/About/History.module.css b/src/pages/About/History.module.css index a539ddd..7e2cd56 100644 --- a/src/pages/About/History.module.css +++ b/src/pages/About/History.module.css @@ -7,7 +7,7 @@ rgba(200, 200, 205, 0.3) .125rem, rgba(200, 200, 205, 0.3) .25rem ); - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); width: 100%; padding: 6.25rem auto; padding-bottom: 9.375rem; diff --git a/src/pages/Business/CommercialGroup.module.css b/src/pages/Business/CommercialGroup.module.css index a1b7bce..69aea49 100644 --- a/src/pages/Business/CommercialGroup.module.css +++ b/src/pages/Business/CommercialGroup.module.css @@ -134,7 +134,7 @@ /* Features Section */ .featuresHero { width: 100%; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; background-repeat: no-repeat; @@ -223,7 +223,7 @@ } .propertyServices { - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); color: #fff; padding: 0 16.25rem; } diff --git a/src/pages/Business/InvestGroup.module.css b/src/pages/Business/InvestGroup.module.css index 9c13175..c52832a 100644 --- a/src/pages/Business/InvestGroup.module.css +++ b/src/pages/Business/InvestGroup.module.css @@ -83,7 +83,7 @@ .industryFoster { width: 100%; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); display: flex; align-items: center; position: relative; diff --git a/src/pages/Home/Home.module.css b/src/pages/Home/Home.module.css index 49cb9b7..324a326 100644 --- a/src/pages/Home/Home.module.css +++ b/src/pages/Home/Home.module.css @@ -1,7 +1,7 @@ /* Hero */ .hero { position: relative; - min-height: 100vh; + min-height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; } @@ -43,7 +43,7 @@ /* Commercial */ .commercial { position: relative; - height: 100vh; + height: calc(100vh / var(--zoom-compensation, 1)); background-size: cover; background-position: center; display: flex;