* 等待 main 容器的 scrollHeight 稳定(连续 N 帧不变),表示 React 渲染 + layout 完成; * 然后回调。避免在元素 *出现* 但页面 *还在长高* 时计算错位置。 * * 兜底:超过 maxFrames 仍未稳定 → 仍调 callback 但通过 onTimeout 通知调用方 * (让 debug 模式能 warn"layout 未稳定但已尝试滚动",方便排查异步图片 * 加载导致的位置漂移)。
( probeEl: HTMLElement, callback: () => void, onTimeout?: () => void, )
| 144 | * 加载导致的位置漂移)。 |
| 145 | */ |
| 146 | function whenLayoutStable( |
| 147 | probeEl: HTMLElement, |
| 148 | callback: () => void, |
| 149 | onTimeout?: () => void, |
| 150 | ) { |
| 151 | const ancestor = findScrollableAncestor(probeEl); |
| 152 | const measure = () => |
| 153 | ancestor instanceof Window |
| 154 | ? document.documentElement.scrollHeight |
| 155 | : ancestor.scrollHeight; |
| 156 | |
| 157 | let last = -1; |
| 158 | let stableFrames = 0; |
| 159 | let totalFrames = 0; |
| 160 | const maxFrames = 40; // 约 650ms 兜底 |
| 161 | |
| 162 | const tick = () => { |
| 163 | totalFrames += 1; |
| 164 | if (totalFrames > maxFrames) { |
| 165 | onTimeout?.(); |
| 166 | callback(); |
| 167 | return; |
| 168 | } |
| 169 | const h = measure(); |
| 170 | if (h === last) { |
| 171 | stableFrames += 1; |
| 172 | if (stableFrames >= 3) { |
| 173 | callback(); |
| 174 | return; |
| 175 | } |
| 176 | } else { |
| 177 | last = h; |
| 178 | stableFrames = 0; |
| 179 | } |
| 180 | requestAnimationFrame(tick); |
| 181 | }; |
| 182 | requestAnimationFrame(tick); |
| 183 | } |
| 184 | |
| 185 | // 调试开关:把 ?hashdebug=1 加到 URL 上启用 |
| 186 | function isDebug(): boolean { |
no test coverage detected