perf: render theo nhu cầu + throttle cameraYaw (giảm tải khi đứng yên)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
tuyenio
2026-06-24 16:07:17 +07:00
parent 3ffa0d9455
commit 336e3ddb63
+31 -7
View File
@@ -958,6 +958,8 @@ function TourExperience({
const transitionLockRef = useRef(false); const transitionLockRef = useRef(false);
const currentFovRef = useRef(DEFAULT_FOV); const currentFovRef = useRef(DEFAULT_FOV);
const targetFovRef = useRef(DEFAULT_FOV); const targetFovRef = useRef(DEFAULT_FOV);
const needsRenderRef = useRef(true);
const lastYawRef = useRef(0);
const lastPinchDistanceRef = useRef<number | null>(null); const lastPinchDistanceRef = useRef<number | null>(null);
const infoMarkerRefs = useRef<Map<string, HTMLButtonElement>>(new Map()); const infoMarkerRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
const [activeInfoMarker, setActiveInfoMarker] = useState<InfoMarker | null>(null); const [activeInfoMarker, setActiveInfoMarker] = useState<InfoMarker | null>(null);
@@ -1428,6 +1430,11 @@ function TourExperience({
} }
}; };
const handleControlsChange = () => {
needsRenderRef.current = true;
};
controls.addEventListener("change", handleControlsChange);
renderer.domElement.addEventListener("wheel", handleWheel, { passive: false }); renderer.domElement.addEventListener("wheel", handleWheel, { passive: false });
renderer.domElement.addEventListener("touchstart", handleTouchStart, { passive: false }); renderer.domElement.addEventListener("touchstart", handleTouchStart, { passive: false });
renderer.domElement.addEventListener("touchmove", handleTouchMove, { passive: false }); renderer.domElement.addEventListener("touchmove", handleTouchMove, { passive: false });
@@ -1455,8 +1462,9 @@ function TourExperience({
const animate = () => { const animate = () => {
const targetFov = targetFovRef.current; const targetFov = targetFovRef.current;
const fovAnimating = Math.abs(currentFovRef.current - targetFov) > 0.01;
if (Math.abs(currentFovRef.current - targetFov) > 0.01) { if (fovAnimating) {
currentFovRef.current += (targetFov - currentFovRef.current) * 0.16; currentFovRef.current += (targetFov - currentFovRef.current) * 0.16;
camera.fov = currentFovRef.current; camera.fov = currentFovRef.current;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
@@ -1464,20 +1472,35 @@ function TourExperience({
currentFovRef.current = targetFov; currentFovRef.current = targetFov;
camera.fov = targetFov; camera.fov = targetFov;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
needsRenderRef.current = true;
} }
if (!vrModeRef.current) { // controls.update() trả về true khi camera vừa thay đổi (kể cả damping đang lắng).
controls.update(); const controlsChanged = vrModeRef.current ? false : controls.update();
} const transitionActive = transitionFrameRef.current !== null;
const active =
needsRenderRef.current ||
fovAnimating ||
controlsChanged ||
controls.autoRotate ||
vrModeRef.current ||
transitionActive;
// Update camera yaw for mini-map rotation if (!active) {
return;
}
needsRenderRef.current = false;
// Cập nhật yaw cho mini-map — chỉ setState khi đổi đủ lớn để tránh re-render thừa.
const direction = new THREE.Vector3(); const direction = new THREE.Vector3();
camera.getWorldDirection(direction); camera.getWorldDirection(direction);
const yaw = yawFromDirection(direction); const yaw = yawFromDirection(direction);
setCameraYaw(yaw); if (Math.abs(normalizeYaw(yaw - lastYawRef.current)) > 0.15) {
lastYawRef.current = yaw;
setCameraYaw(yaw);
}
updateHotspots(); updateHotspots();
renderer.render(threeScene, camera); renderer.render(threeScene, camera);
}; };
@@ -1495,6 +1518,7 @@ function TourExperience({
renderer.domElement.removeEventListener("pointermove", handlePointerMove); renderer.domElement.removeEventListener("pointermove", handlePointerMove);
renderer.domElement.removeEventListener("pointerup", handlePointerEnd); renderer.domElement.removeEventListener("pointerup", handlePointerEnd);
renderer.domElement.removeEventListener("pointercancel", handlePointerEnd); renderer.domElement.removeEventListener("pointercancel", handlePointerEnd);
controls.removeEventListener("change", handleControlsChange);
renderer.setAnimationLoop(null); renderer.setAnimationLoop(null);
controls.dispose(); controls.dispose();
geometry.dispose(); geometry.dispose();