Refactoring: move out animate loop

This commit is contained in:
Dejvino 2025-11-13 20:57:33 +01:00
parent 942ab5e0ee
commit 043552f36c
3 changed files with 132 additions and 216 deletions

View File

@ -52,225 +52,10 @@
<script src="./src/effects_dust.js"></script>
<script src="./src/effects_flies.js"></script>
<script src="./src/vcr-display.js"></script>
<script src="./src/animate.js"></script>
<script src="./src/init.js"></script>
<!-- 3D Canvas will be injected here by Three.js -->
<script>
// --- Initialization ---
function init() {
// 1. Scene Setup (Dark, Ambient)
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// 2. Camera Setup
const FOV = 65;
camera = new THREE.PerspectiveCamera(FOV, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.5, 4);
// 3. Renderer Setup
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
// Enable shadows on the renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
container.appendChild(renderer.domElement);
// 4. Lighting (Minimal and focused)
const ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
const roomLight = new THREE.PointLight(0xffaa55, 0.05, roomSize);
roomLight.position.set(0, 1.8, 0);
scene.add(roomLight);
// 5. Build the entire scene with TV and surrounding objects
createSceneObjects();
// 6. Create the Dust Particle System
createDust();
// 7. Create the Room Walls and Ceiling
createRoomWalls();
// --- 8. Debug Visualization Helpers ---
// Visual aids for the light source positions
if (debugLight && THREE.PointLightHelper) {
const screenHelper = new THREE.PointLightHelper(screenLight, 0.1, 0xff0000); // Red for screen
scene.add(screenHelper);
// Lamp Helper will now work since lampLight is added to the scene
const lampHelperPoint = new THREE.PointLightHelper(lampLightPoint, 0.1, 0x00ff00); // Green for lamp
scene.add(lampHelperPoint);
}
// 9. Event Listeners
window.addEventListener('resize', onWindowResize, false);
fileInput.addEventListener('change', loadVideoFile);
// Button logic
loadTapeButton.addEventListener('click', () => {
fileInput.click();
});
// Auto-advance to the next video when the current one finishes.
videoElement.addEventListener('ended', playNextVideo);
// Start the animation loop
animate();
}
// --- Helper function to format seconds into MM:SS ---
function formatTime(seconds) {
if (isNaN(seconds) || seconds === Infinity || seconds < 0) return '--:--';
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
const paddedMinutes = String(minutes).padStart(2, '0');
const paddedSeconds = String(remainingSeconds).padStart(2, '0');
return `${paddedMinutes}:${paddedSeconds}`;
}
// --- Animation Loop ---
function animate() {
requestAnimationFrame(animate);
// 1. Dust animation: slow downward drift
if (dust) {
const positions = dust.geometry.attributes.position.array;
for (let i = 1; i < positions.length; i += 3) {
positions[i] -= 0.001;
if (positions[i] < -2) {
positions[i] = 8;
}
}
dust.geometry.attributes.position.needsUpdate = true;
}
// 2. Camera movement (Gentle, random hovering)
const globalTime = Date.now() * 0.00005;
const lookAtTime = Date.now() * 0.00003;
const camAmplitude = 0.7;
const lookAmplitude = 0.05;
// Base Camera Position in front of the TV
const baseX = -0.5;
const baseY = 1.5;
const baseZ = 2.5;
// Base LookAt target (Center of the screen)
const baseTargetX = -0.7;
const baseTargetY = 1.7;
const baseTargetZ = -0.3;
// Camera Position Offsets (Drift)
const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude;
const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude * 0.4;
const camOffsetZ = Math.cos(globalTime * 3.2) * camAmplitude * 1.4;
camera.position.x = baseX + camOffsetX;
camera.position.y = baseY + camOffsetY;
camera.position.z = baseZ + camOffsetZ;
// LookAt Target Offsets (Subtle Gaze Shift)
const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude;
const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude;
// Apply lookAt to the subtly shifted target
camera.lookAt(
baseTargetX + lookOffsetX,
baseTargetY + lookOffsetY,
baseTargetZ
);
// 3. Lamp Flicker Effect
const flickerChance = 0.995;
const restoreRate = 0.15;
if (Math.random() > flickerChance) {
// Flickers quickly to a dimmer random value (between 0.3 and 1.05)
let lampLightIntensity = originalLampIntensity * (0.3 + Math.random() * 0.7);
lampLightSpot.intensity = lampLightIntensity;
lampLightPoint.intensity = lampLightIntensity;
} else if (lampLightPoint.intensity < originalLampIntensity) {
// Smoothly restore original intensity
let lampLightIntensity = THREE.MathUtils.lerp(lampLightPoint.intensity, originalLampIntensity, restoreRate);
lampLightSpot.intensity = lampLightIntensity;
lampLightPoint.intensity = lampLightIntensity;
}
// 4. Screen Light Pulse and Movement Effect (Updated)
if (isVideoLoaded && screenLight.intensity > 0) {
// A. Pulse Effect (Intensity Fluctuation)
// Generate a small random fluctuation for the pulse (Range: 1.35 to 1.65 around base 1.5)
const pulseTarget = originalScreenIntensity + (Math.random() - 0.5) * screenIntensityPulse;
// Smoothly interpolate towards the new target fluctuation
screenLight.intensity = THREE.MathUtils.lerp(screenLight.intensity, pulseTarget, 0.1);
// B. Movement Effect (Subtle circle around the screen center - circling the room area)
const lightTime = Date.now() * 0.0001;
const radius = 0.01;
const centerX = 0;
const centerY = 1.5;
//const centerZ = 1.2; // Use the updated Z position of the light source
// Move the light in a subtle, erratic circle
screenLight.position.x = centerX + Math.cos(lightTime) * radius;
screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y
//screenLight.position.z = centerZ; // Keep Z constant at the screen light plane
}
// 5. Update video texture (essential to grab the next frame)
if (videoTexture) {
videoTexture.needsUpdate = true;
// Update time display in the animation loop
if (isVideoLoaded && videoElement.readyState >= 3) {
const currentTime = formatTime(videoElement.currentTime);
const duration = formatTime(videoElement.duration);
console.info(`Tape ${currentVideoIndex + 1} of ${videoUrls.length}. Time: ${currentTime} / ${duration}`);
}
}
updateFlies();
const currentTime = baseTime + videoElement.currentTime;
// Simulate playback time
if (Math.abs(currentTime - lastUpdateTime) > 0.1) {
updateVcrDisplay(currentTime);
lastUpdateTime = currentTime;
}
// Blink the colon every second
if (currentTime - lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds
blinkState = !blinkState;
lastBlinkToggleTime = currentTime;
}
// RENDER!
renderer.render(scene, camera);
}
// --- Window Resize Handler ---
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Start everything on window load
window.onload = init;
</script>
</body>
</html>
<!-- textures sourced from https://animalia-life.club/ -->

128
tv-player/src/animate.js Normal file
View File

@ -0,0 +1,128 @@
// --- Animation Loop ---
function animate() {
requestAnimationFrame(animate);
// 1. Dust animation: slow downward drift
if (dust) {
const positions = dust.geometry.attributes.position.array;
for (let i = 1; i < positions.length; i += 3) {
positions[i] -= 0.001;
if (positions[i] < -2) {
positions[i] = 8;
}
}
dust.geometry.attributes.position.needsUpdate = true;
}
// 2. Camera movement (Gentle, random hovering)
const globalTime = Date.now() * 0.00005;
const lookAtTime = Date.now() * 0.00003;
const camAmplitude = 0.7;
const lookAmplitude = 0.05;
// Base Camera Position in front of the TV
const baseX = -0.5;
const baseY = 1.5;
const baseZ = 2.5;
// Base LookAt target (Center of the screen)
const baseTargetX = -0.7;
const baseTargetY = 1.7;
const baseTargetZ = -0.3;
// Camera Position Offsets (Drift)
const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude;
const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude * 0.4;
const camOffsetZ = Math.cos(globalTime * 3.2) * camAmplitude * 1.4;
camera.position.x = baseX + camOffsetX;
camera.position.y = baseY + camOffsetY;
camera.position.z = baseZ + camOffsetZ;
// LookAt Target Offsets (Subtle Gaze Shift)
const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude;
const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude;
// Apply lookAt to the subtly shifted target
camera.lookAt(
baseTargetX + lookOffsetX,
baseTargetY + lookOffsetY,
baseTargetZ
);
// 3. Lamp Flicker Effect
const flickerChance = 0.995;
const restoreRate = 0.15;
if (Math.random() > flickerChance) {
// Flickers quickly to a dimmer random value (between 0.3 and 1.05)
let lampLightIntensity = originalLampIntensity * (0.3 + Math.random() * 0.7);
lampLightSpot.intensity = lampLightIntensity;
lampLightPoint.intensity = lampLightIntensity;
} else if (lampLightPoint.intensity < originalLampIntensity) {
// Smoothly restore original intensity
let lampLightIntensity = THREE.MathUtils.lerp(lampLightPoint.intensity, originalLampIntensity, restoreRate);
lampLightSpot.intensity = lampLightIntensity;
lampLightPoint.intensity = lampLightIntensity;
}
// 4. Screen Light Pulse and Movement Effect (Updated)
if (isVideoLoaded && screenLight.intensity > 0) {
// A. Pulse Effect (Intensity Fluctuation)
// Generate a small random fluctuation for the pulse (Range: 1.35 to 1.65 around base 1.5)
const pulseTarget = originalScreenIntensity + (Math.random() - 0.5) * screenIntensityPulse;
// Smoothly interpolate towards the new target fluctuation
screenLight.intensity = THREE.MathUtils.lerp(screenLight.intensity, pulseTarget, 0.1);
// B. Movement Effect (Subtle circle around the screen center - circling the room area)
const lightTime = Date.now() * 0.0001;
const radius = 0.01;
const centerX = 0;
const centerY = 1.5;
//const centerZ = 1.2; // Use the updated Z position of the light source
// Move the light in a subtle, erratic circle
screenLight.position.x = centerX + Math.cos(lightTime) * radius;
screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y
//screenLight.position.z = centerZ; // Keep Z constant at the screen light plane
}
// 5. Update video texture (essential to grab the next frame)
if (videoTexture) {
videoTexture.needsUpdate = true;
// Update time display in the animation loop
if (isVideoLoaded && videoElement.readyState >= 3) {
const currentTime = formatTime(videoElement.currentTime);
const duration = formatTime(videoElement.duration);
console.info(`Tape ${currentVideoIndex + 1} of ${videoUrls.length}. Time: ${currentTime} / ${duration}`);
}
}
updateFlies();
const currentTime = baseTime + videoElement.currentTime;
// Simulate playback time
if (Math.abs(currentTime - lastUpdateTime) > 0.1) {
updateVcrDisplay(currentTime);
lastUpdateTime = currentTime;
}
// Blink the colon every second
if (currentTime - lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds
blinkState = !blinkState;
lastBlinkToggleTime = currentTime;
}
// RENDER!
renderer.render(scene, camera);
}
// --- Window Resize Handler ---
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

View File

@ -62,3 +62,6 @@ function init() {
// Start the animation loop
animate();
}
// Start everything on window load
window.onload = init;