Refactoring: move out init, scene, utils
This commit is contained in:
parent
ce4ae9e43e
commit
f2057836eb
@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Retro TV Player</title>
|
<title>Retro TV Player</title>
|
||||||
<!-- Load Tailwind CSS for styling --><script src="./vendor/tailwind-3.4.17.js" x-src="https://cdn.tailwindcss.com"></script>
|
<!-- Load Tailwind CSS for styling --><script src="./vendor/tailwind-3.4.17.js" x-src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
<!-- Load Three.js for 3D rendering --><script src="./vendor/three.min.js" x-src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
<!-- Load Three.js for 3D rendering --><script src="./vendor/three.min.js" x-src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||||
<script src="./src/tailwind-config.js"></script>
|
<script src="./src/tailwind-config.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@ -34,6 +35,7 @@
|
|||||||
<!-- Hidden Video Element --><video id="video" playsinline muted class="hidden"></video>
|
<!-- Hidden Video Element --><video id="video" playsinline muted class="hidden"></video>
|
||||||
|
|
||||||
<!-- Controls for loading video --><div id="controls" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20 flex flex-col items-center space-y-2">
|
<!-- Controls for loading video --><div id="controls" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20 flex flex-col items-center space-y-2">
|
||||||
|
|
||||||
<!-- Hidden File Input that will be triggered by the button --><input type="file" id="fileInput" accept="video/mp4" class="hidden" multiple>
|
<!-- Hidden File Input that will be triggered by the button --><input type="file" id="fileInput" accept="video/mp4" class="hidden" multiple>
|
||||||
|
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
@ -44,36 +46,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="./src/global-variables.js"></script>
|
<script src="./src/global-variables.js"></script>
|
||||||
|
<script src="./src/utils.js"></script>
|
||||||
|
<script src="./src/scene.js"></script>
|
||||||
|
<script src="./src/init.js"></script>
|
||||||
|
|
||||||
<!-- 3D Canvas will be injected here by Three.js --><script>
|
<!-- 3D Canvas will be injected here by Three.js -->
|
||||||
// --- Utility: Random Color (seeded) ---
|
<script>
|
||||||
function getRandomColor() {
|
|
||||||
const hue = seededRandom();
|
|
||||||
const saturation = 0.6 + seededRandom() * 0.4;
|
|
||||||
const lightness = 0.3 + seededRandom() * 0.4;
|
|
||||||
return new THREE.Color().setHSL(hue, saturation, lightness).getHex();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts degrees to radians.
|
|
||||||
* @param {number} degrees
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
function degToRad(degrees) {
|
|
||||||
return degrees * (Math.PI / 180);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Seedable Random Number Generator (Mulberry32) ---
|
|
||||||
function seededRandom() {
|
|
||||||
let t = seed += 0x6D2B79F5;
|
|
||||||
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
||||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
||||||
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Initialization ---
|
// --- Initialization ---
|
||||||
function init() {
|
function init() {
|
||||||
// 1. Scene Setup (Dark, Ambient)
|
// 1. Scene Setup (Dark, Ambient)
|
||||||
|
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
scene.background = new THREE.Color(0x000000);
|
scene.background = new THREE.Color(0x000000);
|
||||||
|
|
||||||
@ -82,6 +64,7 @@
|
|||||||
camera = new THREE.PerspectiveCamera(FOV, window.innerWidth / window.innerHeight, 0.1, 1000);
|
camera = new THREE.PerspectiveCamera(FOV, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||||
camera.position.set(0, 1.5, 4);
|
camera.position.set(0, 1.5, 4);
|
||||||
|
|
||||||
|
|
||||||
// 3. Renderer Setup
|
// 3. Renderer Setup
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
@ -90,6 +73,7 @@
|
|||||||
renderer.shadowMap.enabled = true;
|
renderer.shadowMap.enabled = true;
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
|
||||||
|
|
||||||
|
|
||||||
container.appendChild(renderer.domElement);
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
// 4. Lighting (Minimal and focused)
|
// 4. Lighting (Minimal and focused)
|
||||||
@ -100,15 +84,18 @@
|
|||||||
roomLight.position.set(0, 1.8, 0);
|
roomLight.position.set(0, 1.8, 0);
|
||||||
scene.add(roomLight);
|
scene.add(roomLight);
|
||||||
|
|
||||||
|
|
||||||
// 5. Build the entire scene with TV and surrounding objects
|
// 5. Build the entire scene with TV and surrounding objects
|
||||||
createSceneObjects();
|
createSceneObjects();
|
||||||
|
|
||||||
// 6. Create the Dust Particle System
|
// 6. Create the Dust Particle System
|
||||||
createDust();
|
createDust();
|
||||||
|
|
||||||
|
|
||||||
// 7. Create the Room Walls and Ceiling
|
// 7. Create the Room Walls and Ceiling
|
||||||
createRoomWalls();
|
createRoomWalls();
|
||||||
|
|
||||||
|
|
||||||
// --- 8. Debug Visualization Helpers ---
|
// --- 8. Debug Visualization Helpers ---
|
||||||
// Visual aids for the light source positions
|
// Visual aids for the light source positions
|
||||||
if (debugLight && THREE.PointLightHelper) {
|
if (debugLight && THREE.PointLightHelper) {
|
||||||
@ -120,6 +107,7 @@
|
|||||||
scene.add(lampHelperPoint);
|
scene.add(lampHelperPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 9. Event Listeners
|
// 9. Event Listeners
|
||||||
window.addEventListener('resize', onWindowResize, false);
|
window.addEventListener('resize', onWindowResize, false);
|
||||||
fileInput.addEventListener('change', loadVideoFile);
|
fileInput.addEventListener('change', loadVideoFile);
|
||||||
@ -136,100 +124,6 @@
|
|||||||
animate();
|
animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Room Walls Function ---
|
|
||||||
function createRoomWalls() {
|
|
||||||
const wallTexture = loader.load('./textures/wall.jpg');
|
|
||||||
wallTexture.wrapS = THREE.RepeatWrapping;
|
|
||||||
wallTexture.wrapT = THREE.RepeatWrapping;
|
|
||||||
|
|
||||||
// USING MeshPhongMaterial for specular highlights on walls
|
|
||||||
const wallMaterial = new THREE.MeshPhongMaterial({
|
|
||||||
map: wallTexture,
|
|
||||||
side: THREE.FrontSide,
|
|
||||||
shininess: 5,
|
|
||||||
specular: 0x111111 // Subtle reflection
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1. Back Wall (behind the TV)
|
|
||||||
const backWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
|
||||||
backWall.position.set(0, roomHeight / 2, -roomSize / 2);
|
|
||||||
backWall.receiveShadow = true;
|
|
||||||
scene.add(backWall);
|
|
||||||
|
|
||||||
// 2. Front Wall (behind the camera)
|
|
||||||
const frontWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
|
||||||
frontWall.position.set(0, roomHeight / 2, roomSize / 2);
|
|
||||||
frontWall.rotation.y = Math.PI;
|
|
||||||
frontWall.receiveShadow = true;
|
|
||||||
scene.add(frontWall);
|
|
||||||
|
|
||||||
// 3. Left Wall
|
|
||||||
const leftWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
|
||||||
leftWall.rotation.y = Math.PI / 2;
|
|
||||||
leftWall.position.set(-roomSize / 2, roomHeight / 2, 0);
|
|
||||||
leftWall.receiveShadow = true;
|
|
||||||
scene.add(leftWall);
|
|
||||||
|
|
||||||
// 4. Right Wall
|
|
||||||
const rightWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
|
||||||
rightWall.rotation.y = -Math.PI / 2;
|
|
||||||
rightWall.position.set(roomSize / 2, roomHeight / 2, 0);
|
|
||||||
rightWall.receiveShadow = true;
|
|
||||||
scene.add(rightWall);
|
|
||||||
|
|
||||||
// 5. Ceiling
|
|
||||||
const ceilingGeometry = new THREE.PlaneGeometry(roomSize, roomSize);
|
|
||||||
const ceilingTexture = wallTexture;
|
|
||||||
ceilingTexture.repeat.set(4, 4);
|
|
||||||
// USING MeshPhongMaterial
|
|
||||||
const ceilingMaterial = new THREE.MeshPhongMaterial({
|
|
||||||
map: ceilingTexture,
|
|
||||||
side: THREE.FrontSide,
|
|
||||||
shininess: 5,
|
|
||||||
specular: 0x111111
|
|
||||||
});
|
|
||||||
|
|
||||||
const ceiling = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
|
|
||||||
ceiling.rotation.x = Math.PI / 2;
|
|
||||||
ceiling.position.set(0, roomHeight, 0);
|
|
||||||
ceiling.receiveShadow = true;
|
|
||||||
scene.add(ceiling);
|
|
||||||
|
|
||||||
// --- 6. Add a Window to the Back Wall ---
|
|
||||||
const windowWidth = 1.5;
|
|
||||||
const windowHeight = 1.2;
|
|
||||||
const windowGeometry = new THREE.PlaneGeometry(windowWidth, windowHeight);
|
|
||||||
|
|
||||||
const nightSkyMaterial = new THREE.MeshBasicMaterial({
|
|
||||||
color: 0x0a1a3a,
|
|
||||||
emissive: 0x0a1a3a,
|
|
||||||
emissiveIntensity: 0.5,
|
|
||||||
side: THREE.FrontSide
|
|
||||||
});
|
|
||||||
const windowPane = new THREE.Mesh(windowGeometry, nightSkyMaterial);
|
|
||||||
|
|
||||||
const windowZ = -roomSize / 2 + 0.001;
|
|
||||||
windowPane.position.set(-3.5, roomHeight * 0.5 + 1.5, windowZ);
|
|
||||||
scene.add(windowPane);
|
|
||||||
|
|
||||||
// USING MeshPhongMaterial for the frame
|
|
||||||
const frameMaterial = new THREE.MeshPhongMaterial({ color: 0x4d3934, shininess: 10 });
|
|
||||||
|
|
||||||
// Horizontal bar
|
|
||||||
const hBarGeometry = new THREE.BoxGeometry(windowWidth + 0.1, 0.05, 0.05);
|
|
||||||
const hBar = new THREE.Mesh(hBarGeometry, frameMaterial);
|
|
||||||
hBar.position.set(windowPane.position.x, windowPane.position.y, windowZ);
|
|
||||||
hBar.castShadow = true;
|
|
||||||
scene.add(hBar);
|
|
||||||
|
|
||||||
// Vertical bar
|
|
||||||
const vBarGeometry = new THREE.BoxGeometry(0.05, windowHeight + 0.1, 0.05);
|
|
||||||
const vBar = new THREE.Mesh(vBarGeometry, frameMaterial);
|
|
||||||
vBar.position.set(windowPane.position.x, windowPane.position.y, windowZ);
|
|
||||||
vBar.castShadow = true;
|
|
||||||
scene.add(vBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createBookshelf(x, z, rotationY, uniqueSeed) {
|
function createBookshelf(x, z, rotationY, uniqueSeed) {
|
||||||
seed = uniqueSeed; // Reset seed for this specific shelf instance
|
seed = uniqueSeed; // Reset seed for this specific shelf instance
|
||||||
|
|
||||||
@ -1099,17 +993,6 @@
|
|||||||
return vcrGroup;
|
return vcrGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helper function to format seconds into MM:SS ---
|
|
||||||
function formatTime(seconds) {
|
|
||||||
if (isNaN(seconds) || seconds < 0) return '00:00';
|
|
||||||
const totalSeconds = Math.floor(seconds);
|
|
||||||
const minutes = Math.floor(totalSeconds / 60);
|
|
||||||
const remainingSeconds = totalSeconds % 60;
|
|
||||||
const paddedMinutes = String(minutes).padStart(2, '0');
|
|
||||||
const paddedSeconds = String(remainingSeconds).padStart(2, '0');
|
|
||||||
return `${paddedMinutes}:${paddedSeconds}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomFlyTarget() {
|
function randomFlyTarget() {
|
||||||
return new THREE.Vector3(
|
return new THREE.Vector3(
|
||||||
(Math.random() - 0.5) * (ROOM_SIZE - 1),
|
(Math.random() - 0.5) * (ROOM_SIZE - 1),
|
||||||
|
|||||||
64
tv-player/src/init.js
Normal file
64
tv-player/src/init.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// --- 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();
|
||||||
|
}
|
||||||
76
tv-player/src/scene.js
Normal file
76
tv-player/src/scene.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// --- Room Walls Function ---
|
||||||
|
function createRoomWalls() {
|
||||||
|
const wallTexture = loader.load('./textures/wall.jpg');
|
||||||
|
wallTexture.wrapS = THREE.RepeatWrapping;
|
||||||
|
wallTexture.wrapT = THREE.RepeatWrapping;
|
||||||
|
|
||||||
|
// USING MeshPhongMaterial for specular highlights on walls
|
||||||
|
const wallMaterial = new THREE.MeshPhongMaterial({
|
||||||
|
map: wallTexture,
|
||||||
|
side: THREE.FrontSide,
|
||||||
|
shininess: 5,
|
||||||
|
specular: 0x111111 // Subtle reflection
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. Back Wall (behind the TV)
|
||||||
|
const backWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
||||||
|
backWall.position.set(0, roomHeight / 2, -roomSize / 2);
|
||||||
|
backWall.receiveShadow = true;
|
||||||
|
scene.add(backWall);
|
||||||
|
|
||||||
|
// 2. Front Wall (behind the camera)
|
||||||
|
const frontWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
||||||
|
frontWall.position.set(0, roomHeight / 2, roomSize / 2);
|
||||||
|
frontWall.rotation.y = Math.PI;
|
||||||
|
frontWall.receiveShadow = true;
|
||||||
|
scene.add(frontWall);
|
||||||
|
|
||||||
|
// 3. Left Wall
|
||||||
|
const leftWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
||||||
|
leftWall.rotation.y = Math.PI / 2;
|
||||||
|
leftWall.position.set(-roomSize / 2, roomHeight / 2, 0);
|
||||||
|
leftWall.receiveShadow = true;
|
||||||
|
scene.add(leftWall);
|
||||||
|
|
||||||
|
// 4. Right Wall
|
||||||
|
const rightWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
|
||||||
|
rightWall.rotation.y = -Math.PI / 2;
|
||||||
|
rightWall.position.set(roomSize / 2, roomHeight / 2, 0);
|
||||||
|
rightWall.receiveShadow = true;
|
||||||
|
scene.add(rightWall);
|
||||||
|
|
||||||
|
// 5. Ceiling
|
||||||
|
const ceilingGeometry = new THREE.PlaneGeometry(roomSize, roomSize);
|
||||||
|
const ceilingTexture = wallTexture;
|
||||||
|
ceilingTexture.repeat.set(4, 4);
|
||||||
|
// USING MeshPhongMaterial
|
||||||
|
const ceilingMaterial = new THREE.MeshPhongMaterial({
|
||||||
|
map: ceilingTexture,
|
||||||
|
side: THREE.FrontSide,
|
||||||
|
shininess: 5,
|
||||||
|
specular: 0x111111
|
||||||
|
});
|
||||||
|
|
||||||
|
const ceiling = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
|
||||||
|
ceiling.rotation.x = Math.PI / 2;
|
||||||
|
ceiling.position.set(0, roomHeight, 0);
|
||||||
|
ceiling.receiveShadow = true;
|
||||||
|
scene.add(ceiling);
|
||||||
|
|
||||||
|
// --- 6. Add a Window to the Back Wall ---
|
||||||
|
const windowWidth = 1.5;
|
||||||
|
const windowHeight = 1.2;
|
||||||
|
const windowGeometry = new THREE.PlaneGeometry(windowWidth, windowHeight);
|
||||||
|
|
||||||
|
const nightSkyMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: 0x0a1a3a,
|
||||||
|
emissive: 0x0a1a3a,
|
||||||
|
emissiveIntensity: 0.5,
|
||||||
|
side: THREE.FrontSide
|
||||||
|
});
|
||||||
|
const windowPane = new THREE.Mesh(windowGeometry, nightSkyMaterial);
|
||||||
|
|
||||||
|
const windowZ = -roomSize / 2 + 0.001;
|
||||||
|
windowPane.position.set(-3.5, roomHeight * 0.5 + 1.5, windowZ);
|
||||||
|
scene.add(windowPane);
|
||||||
|
}
|
||||||
34
tv-player/src/utils.js
Normal file
34
tv-player/src/utils.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// --- Utility: Random Color (seeded) ---
|
||||||
|
function getRandomColor() {
|
||||||
|
const hue = seededRandom();
|
||||||
|
const saturation = 0.6 + seededRandom() * 0.4;
|
||||||
|
const lightness = 0.3 + seededRandom() * 0.4;
|
||||||
|
return new THREE.Color().setHSL(hue, saturation, lightness).getHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts degrees to radians.
|
||||||
|
* @param {number} degrees
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function degToRad(degrees) {
|
||||||
|
return degrees * (Math.PI / 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Seedable Random Number Generator (Mulberry32) ---
|
||||||
|
function seededRandom() {
|
||||||
|
let t = seed += 0x6D2B79F5;
|
||||||
|
t = Math.imul(t ^ t >>> 15, t | 1);
|
||||||
|
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
||||||
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 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}`;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user