Snippets Vue 360 Full Background – Bootstrap 5

🏷️ Extraits & Composants HTML 📅 31/03/2026 20:00:00 👤 Mezgani said
Bootstrap 5 Vue 360 360 View Full Background Template Bootstrap Snippets Html

Exemple de vue 360° en full background avec widgets et graphiques intégrés, développé en Bootstrap 5.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8" />
  <meta name="copyright" content="MEZGANI Said" />
  <meta name="author" content="AngularForAll" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Snippets 360 View 01 | AngularForAll</title>
<style>
    body {
      margin: 0;
      overflow: hidden;
      font-family: 'Segoe UI', 'Inter', sans-serif;
      background-color: #11141a;
    }
    #info {
      position: absolute;
      top: 20px;
      left: 0;
      width: 100%;
      text-align: center;
      color: white;
      text-shadow: 0 2px 20px rgba(0,0,0,0.8);
      pointer-events: none;
      z-index: 10;
    }
    h1 {
      font-weight: 500;
      font-size: 1.8rem;
      letter-spacing: 4px;
      margin-bottom: 5px;
      background: linear-gradient(135deg, #e0e7ff, #ffffff);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }
    .subtitle {
      font-size: 1rem;
      opacity: 0.8;
      font-weight: 300;
      letter-spacing: 2px;
    }
    #instruction {
      position: absolute;
      bottom: 25px;
      left: 0;
      width: 100%;
      text-align: center;
      color: rgba(255,255,255,0.7);
      font-size: 0.9rem;
      pointer-events: none;
      z-index: 10;
      background: rgba(20,30,45,0.4);
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
      padding: 10px 24px;
      width: fit-content;
      margin: 0 auto;
      border-radius: 40px;
      border: 1px solid rgba(255,255,255,0.15);
      left: 50%;
      transform: translateX(-50%);
    }
    #instruction i {
      font-style: normal;
      margin: 0 8px;
    }
    .badge {
      position: absolute;
      bottom: 25px;
      right: 25px;
      color: rgba(255,255,255,0.5);
      font-size: 0.8rem;
      z-index: 10;
      background: rgba(0,0,0,0.2);
      padding: 6px 16px;
      border-radius: 30px;
      backdrop-filter: blur(5px);
      pointer-events: none;
    }
    @media (max-width: 600px) {
      h1 { font-size: 1.3rem; }
      #instruction { font-size: 0.75rem; padding: 8px 16px; }
    }
  </style>
  <!-- Import Three.js et addons -->
  <script type="importmap">
    {
      "imports": {
        "three": "https://unpkg.com/three@0.128.0/build/three.module.js",
        "three/addons/": "https://unpkg.com/three@0.128.0/examples/jsm/"
      }
    }
  </script>
</head>
<body>
  <div id="info">
    <h1>📱 GALAXY NEO · VUE 360°</h1>
    <div class="subtitle">Produit · Téléphone · Rotation complète</div>
  </div>
  <div id="instruction">
    <i>🖱️ Glisser pour pivoter</i> · <i>📌 Scroll pour zoomer</i> · <i>🔄 Vue sous tous les angles</i>
  </div>
  <div class="badge">3D interactif · OrbitControls</div>

  <script type="module">
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';

    // --- INITIALISATION SCÈNE, CAMÉRA, RENDERERS ---
    const scene = new THREE.Scene();
    scene.background = new THREE.Color('#11141a'); // fond sombre élégant

    // Caméra
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
    camera.position.set(3, 2, 5);
    camera.lookAt(0, 0.5, 0);

    // Renderer WebGL
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    document.body.appendChild(renderer.domElement);

    // CSS2 Renderer pour les labels (optionnel, ajoute du détail produit)
    const cssRenderer = new CSS2DRenderer();
    cssRenderer.setSize(window.innerWidth, window.innerHeight);
    cssRenderer.domElement.style.position = 'absolute';
    cssRenderer.domElement.style.top = '0px';
    cssRenderer.domElement.style.left = '0px';
    cssRenderer.domElement.style.pointerEvents = 'none';  // pour ne pas bloquer les contrôles
    document.body.appendChild(cssRenderer.domElement);

    // --- CONTRÔLES (360° fluides) ---
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;
    controls.autoRotate = true;
    controls.autoRotateSpeed = 1.2;
    controls.enableZoom = true;
    controls.zoomSpeed = 1.0;
    controls.enablePan = false;
    controls.target.set(0, 0.5, 0);
    controls.maxPolarAngle = Math.PI / 1.8; // permet de voir le dessus mais pas passer en dessous
    controls.minDistance = 2.2;
    controls.maxDistance = 8.0;

    // --- LUMIÈRES (mise en valeur du téléphone) ---
    // Lumière ambiante
    scene.add(new THREE.AmbientLight(0x404060, 0.55));

    // Lumière principale directionnelle (soleil)
    const mainLight = new THREE.DirectionalLight(0xfff5e6, 1.2);
    mainLight.position.set(4, 6, 3);
    mainLight.castShadow = true;
    mainLight.receiveShadow = true;
    mainLight.shadow.mapSize.width = 1024;
    mainLight.shadow.mapSize.height = 1024;
    const d = 6;
    mainLight.shadow.camera.left = -d;
    mainLight.shadow.camera.right = d;
    mainLight.shadow.camera.top = d;
    mainLight.shadow.camera.bottom = -d;
    mainLight.shadow.camera.near = 1;
    mainLight.shadow.camera.far = 20;
    mainLight.shadow.bias = -0.001;
    scene.add(mainLight);

    // Remplissage coloré
    const fillLight1 = new THREE.PointLight(0x4466cc, 0.7);
    fillLight1.position.set(-3, 2, 4);
    scene.add(fillLight1);

    const fillLight2 = new THREE.PointLight(0xccaa88, 0.5);
    fillLight2.position.set(2, 1, -5);
    scene.add(fillLight2);

    const backLight = new THREE.PointLight(0x6688aa, 0.5);
    backLight.position.set(-1, 1.5, -6);
    scene.add(backLight);

    // Lumière sous le téléphone pour éclairer les détails
    const bottomLight = new THREE.PointLight(0x335577, 0.3);
    bottomLight.position.set(0, -1, 1);
    scene.add(bottomLight);

    // --- SOL / ENVIRONNEMENT (plateau réfléchissant) ---
    const planeGeometry = new THREE.CircleGeometry(5, 32);
    const planeMaterial = new THREE.MeshStandardMaterial({
      color: 0x1a2332,
      roughness: 0.35,
      metalness: 0.2,
      transparent: true,
      opacity: 0.9,
      side: THREE.DoubleSide
    });
    const plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.rotation.x = -Math.PI / 2;
    plane.position.y = -0.01;
    plane.receiveShadow = true;
    scene.add(plane);

    // Grille subtile pour le style
    const gridHelper = new THREE.GridHelper(8, 20, 0x88aaff, 0x334466);
    gridHelper.position.y = 0;
    scene.add(gridHelper);

    // --- CONSTRUCTION DU TÉLÉPHONE (modèle détaillé) ---
    const phoneGroup = new THREE.Group();

    // Matériaux
    const bodyMaterial = new THREE.MeshStandardMaterial({
      color: 0x2c2c36,
      roughness: 0.25,
      metalness: 0.4,
      emissive: new THREE.Color(0x111122),
      emissiveIntensity: 0.1
    });

    const screenMaterial = new THREE.MeshStandardMaterial({
      color: 0x0a0e14,
      roughness: 0.15,
      metalness: 0.05,
      emissive: new THREE.Color(0x112233),
      emissiveIntensity: 0.3
    });

    const edgeMaterial = new THREE.MeshStandardMaterial({
      color: 0x888c94,
      roughness: 0.3,
      metalness: 0.7
    });

    const cameraMaterial = new THREE.MeshStandardMaterial({
      color: 0x1a1a1a,
      roughness: 0.1,
      metalness: 0.1,
      emissive: new THREE.Color(0x111122),
    });

    const buttonMaterial = new THREE.MeshStandardMaterial({
      color: 0x666a70,
      roughness: 0.4,
      metalness: 0.5
    });

    // --- Corps principal (brique arrondie via box avec radius? on va utiliser une box + petits cylindres pour les coins, mais on garde simple avec une box et un châssis) ---
    // Pour un rendu plus "smartphone", on fait un assemblage.

    // Châssis arrière / cadre principal
    const mainBody = new THREE.Mesh(new THREE.BoxGeometry(1.6, 3.2, 0.32), bodyMaterial);
    mainBody.castShadow = true;
    mainBody.receiveShadow = true;
    mainBody.position.y = 0.5;
    phoneGroup.add(mainBody);

    // Écran (face avant)
    const screen = new THREE.Mesh(new THREE.BoxGeometry(1.45, 2.95, 0.05), screenMaterial);
    screen.castShadow = true;
    screen.receiveShadow = true;
    screen.position.set(0, 0.5, 0.165);
    phoneGroup.add(screen);

    // Bordure brillante autour de l'écran (cadre avant)
    const frontFrame = new THREE.Mesh(new THREE.BoxGeometry(1.58, 3.18, 0.02), new THREE.MeshStandardMaterial({ color: 0xccccdd, roughness: 0.2, metalness: 0.8 }));
    frontFrame.position.set(0, 0.5, 0.19);
    frontFrame.castShadow = true;
    phoneGroup.add(frontFrame);

    // Verre de protection (transparent)
    const glass = new THREE.Mesh(new THREE.BoxGeometry(1.44, 2.94, 0.01), new THREE.MeshStandardMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0.15,
      roughness: 0.05,
      metalness: 0.0
    }));
    glass.position.set(0, 0.5, 0.21);
    phoneGroup.add(glass);

    // --- Détails : caméra frontale, capteurs ---
    const frontCamera = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.02, 16), cameraMaterial);
    frontCamera.rotation.x = Math.PI / 2;
    frontCamera.position.set(0.45, 1.75, 0.21);
    frontCamera.castShadow = true;
    phoneGroup.add(frontCamera);

    const sensor = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.02, 12), new THREE.MeshStandardMaterial({ color: 0x2a2a35 }));
    sensor.rotation.x = Math.PI / 2;
    sensor.position.set(-0.45, 1.75, 0.21);
    phoneGroup.add(sensor);

    // Haut-parleur
    const speaker = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.04, 0.02), new THREE.MeshStandardMaterial({ color: 0x1a1a1a }));
    speaker.position.set(0, 1.85, 0.21);
    phoneGroup.add(speaker);

    // --- Caméras arrières (triple module) ---
    const cameraModuleBase = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 0.04, 8), new THREE.MeshStandardMaterial({ color: 0x333338, roughness: 0.5 }));
    cameraModuleBase.rotation.y = Math.PI/8;
    cameraModuleBase.position.set(0.3, 1.35, -0.18);
    phoneGroup.add(cameraModuleBase);

    const lens1 = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, 0.03, 12), new THREE.MeshStandardMaterial({ color: 0x111116, roughness: 0.1, emissive: new THREE.Color(0x112233), emissiveIntensity: 0.5 }));
    lens1.rotation.x = Math.PI/2;
    lens1.position.set(0.3, 1.35, -0.2);
    phoneGroup.add(lens1);

    const lens2 = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.08, 0.03, 12), new THREE.MeshStandardMaterial({ color: 0x222228 }));
    lens2.rotation.x = Math.PI/2;
    lens2.position.set(0.5, 1.2, -0.19);
    phoneGroup.add(lens2);

    const lens3 = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.08, 0.03, 12), new THREE.MeshStandardMaterial({ color: 0x222228 }));
    lens3.rotation.x = Math.PI/2;
    lens3.position.set(0.1, 1.2, -0.19);
    phoneGroup.add(lens3);

    // Flash
    const flash = new THREE.Mesh(new THREE.SphereGeometry(0.05, 8), new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: new THREE.Color(0xffccaa), emissiveIntensity: 0.2 }));
    flash.position.set(0.5, 1.5, -0.19);
    phoneGroup.add(flash);

    // --- Boutons latéraux ---
    // Volume haut
    const btn1 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.02), buttonMaterial);
    btn1.position.set(0.82, 1.15, 0.05);
    phoneGroup.add(btn1);

    const btn2 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.02), buttonMaterial);
    btn2.position.set(0.82, 0.85, 0.05);
    phoneGroup.add(btn2);

    // Power (côté droit)
    const powerBtn = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.3, 0.02), buttonMaterial);
    powerBtn.position.set(-0.82, 1.2, 0.05);
    phoneGroup.add(powerBtn);

    // Port USB / bas
    const usbPort = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.03, 0.05), new THREE.MeshStandardMaterial({ color: 0x555555 }));
    usbPort.position.set(0, -0.95, 0.0);
    phoneGroup.add(usbPort);

    // --- Ajout de quelques reflets / lumières sur l'écran (via texture ou émissif) ---
    // On ajoute un léger dégradé sur l'écran via un canvas texture? On va plutôt placer un petit rectangle lumineux.

    // Ajustement position globale du groupe
    phoneGroup.position.y = 0.0;
    phoneGroup.position.z = 0.0;
    scene.add(phoneGroup);

    // --- ÉCLAIRAGE D'AMBIANCE supplémentaire pour faire briller ---
    // Quelques point lights pour refléter sur le métal

    // --- LABELS CSS (pour un côté "produit") ---
    function createLabel(text, position, fontSize = '14px', color = '#aaccff') {
      const div = document.createElement('div');
      div.textContent = text;
      div.style.color = '#fff';
      div.style.fontSize = fontSize;
      div.style.fontWeight = '400';
      div.style.textShadow = '0 0 15px rgba(0,0,0,0.9)';
      div.style.background = 'rgba(20,30,45,0.7)';
      div.style.padding = '4px 14px';
      div.style.borderRadius = '20px';
      div.style.backdropFilter = 'blur(8px)';
      div.style.border = '1px solid '+color;
      div.style.pointerEvents = 'none';
      div.style.letterSpacing = '0.5px';

      const label = new CSS2DObject(div);
      label.position.copy(position);
      return label;
    }

    const labelScreen = createLabel('📱 6.7" AMOLED', new THREE.Vector3(0, 1.4, 0.6), '13px', '#7aaaff');
    scene.add(labelScreen);

    const labelCamera = createLabel('📸 Triple caméra 50MP', new THREE.Vector3(0.8, 1.6, -0.4), '12px', '#ccaa88');
    scene.add(labelCamera);

    const labelFrame = createLabel('✨ Cadre en titane', new THREE.Vector3(-1.2, 0.9, 0.3), '12px', '#aaccdd');
    scene.add(labelFrame);

    // --- OBJETS DÉCORATIFS (pour l'environnement) ---
    // Quelques sphères lumineuses flottantes
    const sphereGeo = new THREE.SphereGeometry(0.08, 8);
    const sphereMat = new THREE.MeshStandardMaterial({ color: 0x88aadd, emissive: new THREE.Color(0x336699), emissiveIntensity: 0.8 });
    const orb1 = new THREE.Mesh(sphereGeo, sphereMat);
    orb1.position.set(1.8, 1.0, -1.5);
    scene.add(orb1);

    const orb2 = orb1.clone();
    orb2.material = new THREE.MeshStandardMaterial({ color: 0xccaa88, emissive: new THREE.Color(0x996633), emissiveIntensity: 0.6 });
    orb2.position.set(-2.0, 0.6, 1.2);
    scene.add(orb2);

    // --- ANIMATION & RENDU ---
    function animate() {
      requestAnimationFrame(animate);

      // Auto-rotation si activée
      controls.update();

      // Légère animation des orbes
      const time = Date.now() * 0.001;
      orb1.position.y = 1.0 + Math.sin(time * 0.8) * 0.15;
      orb2.position.x = -2.0 + Math.sin(time * 0.5) * 0.2;

      // Rendu
      renderer.render(scene, camera);
      cssRenderer.render(scene, camera);
    }

    animate();

    // --- RESIZE HANDLER ---
    window.addEventListener('resize', onWindowResize, false);
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
      cssRenderer.setSize(window.innerWidth, window.innerHeight);
    }

    // Petit effet : désactiver l'auto-rotation au premier drag utilisateur
    renderer.domElement.addEventListener('mousedown', () => {
      controls.autoRotate = false;
    });
    renderer.domElement.addEventListener('mouseup', () => {
      // On pourrait réactiver après 3 secondes, mais on laisse l'utilisateur maître
    });
    // Réactiver autoRotate après 5 secondes d'inactivité
    let idleTimer;
    window.addEventListener('mousemove', () => {
      controls.autoRotate = false;
      clearTimeout(idleTimer);
      idleTimer = setTimeout(() => {
        controls.autoRotate = true;
      }, 4000);
    });
    // Au touch aussi
    renderer.domElement.addEventListener('touchstart', () => {
      controls.autoRotate = false;
      clearTimeout(idleTimer);
      idleTimer = setTimeout(() => {
        controls.autoRotate = true;
      }, 4000);
    });
  </script>
</body>
</html>

Ouvrir cet aperçu dans un nouvel onglet du navigateur

🔗 Ouvrir dans le navigateur