From c7f334ed535db892bccc1a4e21ea3bf659014bd7 Mon Sep 17 00:00:00 2001 From: zhaichao Date: Sun, 23 Aug 2026 17:15:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B2=E6=8A=96=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/adaptive.js | 70 ++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/frontend/src/utils/adaptive.js b/frontend/src/utils/adaptive.js index 7ef8576d6..7beb35232 100644 --- a/frontend/src/utils/adaptive.js +++ b/frontend/src/utils/adaptive.js @@ -51,26 +51,33 @@ export function initAdaptive(designWidth = DEFAULT_DESIGN_WIDTH) { const useZoom = isZoomSupported() - // 首次:渲染前同步应用一次,避免首帧闪烁 - apply() - window.addEventListener('resize', onResize) - // 从后台标签切回来时,若期间窗口尺寸变化(因 hidden 被跳过)需补算一次 - document.addEventListener('visibilitychange', onVisibilityChange) + // rAF 帧对齐节流:把同一帧内多次 resize 合并为一次 + let rafId = null + // 真实写入节流 + 尾随兜底:拖拽/设备模拟期间 resize 高频触发, + // 若每帧都写 html.zoom 会强制整页重排+重绘导致卡顿,因此限制真实写入频率 + let lastAppliedScale = -1 + let lastWriteAt = 0 + let trailingTimer = null + const WRITE_MIN_INTERVAL = 100 // ms - /** - * 真正的缩放计算与写入。 - * 比例基于 window.innerWidth(窗宽),它不受页面自身 zoom 缩放影响, - * 可避免因滚动条宽度变化造成的“缩放-比例”联动回摆。 - */ - function apply() { + function computeScale() { + const root = document.documentElement + const width = window.innerWidth || (root && root.clientWidth) || designWidth + const ratio = width / designWidth + // 收敛到 [MIN_SCALE, MAX_SCALE],规避极端宽高的异常比例 + return Math.min(MAX_SCALE, Math.max(MIN_SCALE, ratio)) + } + + function writeScale(scale) { const root = document.documentElement if (!root) { return } - const width = window.innerWidth || root.clientWidth || designWidth - const ratio = width / designWidth - // 收敛到 [MIN_SCALE, MAX_SCALE],规避极端宽高的异常比例 - const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, ratio)) + // 值未变化:跳过,避免冗余的样式失效/整页重排 + if (Math.abs(scale - lastAppliedScale) < 1e-4) { + return + } + lastAppliedScale = scale if (scale === 1) { clearScale(root) @@ -105,8 +112,11 @@ export function initAdaptive(designWidth = DEFAULT_DESIGN_WIDTH) { } } - // rAF 帧对齐节流:把同一帧内多次 resize 合并为一次应用,实时且顺滑 - let rafId = null + /** 立即计算并写入一次(带值变化守卫) */ + function applyNow() { + lastWriteAt = Date.now() + writeScale(computeScale()) + } function onResize() { if (document.hidden) { @@ -117,15 +127,35 @@ export function initAdaptive(designWidth = DEFAULT_DESIGN_WIDTH) { } rafId = window.requestAnimationFrame(() => { rafId = null - if (!document.hidden) { - apply() + if (document.hidden) { + return } + const now = Date.now() + if (trailingTimer) { + clearTimeout(trailingTimer) + } + // 距上次真实写入已超过阈值才立即写入,其余情况靠尾随兜底,避免满帧整页重排 + if (now - lastWriteAt >= WRITE_MIN_INTERVAL) { + applyNow() + } + // 尾随:拖拽/改变尺寸停下后,确保落到最终窗口尺寸 + trailingTimer = setTimeout(applyNow, WRITE_MIN_INTERVAL) }) } function onVisibilityChange() { if (!document.hidden) { - apply() + // 从后台切回时补算一次 + if (trailingTimer) { + clearTimeout(trailingTimer) + } + applyNow() } } + + // 首次:渲染前同步应用一次,避免首帧闪烁 + applyNow() + window.addEventListener('resize', onResize) + // 从后台标签切回来时,若期间窗口尺寸变化(因 hidden 被跳过)需补算一次 + document.addEventListener('visibilitychange', onVisibilityChange) } \ No newline at end of file