Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Système Solaire 3D avec Comète</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
background-color: #000; | |
font-family: Arial, sans-serif; | |
} | |
canvas { | |
display: block; | |
} | |
.info-panel { | |
position: absolute; | |
bottom: 20px; | |
left: 20px; | |
color: white; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 10px 15px; | |
border-radius: 8px; | |
font-size: 14px; | |
max-width: 300px; | |
backdrop-filter: blur(5px); | |
} | |
.controls { | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
color: white; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 15px; | |
border-radius: 8px; | |
backdrop-filter: blur(5px); | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
} | |
.planet-label { | |
position: absolute; | |
color: white; | |
font-size: 12px; | |
pointer-events: none; | |
text-shadow: 0 0 3px black; | |
white-space: nowrap; | |
transform: translate(-50%, -50%); | |
} | |
.loading { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: #000; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
color: white; | |
font-size: 24px; | |
z-index: 1000; | |
} | |
.speed-control { | |
width: 150px; | |
margin: 10px 0; | |
} | |
.planet-info { | |
position: absolute; | |
right: 20px; | |
top: 20px; | |
color: white; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 15px; | |
border-radius: 8px; | |
max-width: 250px; | |
backdrop-filter: blur(5px); | |
display: none; | |
} | |
.comet-label { | |
position: absolute; | |
color: #aaf; | |
font-size: 12px; | |
pointer-events: none; | |
text-shadow: 0 0 3px black; | |
white-space: nowrap; | |
transform: translate(-50%, -50%); | |
} | |
</style> | |
</head> | |
<body> | |
<div class="loading" id="loading"> | |
Chargement du système solaire... | |
</div> | |
<div class="controls"> | |
<h1 class="text-xl font-bold mb-3 text-yellow-300">Système Solaire 3D</h1> | |
<div class="mb-3"> | |
<label class="block mb-1">Vitesse de rotation:</label> | |
<div class="flex items-center"> | |
<input type="range" id="speedControl" min="0" max="5" step="0.1" value="1" class="speed-control mr-3"> | |
<span id="speedValue" class="font-mono">1.0x</span> | |
</div> | |
</div> | |
<div class="flex space-x-2"> | |
<button id="toggleOrbits" class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-3 rounded text-sm transition"> | |
Masquer les orbites | |
</button> | |
<button id="toggleLabels" class="bg-purple-600 hover:bg-purple-700 text-white py-1 px-3 rounded text-sm transition"> | |
Masquer les labels | |
</button> | |
<button id="toggleComet" class="bg-green-600 hover:bg-green-700 text-white py-1 px-3 rounded text-sm transition"> | |
Masquer la comète | |
</button> | |
</div> | |
</div> | |
<div class="info-panel"> | |
<div class="flex items-center mb-2"> | |
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path> | |
</svg> | |
<span>Cliquez sur une planète pour plus d'informations</span> | |
</div> | |
<div class="flex items-center"> | |
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"></path> | |
</svg> | |
<span>Utilisez la souris pour naviguer</span> | |
</div> | |
</div> | |
<div class="planet-info" id="planetInfo"> | |
<h2 class="text-lg font-bold mb-2" id="planetName">Planète</h2> | |
<div class="grid grid-cols-2 gap-2 text-sm"> | |
<div>Distance au Soleil:</div> | |
<div class="font-mono" id="planetDistance">0 UA</div> | |
<div>Diamètre:</div> | |
<div class="font-mono" id="planetSize">0 km</div> | |
<div>Période orbitale:</div> | |
<div class="font-mono" id="planetPeriod">0 jours</div> | |
</div> | |
</div> | |
<script> | |
// Vérification que Three.js est chargé | |
if (!THREE) { | |
document.getElementById('loading').innerHTML = 'Erreur: Three.js n\'a pas pu être chargé. Veuillez vérifier votre connexion internet.'; | |
throw new Error('Three.js not loaded'); | |
} | |
// Initialisation de la scène Three.js | |
const scene = new THREE.Scene(); | |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000); | |
const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
renderer.setPixelRatio(window.devicePixelRatio); | |
document.body.appendChild(renderer.domElement); | |
// Ajout d'un fond étoilé | |
const starGeometry = new THREE.BufferGeometry(); | |
const starMaterial = new THREE.PointsMaterial({ | |
color: 0xFFFFFF, | |
size: 0.1, | |
transparent: true | |
}); | |
const starVertices = []; | |
for (let i = 0; i < 5000; i++) { | |
const x = (Math.random() - 0.5) * 2000; | |
const y = (Math.random() - 0.5) * 2000; | |
const z = (Math.random() - 0.5) * 2000; | |
starVertices.push(x, y, z); | |
} | |
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3)); | |
const stars = new THREE.Points(starGeometry, starMaterial); | |
scene.add(stars); | |
// Contrôles de la caméra | |
const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
controls.enableDamping = true; | |
controls.dampingFactor = 0.05; | |
controls.minDistance = 20; | |
controls.maxDistance = 500; | |
// Position initiale de la caméra | |
camera.position.set(0, 50, 150); | |
controls.update(); | |
// Lumière ambiante | |
const ambientLight = new THREE.AmbientLight(0x404040); | |
scene.add(ambientLight); | |
// Lumière directionnelle (simule le soleil) | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5); | |
directionalLight.position.set(0, 0, 0); | |
scene.add(directionalLight); | |
// Effet de halo pour le soleil | |
const sunGlow = new THREE.Mesh( | |
new THREE.SphereGeometry(6, 32, 32), | |
new THREE.MeshBasicMaterial({ | |
color: 0xffaa00, | |
transparent: true, | |
opacity: 0.3 | |
}) | |
); | |
scene.add(sunGlow); | |
// Création du soleil | |
const sunGeometry = new THREE.SphereGeometry(5, 64, 64); | |
const sunMaterial = new THREE.MeshBasicMaterial({ | |
color: 0xffff00, | |
emissive: 0xffff00, | |
emissiveIntensity: 1 | |
}); | |
const sun = new THREE.Mesh(sunGeometry, sunMaterial); | |
scene.add(sun); | |
// Animation du soleil | |
const sunParticles = new THREE.Group(); | |
for (let i = 0; i < 100; i++) { | |
const particle = new THREE.Mesh( | |
new THREE.SphereGeometry(0.1 + Math.random() * 0.3, 8, 8), | |
new THREE.MeshBasicMaterial({ color: 0xff6600 }) | |
); | |
// Position aléatoire autour du soleil | |
const radius = 5 + Math.random() * 3; | |
const theta = Math.random() * Math.PI * 2; | |
const phi = Math.random() * Math.PI * 2; | |
particle.position.x = radius * Math.sin(phi) * Math.cos(theta); | |
particle.position.y = radius * Math.sin(phi) * Math.sin(theta); | |
particle.position.z = radius * Math.cos(phi); | |
sunParticles.add(particle); | |
} | |
scene.add(sunParticles); | |
// Données des planètes (nom, taille, distance, couleur, vitesse, période orbitale) | |
const planetsData = [ | |
{ | |
name: "Mercure", | |
size: 0.4, | |
distance: 15, | |
color: 0x8B8B8B, | |
speed: 0.04, | |
period: 88, | |
info: "La plus petite planète du système solaire et la plus proche du Soleil." | |
}, | |
{ | |
name: "Vénus", | |
size: 0.9, | |
distance: 20, | |
color: 0xE6C229, | |
speed: 0.015, | |
period: 225, | |
info: "Similaire à la Terre en taille mais avec une atmosphère toxique de CO₂." | |
}, | |
{ | |
name: "Terre", | |
size: 1, | |
distance: 30, | |
color: 0x3498db, | |
speed: 0.01, | |
period: 365, | |
info: "Notre planète, la seule connue à abriter la vie." | |
}, | |
{ | |
name: "Mars", | |
size: 0.5, | |
distance: 40, | |
color: 0xE27B58, | |
speed: 0.008, | |
period: 687, | |
info: "Appelée 'Planète Rouge' à cause de son sol riche en oxyde de fer." | |
}, | |
{ | |
name: "Jupiter", | |
size: 2.5, | |
distance: 60, | |
color: 0xE3B78D, | |
speed: 0.002, | |
period: 4333, | |
info: "La plus grande planète, une géante gazeuse avec une grande tache rouge." | |
}, | |
{ | |
name: "Saturne", | |
size: 2, | |
distance: 80, | |
color: 0xF7E39F, | |
speed: 0.0007, | |
hasRing: true, | |
period: 10759, | |
info: "Connue pour ses magnifiques anneaux composés de glace et de poussière." | |
}, | |
{ | |
name: "Uranus", | |
size: 1.5, | |
distance: 95, | |
color: 0x7EC0EE, | |
speed: 0.0005, | |
period: 30687, | |
info: "Géante de glace qui tourne sur le côté, avec des anneaux verticaux." | |
}, | |
{ | |
name: "Neptune", | |
size: 1.5, | |
distance: 110, | |
color: 0x4B70DD, | |
speed: 0.0003, | |
period: 60190, | |
info: "La planète la plus éloignée du Soleil, avec les vents les plus forts." | |
} | |
]; | |
const planets = []; | |
const orbits = []; | |
const labels = []; | |
const planetMeshes = []; | |
// Raycaster pour détecter les clics sur les planètes | |
const raycaster = new THREE.Raycaster(); | |
const mouse = new THREE.Vector2(); | |
// Création des planètes et de leurs orbites | |
planetsData.forEach((data, index) => { | |
// Planète | |
const geometry = new THREE.SphereGeometry(data.size, 32, 32); | |
const material = new THREE.MeshPhongMaterial({ | |
color: data.color, | |
shininess: 10 | |
}); | |
const planet = new THREE.Mesh(geometry, material); | |
// Position initiale sur l'orbite | |
const angle = Math.random() * Math.PI * 2; | |
planet.position.x = data.distance * Math.cos(angle); | |
planet.position.z = data.distance * Math.sin(angle); | |
// Anneaux pour Saturne | |
if (data.hasRing) { | |
const ringGeometry = new THREE.RingGeometry(data.size * 1.3, data.size * 1.7, 64); | |
const ringMaterial = new THREE.MeshPhongMaterial({ | |
color: 0xF7E39F, | |
side: THREE.DoubleSide, | |
transparent: true, | |
opacity: 0.8, | |
shininess: 30 | |
}); | |
const ring = new THREE.Mesh(ringGeometry, ringMaterial); | |
ring.rotation.x = Math.PI / 2; | |
planet.add(ring); | |
} | |
scene.add(planet); | |
planets.push({ | |
mesh: planet, | |
speed: data.speed, | |
distance: data.distance, | |
angle: angle, | |
data: data | |
}); | |
planetMeshes.push(planet); | |
// Orbite | |
const orbitGeometry = new THREE.RingGeometry(data.distance - 0.1, data.distance + 0.1, 128); | |
const orbitMaterial = new THREE.MeshBasicMaterial({ | |
color: 0x555555, | |
side: THREE.DoubleSide, | |
transparent: true, | |
opacity: 0.3 | |
}); | |
const orbit = new THREE.Mesh(orbitGeometry, orbitMaterial); | |
orbit.rotation.x = Math.PI / 2; | |
scene.add(orbit); | |
orbits.push(orbit); | |
// Label | |
const label = document.createElement('div'); | |
label.className = 'planet-label'; | |
label.textContent = data.name; | |
label.style.color = `rgb(${Math.floor(data.color >> 16)}, ${Math.floor((data.color >> 8) & 0xff)}, ${Math.floor(data.color & 0xff)})`; | |
document.body.appendChild(label); | |
labels.push({ element: label, planet: planet }); | |
}); | |
// Création de la comète | |
function createComet() { | |
// Noyau de la comète | |
const cometGeometry = new THREE.SphereGeometry(0.3, 16, 16); | |
const cometMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x88ccff, | |
emissive: 0x88ccff, | |
emissiveIntensity: 0.5 | |
}); | |
const comet = new THREE.Mesh(cometGeometry, cometMaterial); | |
// Queue de la comète (particules) | |
const tailParticles = new THREE.BufferGeometry(); | |
const tailMaterial = new THREE.PointsMaterial({ | |
color: 0x88ffff, | |
size: 0.1, | |
transparent: true, | |
opacity: 0.8, | |
blending: THREE.AdditiveBlending | |
}); | |
const tailVertices = []; | |
for (let i = 0; i < 100; i++) { | |
// Position aléatoire derrière la comète | |
const x = -Math.random() * 5; | |
const y = (Math.random() - 0.5) * 0.5; | |
const z = (Math.random() - 0.5) * 0.5; | |
tailVertices.push(x, y, z); | |
} | |
tailParticles.setAttribute('position', new THREE.Float32BufferAttribute(tailVertices, 3)); | |
const tail = new THREE.Points(tailParticles, tailMaterial); | |
comet.add(tail); | |
// Position initiale aléatoire | |
const angle = Math.random() * Math.PI * 2; | |
const distance = 150 + Math.random() * 50; | |
comet.position.x = Math.cos(angle) * distance; | |
comet.position.z = Math.sin(angle) * distance; | |
comet.position.y = (Math.random() - 0.5) * 30; | |
// Vitesse et direction aléatoires | |
const velocity = new THREE.Vector3( | |
(Math.random() - 0.5) * 0.5, | |
(Math.random() - 0.5) * 0.2, | |
(Math.random() - 0.5) * 0.5 | |
); | |
// Label pour la comète | |
const label = document.createElement('div'); | |
label.className = 'comet-label'; | |
label.textContent = 'Comète'; | |
document.body.appendChild(label); | |
return { | |
mesh: comet, | |
velocity: velocity, | |
label: label, | |
tail: tail | |
}; | |
} | |
const comet = createComet(); | |
scene.add(comet.mesh); | |
// Contrôles UI | |
const speedControl = document.getElementById('speedControl'); | |
const speedValue = document.getElementById('speedValue'); | |
const toggleOrbits = document.getElementById('toggleOrbits'); | |
const toggleLabels = document.getElementById('toggleLabels'); | |
const toggleComet = document.getElementById('toggleComet'); | |
const planetInfo = document.getElementById('planetInfo'); | |
const planetName = document.getElementById('planetName'); | |
const planetDistance = document.getElementById('planetDistance'); | |
const planetSize = document.getElementById('planetSize'); | |
const planetPeriod = document.getElementById('planetPeriod'); | |
let showOrbits = true; | |
let showLabels = true; | |
let showComet = true; | |
let speedFactor = 1; | |
speedControl.addEventListener('input', (e) => { | |
speedFactor = parseFloat(e.target.value); | |
speedValue.textContent = speedFactor.toFixed(1) + 'x'; | |
}); | |
toggleOrbits.addEventListener('click', () => { | |
showOrbits = !showOrbits; | |
orbits.forEach(orbit => orbit.visible = showOrbits); | |
toggleOrbits.textContent = showOrbits ? 'Masquer les orbites' : 'Afficher les orbites'; | |
toggleOrbits.classList.toggle('bg-blue-600'); | |
toggleOrbits.classList.toggle('bg-gray-600'); | |
}); | |
toggleLabels.addEventListener('click', () => { | |
showLabels = !showLabels; | |
labels.forEach(label => label.element.style.display = showLabels ? 'block' : 'none'); | |
comet.label.style.display = showLabels ? 'block' : 'none'; | |
toggleLabels.textContent = showLabels ? 'Masquer les labels' : 'Afficher les labels'; | |
toggleLabels.classList.toggle('bg-purple-600'); | |
toggleLabels.classList.toggle('bg-gray-600'); | |
}); | |
toggleComet.addEventListener('click', () => { | |
showComet = !showComet; | |
comet.mesh.visible = showComet; | |
comet.label.style.display = showComet && showLabels ? 'block' : 'none'; | |
toggleComet.textContent = showComet ? 'Masquer la comète' : 'Afficher la comète'; | |
toggleComet.classList.toggle('bg-green-600'); | |
toggleComet.classList.toggle('bg-gray-600'); | |
}); | |
// Gestion des clics sur les planètes | |
window.addEventListener('click', (event) => { | |
// Calcul de la position de la souris en coordonnées normalisées | |
mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; | |
// Mise à jour du raycaster | |
raycaster.setFromCamera(mouse, camera); | |
// Calcul des objets intersectés | |
const intersects = raycaster.intersectObjects(planetMeshes); | |
if (intersects.length > 0) { | |
const planetMesh = intersects[0].object; | |
const planetData = planets.find(p => p.mesh === planetMesh).data; | |
// Affichage des informations de la planète | |
planetName.textContent = planetData.name; | |
planetDistance.textContent = planetData.distance + " UA"; | |
planetSize.textContent = (planetData.size * 12742).toFixed(0) + " km"; | |
planetPeriod.textContent = planetData.period + " jours terrestres"; | |
// Animation du panneau d'information | |
planetInfo.style.display = 'block'; | |
planetInfo.style.opacity = '0'; | |
planetInfo.style.transform = 'translateX(20px)'; | |
setTimeout(() => { | |
planetInfo.style.opacity = '1'; | |
planetInfo.style.transform = 'translateX(0)'; | |
}, 10); | |
} | |
}); | |
// Animation | |
function animate() { | |
requestAnimationFrame(animate); | |
// Rotation des planètes autour du soleil | |
planets.forEach(planet => { | |
planet.angle += planet.speed * speedFactor; | |
planet.mesh.position.x = Math.cos(planet.angle) * planet.distance; | |
planet.mesh.position.z = Math.sin(planet.angle) * planet.distance; | |
// Rotation sur elles-mêmes | |
planet.mesh.rotation.y += 0.01 * speedFactor; | |
}); | |
// Mise à jour des labels | |
labels.forEach(label => { | |
const screenPosition = label.planet.position.clone().project(camera); | |
const x = (screenPosition.x * 0.5 + 0.5) * window.innerWidth; | |
const y = (-(screenPosition.y * 0.5 + 0.5) + 1) * window.innerHeight; | |
label.element.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`; | |
}); | |
// Mise à jour du label de la comète | |
const cometScreenPos = comet.mesh.position.clone().project(camera); | |
const cometX = (cometScreenPos.x * 0.5 + 0.5) * window.innerWidth; | |
const cometY = (-(cometScreenPos.y * 0.5 + 0.5) + 1) * window.innerHeight; | |
comet.label.style.transform = `translate(-50%, -50%) translate(${cometX}px,${cometY}px)`; | |
// Animation du soleil | |
sun.rotation.y += 0.002 * speedFactor; | |
sunGlow.scale.set(1 + Math.sin(Date.now() * 0.001) * 0.05, 1, 1); | |
// Animation des particules solaires | |
sunParticles.rotation.y += 0.001 * speedFactor; | |
sunParticles.children.forEach((particle, i) => { | |
particle.position.x += (Math.random() - 0.5) * 0.1; | |
particle.position.y += (Math.random() - 0.5) * 0.1; | |
particle.position.z += (Math.random() - 0.5) * 0.1; | |
// Ramener les particules vers le soleil si elles s'éloignent trop | |
const distance = particle.position.length(); | |
if (distance > 8) { | |
particle.position.normalize().multiplyScalar(5 + Math.random() * 2); | |
} | |
}); | |
// Animation de la comète | |
comet.mesh.position.x += comet.velocity.x * speedFactor; | |
comet.mesh.position.y += comet.velocity.y * speedFactor; | |
comet.mesh.position.z += comet.velocity.z * speedFactor; | |
// Faire tourner la queue de la comète | |
comet.tail.rotation.y += 0.01 * speedFactor; | |
// Si la comète sort de la scène, la repositionner de l'autre côté | |
if (Math.abs(comet.mesh.position.x) > 200 || | |
Math.abs(comet.mesh.position.y) > 100 || | |
Math.abs(comet.mesh.position.z) > 200) { | |
// Nouvelle position aléatoire | |
const angle = Math.random() * Math.PI * 2; | |
const distance = 150 + Math.random() * 50; | |
comet.mesh.position.x = Math.cos(angle) * distance; | |
comet.mesh.position.z = Math.sin(angle) * distance; | |
comet.mesh.position.y = (Math.random() - 0.5) * 30; | |
// Nouvelle direction | |
comet.velocity.set( | |
(Math.random() - 0.5) * 0.5, | |
(Math.random() - 0.5) * 0.2, | |
(Math.random() - 0.5) * 0.5 | |
); | |
} | |
// Orientation de la comète vers sa direction de mouvement | |
comet.mesh.lookAt( | |
comet.mesh.position.x + comet.velocity.x, | |
comet.mesh.position.y + comet.velocity.y, | |
comet.mesh.position.z + comet.velocity.z | |
); | |
controls.update(); | |
renderer.render(scene, camera); | |
} | |
// Redimensionnement | |
window.addEventListener('resize', () => { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
}); | |
// Cacher l'écran de chargement une fois que tout est prêt | |
window.addEventListener('load', () => { | |
setTimeout(() => { | |
document.getElementById('loading').style.opacity = '0'; | |
setTimeout(() => { | |
document.getElementById('loading').style.display = 'none'; | |
}, 500); | |
}, 1000); | |
}); | |
animate(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=yaspred/systeme-solaire" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |