fix: pause animation loop while not rendering

This commit is contained in:
2026-01-21 20:42:56 +01:00
parent 5d0d285971
commit 2d660e05a0

View File

@@ -192,6 +192,7 @@ let mesh = null;
let directionalLight = null; let directionalLight = null;
let ambientLight = null; let ambientLight = null;
let animationId = null; let animationId = null;
let animationPaused = false;
// Geometry cache for height exaggeration // Geometry cache for height exaggeration
let geometryCache = null; let geometryCache = null;
@@ -233,8 +234,19 @@ const initThreeJS = () => {
updateLightPosition(); updateLightPosition();
// Animation loop // Don't start animation loop yet - will be controlled by pause/resume
};
// Animation loop control
const startAnimation = () => {
if (animationId) return; // Already running
const animate = () => { const animate = () => {
if (animationPaused) {
animationId = null;
return;
}
animationId = requestAnimationFrame(animate); animationId = requestAnimationFrame(animate);
if (renderer && scene && camera) { if (renderer && scene && camera) {
renderer.render(scene, camera); renderer.render(scene, camera);
@@ -243,6 +255,26 @@ const initThreeJS = () => {
animate(); animate();
}; };
const pauseAnimation = () => {
animationPaused = true;
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
};
const resumeAnimation = () => {
animationPaused = false;
startAnimation();
};
// Render a single frame (for when animation is paused but we need to update the view)
const renderSingleFrame = () => {
if (renderer && scene && camera) {
renderer.render(scene, camera);
}
};
// Handle canvas resize // Handle canvas resize
const handleResize = () => { const handleResize = () => {
if (!canvasRef.value || !renderer || !camera) return; if (!canvasRef.value || !renderer || !camera) return;
@@ -422,11 +454,13 @@ const updateHeightScale = () => {
geometryCache.geometry.attributes.position.needsUpdate = true; geometryCache.geometry.attributes.position.needsUpdate = true;
geometryCache.geometry.computeVertexNormals(); geometryCache.geometry.computeVertexNormals();
renderSingleFrame(); // Render once to show the change
}; };
// Update scene (for real-time preview) // Update scene (for real-time preview)
const updateScene = () => { const updateScene = () => {
updateLightPosition(); updateLightPosition();
renderSingleFrame(); // Render once to show the change
}; };
// Color conversion utilities // Color conversion utilities
@@ -438,6 +472,7 @@ const updateColor = (e) => {
settings.terrainColor = parseInt(e.target.value.replace('#', ''), 16); settings.terrainColor = parseInt(e.target.value.replace('#', ''), 16);
if (mesh && mesh.material) { if (mesh && mesh.material) {
mesh.material.color.setHex(settings.terrainColor); mesh.material.color.setHex(settings.terrainColor);
renderSingleFrame(); // Render once to show the change
} }
}; };
@@ -634,9 +669,7 @@ const downloadLastRender = () => {
// Cleanup // Cleanup
const cleanup = () => { const cleanup = () => {
if (animationId) { pauseAnimation();
cancelAnimationFrame(animationId);
}
if (resizeObserver) { if (resizeObserver) {
resizeObserver.disconnect(); resizeObserver.disconnect();
resizeObserver = null; resizeObserver = null;
@@ -663,6 +696,11 @@ onMounted(() => {
}); });
resizeObserver.observe(canvasRef.value); resizeObserver.observe(canvasRef.value);
} }
// Only start animation if visible and not offscreen
if (!props.offscreen) {
resumeAnimation();
}
}); });
} }
}); });
@@ -680,7 +718,29 @@ watch(() => props.visible, (newVal) => {
}); });
resizeObserver.observe(canvasRef.value); resizeObserver.observe(canvasRef.value);
} }
// Start animation when visible and not offscreen
if (!props.offscreen) {
resumeAnimation();
}
}); });
} else if (newVal && !props.offscreen) {
// Component already initialized, just resume animation
resumeAnimation();
} else if (!newVal) {
// Hidden - pause animation
pauseAnimation();
}
});
// Watch for offscreen changes
watch(() => props.offscreen, (newVal) => {
if (newVal) {
// Offscreen mode - pause animation (we only render single frames on demand)
pauseAnimation();
} else if (props.visible) {
// Not offscreen and visible - resume animation
resumeAnimation();
} }
}); });
@@ -721,8 +781,7 @@ defineExpose({
} }
.modal-container.offscreen { .modal-container.offscreen {
left: -10000px; display: none;
top: -10000px;
} }
.modal-header { .modal-header {