Code Templates Collection angularforall.com

Page produits 360 style 01 - Bootstrap 5

Bootstrap Bootstrap5 Dashboard Admin Widgets Html

Template Bootstrap 5 de page produits 360 avec différents widgets et graphiques.

<!DOCTYPE html>
<html lang="fr" prefix="og: https://ogp.me/ns#">
<head>
  <meta charset="UTF-8" />
  <meta name="copyright" content="AngularForAll" />
  <meta name="author" content="AngularForAll" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="Cache-Control" content="public, max-age=604800" />
  <title>Product Detail 360 Bootstrap 5 01 | AngularForAll</title>
<!-- Bootstrap 5 + Icons -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
  <!-- Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: 'Inter', sans-serif;
      background: #f5f7fc;
      color: #1e293b;
      overflow-x: hidden;
    }
    /* Header */
    .navbar-product {
      background: white;
      box-shadow: 0 4px 20px rgba(0,0,0,0.03);
      padding: 1rem 2rem;
      border-bottom: 1px solid rgba(0,0,0,0.05);
    }
    .breadcrumb {
      margin: 0;
      font-size: 0.9rem;
    }
    .breadcrumb a {
      color: #64748b;
      text-decoration: none;
    }
    /* Layout principal */
    .product-main {
      padding: 2rem 1.5rem;
      max-width: 1400px;
      margin: 0 auto;
    }
    /* Zone 3D */
    .viewer-3d-container {
      background: linear-gradient(145deg, #0f172a, #1e293b);
      border-radius: 32px;
      padding: 16px;
      box-shadow: 0 25px 40px -12px rgba(0,0,0,0.3);
      position: relative;
      overflow: hidden;
      margin-bottom: 20px;
    }
    #canvas-wrapper {
      width: 100%;
      aspect-ratio: 1 / 1;
      border-radius: 24px;
      overflow: hidden;
      background: #0b1120;
      box-shadow: inset 0 0 30px #00000040;
    }
    canvas {
      display: block;
      width: 100%;
      height: 100%;
      outline: none;
    }
    .viewer-badge {
      position: absolute;
      bottom: 30px;
      left: 30px;
      background: rgba(20,30,50,0.7);
      backdrop-filter: blur(12px);
      color: white;
      padding: 6px 18px;
      border-radius: 40px;
      font-size: 0.8rem;
      border: 1px solid rgba(255,255,255,0.2);
      z-index: 5;
      pointer-events: none;
    }
    .rotate-hint {
      position: absolute;
      bottom: 30px;
      right: 30px;
      background: rgba(0,0,0,0.4);
      backdrop-filter: blur(8px);
      color: #ccd6f6;
      padding: 6px 16px;
      border-radius: 30px;
      font-size: 0.8rem;
      z-index: 5;
      pointer-events: none;
    }

    /* Infos produit */
    .product-title {
      font-size: 2.4rem;
      font-weight: 700;
      letter-spacing: -0.02em;
      margin-bottom: 0.25rem;
    }
    .product-sub {
      color: #64748b;
      font-weight: 400;
      margin-bottom: 1rem;
    }
    .price {
      font-size: 2.2rem;
      font-weight: 700;
      color: #0b1e33;
    }
    .old-price {
      text-decoration: line-through;
      color: #94a3b8;
      font-size: 1.2rem;
      margin-left: 12px;
    }
    .stock-badge {
      background: #dcfce7;
      color: #15803d;
      padding: 4px 14px;
      border-radius: 30px;
      font-weight: 500;
      font-size: 0.9rem;
      display: inline-block;
    }
    .color-options {
      display: flex;
      gap: 16px;
      margin: 20px 0;
    }
    .color-dot {
      width: 40px;
      height: 40px;
      border-radius: 40px;
      cursor: pointer;
      border: 3px solid transparent;
      transition: all 0.15s;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    .color-dot.active {
      border-color: #1e293b;
      transform: scale(1.1);
      box-shadow: 0 6px 14px rgba(0,0,0,0.2);
    }
    .color-dot:hover { transform: scale(1.05); }

    .storage-select {
      background: white;
      border: 1px solid #e2e8f0;
      border-radius: 16px;
      padding: 12px 18px;
      margin: 20px 0;
    }
    .btn-cart {
      background: #0f172a;
      color: white;
      border: none;
      padding: 16px 24px;
      border-radius: 50px;
      font-weight: 600;
      font-size: 1.1rem;
      width: 100%;
      transition: 0.2s;
      box-shadow: 0 10px 20px -5px rgba(15,23,42,0.3);
    }
    .btn-cart:hover {
      background: #1e293b;
      transform: translateY(-2px);
    }
    .btn-like {
      background: white;
      border: 1px solid #e2e8f0;
      padding: 16px 20px;
      border-radius: 50px;
      font-weight: 500;
      transition: 0.2s;
      color: #475569;
    }
    .btn-like:hover {
      background: #fee2e2;
      border-color: #f87171;
      color: #b91c1c;
    }
    .btn-like.active {
      background: #fecaca;
      border-color: #ef4444;
      color: #b91c1c;
    }
    .description-box {
      background: white;
      border-radius: 24px;
      padding: 24px;
      margin-top: 24px;
      box-shadow: 0 8px 24px rgba(0,0,0,0.04);
    }
    .feature-icon {
      width: 48px;
      height: 48px;
      background: #eef2ff;
      border-radius: 16px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #1e3a8a;
    }

    @media (max-width: 992px) {
      .product-main { padding: 1rem; }
      .product-title { font-size: 2rem; }
    }
  </style>

  <!-- Three.js -->
  <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>

<!-- Navigation simple -->
<nav class="navbar-product d-flex align-items-center justify-content-between">
  <div class="d-flex align-items-center gap-3">
    <i aria-hidden="true" class="bi bi-phone" style="font-size: 1.8rem; color: #1e293b;"></i>
    <span style="font-weight: 600; font-size: 1.4rem; letter-spacing: -0.5px;">Galaxy Neo</span>
  </div>
  <nav aria-label="breadcrumb">
    <ol class="breadcrumb">
      <li class="breadcrumb-item"><a href="#">Accueil</a></li>
      <li class="breadcrumb-item"><a href="#">Smartphones</a></li>
      <li class="breadcrumb-item active" aria-current="page">Galaxy Neo 5G</li>
    </ol>
  </nav>
</nav>

<!-- Contenu principal -->
<div class="product-main">
  <div class="row g-4">
    <!-- COLONNE GAUCHE : Visualisation 3D -->
    <div class="col-lg-7">
      <div class="viewer-3d-container">
        <div id="canvas-wrapper">
          <canvas id="phone-canvas"></canvas>
        </div>
        <div class="viewer-badge">
          <i aria-hidden="true" class="bi bi-badge-3d me-1"></i> Vue 360° · Drag & zoom
        </div>
        <div class="rotate-hint">
          <i aria-hidden="true" class="bi bi-arrow-repeat me-1"></i> Faites glisser pour tourner
        </div>
      </div>

      <!-- Miniatures / variantes (optionnelles) -->
      <div class="d-flex gap-3 mt-3 justify-content-center">
        <div class="text-center">
          <div class="rounded-3 bg-white p-2 shadow-sm" style="width: 70px; height: 70px; cursor: pointer;" onclick="setPhoneColor(0x2c2c36)">
            <div style="background: #2c2c36; width: 100%; height: 100%; border-radius: 12px;"></div>
          </div>
          <small class="mt-1 d-block">Graphite</small>
        </div>
        <div class="text-center">
          <div class="rounded-3 bg-white p-2 shadow-sm" style="width: 70px; height: 70px; cursor: pointer;" onclick="setPhoneColor(0xc0b7a8)">
            <div style="background: #c0b7a8; width: 100%; height: 100%; border-radius: 12px;"></div>
          </div>
          <small class="mt-1 d-block">Argent</small>
        </div>
        <div class="text-center">
          <div class="rounded-3 bg-white p-2 shadow-sm" style="width: 70px; height: 70px; cursor: pointer;" onclick="setPhoneColor(0x4a6fa5)">
            <div style="background: #4a6fa5; width: 100%; height: 100%; border-radius: 12px;"></div>
          </div>
          <small class="mt-1 d-block">Bleu Océan</small>
        </div>
      </div>
    </div>

    <!-- COLONNE DROITE : Infos, config, panier -->
    <div class="col-lg-5">
      <div class="ps-lg-3">
        <h1 class="product-title">Galaxy Neo 5G</h1>
        <div class="product-sub d-flex align-items-center gap-3">
          <span><i aria-hidden="true" class="bi bi-star-fill text-warning me-1"></i> 4.8 (234 avis)</span>
          <span><i aria-hidden="true" class="bi bi-chat me-1"></i> 128 questions</span>
        </div>

        <div class="d-flex align-items-baseline flex-wrap">
          <span class="price">899 €</span>
          <span class="old-price">999 €</span>
          <span class="ms-3 stock-badge"><i aria-hidden="true" class="bi bi-check-circle me-1"></i> En stock</span>
        </div>

        <!-- Sélecteur de couleur -->
        <div class="mt-4">
          <label class="fw-semibold mb-2">Couleur</label>
          <div class="color-options">
            <div class="color-dot active" style="background: #2c2c36;" onclick="setPhoneColor(0x2c2c36); setActiveColor(this)"></div>
            <div class="color-dot" style="background: #c0b7a8;" onclick="setPhoneColor(0xc0b7a8); setActiveColor(this)"></div>
            <div class="color-dot" style="background: #4a6fa5;" onclick="setPhoneColor(0x4a6fa5); setActiveColor(this)"></div>
          </div>
        </div>

        <!-- Stockage -->
        <div class="storage-select d-flex justify-content-between align-items-center">
          <span><i aria-hidden="true" class="bi bi-memory me-2"></i>Stockage</span>
          <select class="form-select w-auto border-0 bg-transparent fw-semibold"
          style="width: auto;" aria-label="Sélecteur">
            <option selected>256 Go</option>
            <option>512 Go (+120 €)</option>
            <option>1 To (+280 €)</option>
          </select>
        </div>

        <!-- Boutons actions -->
        <div class="d-flex gap-3 mt-3">
          <button class="btn-cart" onclick="addToCart()"><i aria-hidden="true" class="bi bi-cart-plus me-2"></i>Ajouter au panier</button>
          <button class="btn-like" id="likeButton" onclick="toggleLike()"><i aria-hidden="true" class="bi bi-heart me-2"></i>Aimer</button>
        </div>

        <!-- Livraison / Retour -->
        <div class="d-flex gap-4 mt-4 text-secondary small">
          <span><i aria-hidden="true" class="bi bi-truck me-1"></i> Livraison gratuite</span>
          <span><i aria-hidden="true" class="bi bi-arrow-return-left me-1"></i> Retour 30j</span>
          <span><i aria-hidden="true" class="bi bi-shield-check me-1"></i> Garantie 2 ans</span>
        </div>

        <!-- Description -->
        <div class="description-box">
          <h5 class="fw-bold mb-3">Description</h5>
          <p>Le Galaxy Neo 5G redéfinit les standards avec son écran AMOLED 6.7" 120Hz, son triple capteur photo 50MP et sa batterie longue durée. Design premium en titane, résistance à l'eau IP68.</p>
          <hr class="my-3">
          <div class="row g-3">
            <div class="col-4 text-center">
              <div class="feature-icon mx-auto mb-2"><i aria-hidden="true" class="bi bi-cpu fs-4"></i></div>
              <small class="fw-semibold">Snapdragon 8 Gen 3</small>
            </div>
            <div class="col-4 text-center">
              <div class="feature-icon mx-auto mb-2"><i aria-hidden="true" class="bi bi-battery-charging fs-4"></i></div>
              <small class="fw-semibold">5000 mAh</small>
            </div>
            <div class="col-4 text-center">
              <div class="feature-icon mx-auto mb-2"><i aria-hidden="true" class="bi bi-droplet-half fs-4"></i></div>
              <small class="fw-semibold">IP68</small>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

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

  // --- Références globales pour changer couleur ---
  let phoneBodyMaterial;
  let scene, camera, renderer, controls;
  let phoneGroup;

  // Initialisation Three.js
  const canvas = document.getElementById('phone-canvas');

  function initThree() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color('#0b1120');

    camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
    camera.position.set(3, 2, 5);

    renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: false });
    renderer.setSize(canvas.clientWidth, canvas.clientHeight);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;

    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;
    controls.autoRotate = true;
    controls.autoRotateSpeed = 1.5;
    controls.enableZoom = true;
    controls.enablePan = false;
    controls.target.set(0, 0.5, 0);
    controls.maxPolarAngle = Math.PI / 1.8;
    controls.minDistance = 2.2;
    controls.maxDistance = 7.0;

    // Lumières
    scene.add(new THREE.AmbientLight(0x404060, 0.55));
    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;
    scene.add(mainLight);

    const fill1 = new THREE.PointLight(0x4466cc, 0.7);
    fill1.position.set(-3, 2, 4);
    scene.add(fill1);
    const fill2 = new THREE.PointLight(0xccaa88, 0.5);
    fill2.position.set(2, 1, -5);
    scene.add(fill2);

    // Sol réfléchissant
    const planeGeo = new THREE.CircleGeometry(4, 20);
    const planeMat = new THREE.MeshStandardMaterial({ color: 0x1a2332, roughness: 0.4, metalness: 0.1, side: THREE.DoubleSide });
    const plane = new THREE.Mesh(planeGeo, planeMat);
    plane.rotation.x = -Math.PI/2;
    plane.position.y = -0.01;
    plane.receiveShadow = true;
    scene.add(plane);

    // Grille
    const grid = new THREE.GridHelper(6, 20, 0x88aaff, 0x334466);
    grid.position.y = 0;
    scene.add(grid);

    // --- Construction du téléphone (identique à l'exemple précédent) ---
    phoneGroup = new THREE.Group();

    const bodyMat = new THREE.MeshStandardMaterial({ color: 0x2c2c36, roughness: 0.25, metalness: 0.4 });
    phoneBodyMaterial = bodyMat;

    const mainBody = new THREE.Mesh(new THREE.BoxGeometry(1.6, 3.2, 0.32), bodyMat);
    mainBody.castShadow = true; mainBody.receiveShadow = true; mainBody.position.y = 0.5;
    phoneGroup.add(mainBody);

    const screenMat = new THREE.MeshStandardMaterial({ color: 0x0a0e14, roughness: 0.15, emissive: new THREE.Color(0x112233), emissiveIntensity: 0.3 });
    const screen = new THREE.Mesh(new THREE.BoxGeometry(1.45, 2.95, 0.05), screenMat);
    screen.castShadow = true; screen.position.set(0, 0.5, 0.165);
    phoneGroup.add(screen);

    const frameMat = new THREE.MeshStandardMaterial({ color: 0xccccdd, roughness: 0.2, metalness: 0.8 });
    const frontFrame = new THREE.Mesh(new THREE.BoxGeometry(1.58, 3.18, 0.02), frameMat);
    frontFrame.position.set(0, 0.5, 0.19);
    phoneGroup.add(frontFrame);

    // Caméras / détails (simplifié)
    const camMod = new THREE.Mesh(new THREE.CylinderGeometry(0.25,0.25,0.04), new THREE.MeshStandardMaterial({color:0x333338}));
    camMod.rotation.y = 0.3; camMod.position.set(0.3,1.35,-0.18); phoneGroup.add(camMod);

    scene.add(phoneGroup);

    // Redimensionnement
    window.addEventListener('resize', () => {
      const width = document.getElementById('canvas-wrapper').clientWidth;
      const height = width; // carré
      renderer.setSize(width, height);
      camera.aspect = 1;
      camera.updateProjectionMatrix();
    });

    // Lancer animation
    function animate() {
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    }
    animate();

    // Ajuster taille initiale
    setTimeout(() => {
      const w = document.getElementById('canvas-wrapper').clientWidth;
      renderer.setSize(w, w);
      camera.aspect = 1;
      camera.updateProjectionMatrix();
    }, 10);
  }

  initThree();

  // Fonction globale pour changer couleur
  window.setPhoneColor = (hexColor) => {
    if (phoneBodyMaterial) {
      phoneBodyMaterial.color.setHex(hexColor);
    }
  };

  window.setActiveColor = (element) => {
    document.querySelectorAll('.color-dot').forEach(d => d.classList.remove('active'));
    element.classList.add('active');
  };

  // Panier / like
  window.addToCart = () => {
    alert('📱 Galaxy Neo ajouté au panier !');
  };

  window.toggleLike = () => {
    const btn = document.getElementById('likeButton');
    btn.classList.toggle('active');
    const icon = btn.querySelector('i');
    if (btn.classList.contains('active')) {
      icon.classList.remove('bi-heart');
      icon.classList.add('bi-heart-fill');
      btn.innerHTML = '<i aria-hidden="true" class="bi bi-heart-fill me-2"></i>Aimé';
    } else {
      icon.classList.remove('bi-heart-fill');
      icon.classList.add('bi-heart');
      btn.innerHTML = '<i aria-hidden="true" class="bi bi-heart me-2"></i>Aimer';
    }
  };

  // Désactiver auto-rotate après interaction
  canvas.addEventListener('mousedown', () => controls.autoRotate = false);
  canvas.addEventListener('touchstart', () => controls.autoRotate = false);
  let idleTimer;
  window.addEventListener('mousemove', () => {
    controls.autoRotate = false;
    clearTimeout(idleTimer);
    idleTimer = setTimeout(() => controls.autoRotate = true, 4000);
  });
</script>

<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Télécharger le fichier source

Questions fréquentes

Template Bootstrap 5 de page produits 360 avec différents widgets et graphiques.

Oui. Le code de « Page produits 360 style 01 - Bootstrap 5 » est libre de réutilisation : copiez-collez le HTML/CSS/JS dans votre projet et adaptez-le à vos besoins. Il est fourni gratuitement sur AngularForAll.

Partager