Skip to content

Commit ae539c6

Browse files
committed
fix(webui): prevent canvas jitter when mobile keyboard appears
1 parent 758f571 commit ae539c6

File tree

1 file changed

+36
-7
lines changed

1 file changed

+36
-7
lines changed

src/webui/FE/components/common/AnimatedBackground.tsx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const AnimatedBackground: React.FC = () => {
1818
const ballsRef = useRef<Ball[]>([]);
1919
const animationRef = useRef<number>();
2020
const lastTimeRef = useRef<number>(0);
21+
// 记录初始视口大小,避免输入法弹出时重新计算
22+
const initialSizeRef = useRef<{ width: number; height: number } | null>(null);
2123

2224
useEffect(() => {
2325
const canvas = canvasRef.current;
@@ -29,12 +31,31 @@ const AnimatedBackground: React.FC = () => {
2931
const targetFPS = 30;
3032
const frameInterval = 1000 / targetFPS;
3133

34+
// 获取稳定的视口大小(使用 document.documentElement 避免输入法影响)
35+
const getStableSize = () => {
36+
// 优先使用初始大小,避免输入法弹出时的抖动
37+
if (initialSizeRef.current) {
38+
return initialSizeRef.current;
39+
}
40+
// 使用 documentElement 的尺寸,在移动端更稳定
41+
const width = Math.max(window.innerWidth, document.documentElement.clientWidth);
42+
const height = Math.max(window.innerHeight, document.documentElement.clientHeight);
43+
return { width, height };
44+
};
45+
3246
const resizeCanvas = () => {
33-
canvas.width = window.innerWidth;
34-
canvas.height = window.innerHeight;
35-
ballsRef.current.forEach(ball => {
36-
ball.offscreenCanvas = createBallCanvas(ball);
37-
});
47+
const size = getStableSize();
48+
// 只在画布变大时更新,避免输入法弹出时缩小
49+
if (!initialSizeRef.current || size.width > canvas.width || size.height > canvas.height) {
50+
canvas.width = size.width;
51+
canvas.height = size.height;
52+
if (!initialSizeRef.current) {
53+
initialSizeRef.current = size;
54+
}
55+
ballsRef.current.forEach(ball => {
56+
ball.offscreenCanvas = createBallCanvas(ball);
57+
});
58+
}
3859
};
3960

4061
const colors = [
@@ -169,13 +190,21 @@ const AnimatedBackground: React.FC = () => {
169190
});
170191
};
171192

193+
// 防抖处理 resize,避免输入法弹出时频繁触发
194+
let resizeTimeout: ReturnType<typeof setTimeout> | null = null;
195+
const debouncedResize = () => {
196+
if (resizeTimeout) clearTimeout(resizeTimeout);
197+
resizeTimeout = setTimeout(resizeCanvas, 200);
198+
};
199+
172200
resizeCanvas();
173-
window.addEventListener('resize', resizeCanvas);
201+
window.addEventListener('resize', debouncedResize);
174202
initBalls();
175203
animationRef.current = requestAnimationFrame(animate);
176204

177205
return () => {
178-
window.removeEventListener('resize', resizeCanvas);
206+
window.removeEventListener('resize', debouncedResize);
207+
if (resizeTimeout) clearTimeout(resizeTimeout);
179208
if (animationRef.current) {
180209
cancelAnimationFrame(animationRef.current);
181210
}

0 commit comments

Comments
 (0)