防抖优化

This commit is contained in:
2026-08-23 17:15:24 +08:00
parent 088a35c9e8
commit c7f334ed53
+50 -20
View File
@@ -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)
}