Conception d'une Galerie 3D Animée avec Effets de Météores (HTML5/CSS3)

La création d'interfaces immersives en 3D repose aujourd'hui sur l'utilisation conjointe des transformations CSS3 avancées et de la manipulation du DOM via JavaScript. Cet article détaille la mise en œuvre d'un album rotatif tridimensionnel intégrant un arrière-plan dynamiuqe simulant une pluie de météores et un effet de pluie sur canevas.

Structure Fondamentale du Document HTML

L'architecture repose sur un conteneur principal configuré avec une perspective forcée pour simuler la profondeur spatiale. Les éléments de l'album sont disposés à l'intérieur d'un pivot central.


<html lang="fr">
<head>
   <meta charset="UTF-8">
   <title>Galerie 3D Interactive</title>
   <link rel="stylesheet" href="style.css">
</head>
<body>
   <!-- Pivot de l'album photo -->
   <div id="scene-3d">
       <div id="album-carousel">
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <div class="photo-item"></div>
           <p class="ground-shadow"></p>
       </div>
   </div>

   <!-- Arrière-plan météores -->
   <div class="meteor-container">
       <!-- Génération de 50 éléments météores via CSS -->
       <div class="meteor-streak"></div>
       <!-- ... répéter les streaks ... -->
   </div>

   <script src="https://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
   <script src="app.js"></script>
</body>
</html>

Stylisation CSS et Effets Visuels

Le rendu 3D est obtenu grâce à la propriété transform-style: preserve-3d. Chaque panneau de l'album utilise le reflet via -webkit-box-reflect pour augmenter le réalisme.

:root {
   --meteor-color: #64b5f6;
   --bg-dark: #050505;
}

body {
   margin: 0;
   overflow: hidden;
   background: radial-gradient(circle at center, #1a1a2e 0%, var(--bg-dark) 100%);
   perspective: 1200px;
}

#album-carousel {
   position: relative;
   width: 140px;
   height: 220px;
   margin: 25vh auto;
   transform-style: preserve-3d;
   transform: rotateX(-12deg);
}

.photo-item {
   position: absolute;
   width: 100%;
   height: 100%;
   border: 1px solid rgba(255, 255, 255, 0.2);
   box-shadow: 0 0 15px rgba(255, 255, 255, 0.1);
   -webkit-box-reflect: below 12px linear-gradient(transparent, rgba(0,0,0,0.4));
}

/* Animation des météores */
.meteor-container {
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   transform: rotate(-35deg);
}

.meteor-streak {
   position: absolute;
   background: linear-gradient(90deg, var(--meteor-color), transparent);
   height: 2px;
   filter: drop-shadow(0 0 5px var(--meteor-color));
   animation: slide-down 8s linear infinite;
}

@keyframes slide-down {
   0% { transform: translateX(100vw); opacity: 1; }
   100% { transform: translateX(-100vw); opacity: 0; }
}

Logique Interactive et Physique de Rotation

Le script suivant gère l'initialisation de la disposition circulaire et l'inertie lors de la rotation manuelle à la souris.

window.onload = function() {
   const gallery = document.getElementById('album-carousel');
   const items = gallery.getElementsByClassName('photo-item');
   const totalItems = items.length;
   const angleStep = 360 / totalItems;

   // Placement initial
   for (let i = 0; i < totalItems; i++) {
       items[i].style.backgroundImage = `url('img/${i + 1}.jpg')`;
       items[i].style.backgroundSize = 'cover';
       items[i].style.transform = `rotateY(${i * angleStep}deg) translateZ(400px)`;
       items[i].style.transition = `transform 1s ${ (totalItems - i) * 0.1 }s`;
   }

   let rotX = -12, rotY = 0;
   let lastMouseX, lastMouseY, diffX, diffY;
   let autoRotationTimer;

   document.onmousedown = function(e) {
       clearInterval(autoRotationTimer);
       lastMouseX = e.clientX;
       lastMouseY = e.clientY;

       this.onmousemove = function(ev) {
           diffX = ev.clientX - lastMouseX;
           diffY = ev.clientY - lastMouseY;

           rotY += diffX * 0.15;
           rotX -= diffY * 0.15;

           gallery.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
           lastMouseX = ev.clientX;
           lastMouseY = ev.clientY;
       };

       this.onmouseup = function() {
           this.onmousemove = null;
           // Effet d'inertie
           autoRotationTimer = setInterval(() => {
               diffX *= 0.96;
               diffY *= 0.96;
               rotY += diffX * 0.15;
               rotX -= diffY * 0.15;
               gallery.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
               
               if (Math.abs(diffX) < 0.1 && Math.abs(diffY) < 0.1) {
                   clearInterval(autoRotationTimer);
               }
           }, 16);
       };
       return false;
   };

   // Gestion du zoom (Perspective)
   window.onwheel = function(e) {
       let currentPerspective = parseInt(getComputedStyle(document.body).perspective);
       let newPerspective = currentPerspective + (e.deltaY > 0 ? 50 : -50);
       newPerspective = Math.max(500, Math.min(2000, newPerspective));
       document.body.style.perspective = `${newPerspective}px`;
   };
};

Simulation de Pluie sur Canevas (API 2D)

Pour ajouter une couche atmosphérique, un moteur de particules léger est implémenté via l'élément <canvas>, gérant la vélocité et les rebonds au sol.

(function() {
   const canvas = document.createElement('canvas');
   const ctx = canvas.getContext('2d');
   document.body.appendChild(canvas);

   let width, height;
   const drops = [];

   function resize() {
       width = canvas.width = window.innerWidth;
       height = canvas.height = window.innerHeight;
   }

   class Drop {
       constructor() { this.init(); }
       init() {
           this.x = Math.random() * width;
           this.y = -20;
           this.vy = Math.random() * 5 + 5;
           this.size = 1.5;
       }
       draw() {
           ctx.fillStyle = 'rgba(174, 194, 224, 0.6)';
           ctx.fillRect(this.x, this.y, this.size, this.size * 3);
       }
       update() {
           this.y += this.vy;
           if (this.y > height) this.init();
       }
   }

   function animate() {
       ctx.clearRect(0, 0, width, height);
       if (drops.length < 300) drops.push(new Drop());
       drops.forEach(d => { d.update(); d.draw(); });
       requestAnimationFrame(animate);
   }

   window.addEventListener('resize', resize);
   resize();
   animate();
})();

Étiquettes: HTML5 CSS3 JavaScript 3D Transforms Canvas API

Publié le 27 juillet à 12h19