diff --git a/src/app/lib/render-gate.ts b/src/app/lib/render-gate.ts new file mode 100644 index 0000000..b145586 --- /dev/null +++ b/src/app/lib/render-gate.ts @@ -0,0 +1,39 @@ +// src/app/lib/render-gate.ts +// Chỉ cho phép vòng lặp vẽ (rAF/setAnimationLoop) chạy khi phần tử nằm trong vùng nhìn +// VÀ tab đang hiển thị. Gọi onChange(active) mỗi khi trạng thái đổi. Trả về hàm dọn dẹp. +export function createRenderGate( + element: Element, + onChange: (active: boolean) => void, +): () => void { + let onScreen = false; + let visible = typeof document === "undefined" ? true : !document.hidden; + let last = false; + + const emit = () => { + const next = onScreen && visible; + if (next !== last) { + last = next; + onChange(next); + } + }; + + const io = new IntersectionObserver( + (entries) => { + onScreen = entries.some((entry) => entry.isIntersecting); + emit(); + }, + { threshold: 0.01 }, + ); + io.observe(element); + + const onVisibility = () => { + visible = !document.hidden; + emit(); + }; + document.addEventListener("visibilitychange", onVisibility); + + return () => { + io.disconnect(); + document.removeEventListener("visibilitychange", onVisibility); + }; +}