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