Tailwind Css
Vue 360
Produit
Interactive
Rotation
E Commerce
Template
Html Css Js
Visionneuse produit 360° Tailwind CSS : rotation au drag, navigation clavier, miniatures de frames et expérience immersive pour e-commerce moderne.
<!DOCTYPE html>
<html lang="fr" class="dark">
<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>Snippets View360 Product Tailwindcss 2026 05020020 | AngularForAll</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Configuration Tailwind personnalisée -->
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
night: '#0a0a0f',
eclipse: '#12121a',
neon: {
cyan: '#00f0ff',
purple: '#b347ea',
pink: '#ff3cac',
},
glass: 'rgba(255, 255, 255, 0.04)',
},
fontFamily: {
display: ['"Space Grotesk"', 'system-ui', 'sans-serif'],
mono: ['"JetBrains Mono"', 'monospace'],
},
borderRadius: {
'4xl': '2.5rem',
},
boxShadow: {
'neon-cyan': '0 0 30px rgba(0, 240, 255, 0.15), 0 0 60px rgba(0, 240, 255, 0.05)',
'neon-purple': '0 0 30px rgba(179, 71, 234, 0.15), 0 0 60px rgba(179, 71, 234, 0.05)',
'inner-glow': 'inset 0 0 60px rgba(0, 240, 255, 0.03)',
},
animation: {
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'spin-slow': 'spin 8s linear infinite',
'float': 'float 6s ease-in-out infinite',
},
keyframes: {
float: {
'0%, 100%': { transform: 'translateY(0px)' },
'50%': { transform: 'translateY(-10px)' },
},
},
},
},
};
</script>
<!-- Polices Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<!-- Font Awesome 6 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
/* Styles personnalisés additionnels */
body {
background: linear-gradient(145deg, #0a0a0f 0%, #12121a 50%, #0d0d15 100%);
min-height: 100vh;
overflow-x: hidden;
}
.viewer-container {
background: radial-gradient(circle at 50% 0%, rgba(0, 240, 255, 0.06) 0%, transparent 70%);
}
.product-image-container {
background: linear-gradient(145deg, #16161f, #0d0d14);
cursor: grab;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.06);
}
.product-image-container:active {
cursor: grabbing;
}
.product-image-container::before {
content: '';
position: absolute;
inset: -1px;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(0, 240, 255, 0.3), transparent 40%, transparent 60%, rgba(179, 71, 234, 0.3));
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
pointer-events: none;
z-index: 10;
}
.product-image-container img {
pointer-events: none;
user-select: none;
-webkit-user-drag: none;
}
.btn-control {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.btn-control::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at center, rgba(0, 240, 255, 0.15), transparent 70%);
opacity: 0;
transition: opacity 0.3s ease;
}
.btn-control:hover::before {
opacity: 1;
}
.btn-control:hover {
border-color: rgba(0, 240, 255, 0.4);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4), 0 0 20px rgba(0, 240, 255, 0.1);
}
.btn-control:active {
transform: scale(0.95);
transition: transform 0.1s ease;
}
.frame-badge {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
letter-spacing: 0.05em;
}
.auto-rotate-btn {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
transition: all 0.3s ease;
}
.auto-rotate-btn:hover {
border-color: rgba(179, 71, 234, 0.5);
box-shadow: 0 0 20px rgba(179, 71, 234, 0.1);
}
.auto-rotate-btn.active {
background: rgba(179, 71, 234, 0.1);
border-color: rgba(179, 71, 234, 0.6);
box-shadow: 0 0 25px rgba(179, 71, 234, 0.2);
color: #b347ea;
}
/* Animation de changement d'image */
.fade-transition {
transition: opacity 0.15s ease;
}
/* Scrollbar stylisée */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #0a0a0f;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
/* Effet de particules d'arrière-plan (optionnel) */
.bg-particles {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
opacity: 0.3;
background-image: radial-gradient(circle at 20% 80%, rgba(0, 240, 255, 0.05) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(179, 71, 234, 0.05) 0%, transparent 50%);
}
</style>
</head>
<body class="font-display text-white antialiased relative">
<!-- Fond décoratif -->
<div class="bg-particles"></div>
<!-- Contenu principal -->
<div class="relative z-10 min-h-screen flex flex-col">
<!-- Navigation minimaliste -->
<nav class="w-full px-6 py-5 flex items-center justify-between max-w-6xl mx-auto">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple flex items-center justify-center shadow-neon-cyan">
<i class="fa-solid fa-cube text-white text-sm"></i>
</div>
<div>
<h1 class="text-lg font-semibold tracking-tight leading-none">ThreeSixty<span class="text-neon-cyan">View</span></h1>
<p class="text-xs text-gray-500 tracking-widest uppercase">Produit</p>
</div>
</div>
<div class="hidden sm:flex items-center gap-4">
<span class="text-xs text-gray-400 flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-neon-cyan animate-pulse"></span>
Rotation interactive
</span>
</div>
</nav>
<!-- Section principale avec la visionneuse -->
<main class="flex-grow flex items-center justify-center px-4 py-8">
<div class="w-full max-w-lg viewer-container">
<!-- En-tête du produit -->
<div class="text-center mb-8">
<div class="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-glass border border-white/5 text-xs text-gray-400 mb-4 backdrop-blur-sm">
<i class="fa-solid fa-rotate text-neon-cyan"></i>
<span>Glissez pour pivoter</span>
</div>
<h2 class="text-3xl font-bold tracking-tight mb-2">Air Max Neon</h2>
<p class="text-gray-400 text-sm">Édition limitée • Vue interactive 360°</p>
</div>
<!-- Visionneuse 360° -->
<div class="relative mb-8">
<!-- Conteneur de l'image avec effet de bordure -->
<div class="product-image-container rounded-3xl overflow-hidden aspect-square shadow-2xl shadow-black/50" id="viewerWrapper">
<img
id="productImage"
src="https://placehold.co/600x600/1a1a2e/00f0ff?text=Frame+01"
alt="Vue 360° du produit"
class="w-full h-full object-contain fade-transition relative z-0"
>
<!-- Indicateur de progression sur le côté -->
<div class="absolute right-4 top-1/2 -translate-y-1/2 z-20 flex flex-col items-center gap-1">
<div class="w-0.5 h-32 bg-white/5 rounded-full relative overflow-hidden">
<div class="absolute bottom-0 left-0 w-full bg-gradient-to-t from-neon-cyan to-neon-purple rounded-full transition-all duration-200" id="progressBar" style="height: 2.78%"></div>
</div>
</div>
<!-- Badge du nombre de frames -->
<div class="absolute bottom-4 left-4 z-20 frame-badge rounded-full px-3 py-1.5 text-xs font-mono text-gray-300 backdrop-blur-sm">
<span id="currentFrame">01</span>/<span id="totalFrames">36</span>
</div>
</div>
</div>
<!-- Contrôles de rotation -->
<div class="flex items-center justify-center gap-6 mb-6">
<!-- Bouton gauche -->
<button
id="rotateLeft"
class="btn-control w-14 h-14 rounded-full flex items-center justify-center group"
title="Rotation précédente"
>
<i class="fa-solid fa-chevron-left text-gray-300 group-hover:text-neon-cyan transition-colors text-lg"></i>
</button>
<!-- Zone centrale avec info -->
<div class="flex flex-col items-center gap-2 min-w-[100px]">
<div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-neon-cyan shadow-neon-cyan"></span>
<span class="text-xs text-gray-500 tracking-wider">FRAME</span>
<span class="w-2 h-2 rounded-full bg-neon-purple shadow-neon-purple"></span>
</div>
<div class="font-mono text-2xl font-bold tabular-nums">
<span id="frameNumber" class="text-neon-cyan">01</span>
</div>
</div>
<!-- Bouton droit -->
<button
id="rotateRight"
class="btn-control w-14 h-14 rounded-full flex items-center justify-center group"
title="Rotation suivante"
>
<i class="fa-solid fa-chevron-right text-gray-300 group-hover:text-neon-cyan transition-colors text-lg"></i>
</button>
</div>
<!-- Contrôles supplémentaires -->
<div class="flex items-center justify-center gap-4 flex-wrap">
<!-- Bouton auto-rotation -->
<button
id="autoRotateToggle"
class="auto-rotate-btn px-5 py-2.5 rounded-full text-sm font-medium flex items-center gap-2 transition-all"
>
<i class="fa-solid fa-play text-xs"></i>
<span>Auto-rotation</span>
</button>
</div>
<!-- Indice tactile -->
<div class="text-center mt-6">
<p class="text-xs text-gray-600 flex items-center justify-center gap-1.5">
<i class="fa-regular fa-hand-pointer"></i>
Cliquez-glissez sur l'image
</p>
</div>
</div>
</main>
<!-- Pied de page minimal -->
<footer class="py-6 text-center">
<p class="text-xs text-gray-700">
© 2026 ThreeSixtyView <span class="mx-2">•</span>
<span class="text-gray-600">Expérience immersive</span>
</p>
</footer>
</div>
<!-- Script 360° -->
<script>
(function() {
// ============ CONFIGURATION ============
const TOTAL_FRAMES = 36;
const IMAGE_PATH_PREFIX = 'https://placehold.co/600x600/1a1a2e/00f0ff?text=Frame+';
const AUTO_ROTATE_INTERVAL = 80; // ms
const DRAG_SENSITIVITY = 15; // pixels par frame
// ============ ÉTAT ============
let currentFrame = 1;
let isDragging = false;
let dragStartX = 0;
let dragStartFrame = 1;
let autoRotateTimer = null;
let isAutoRotating = false;
// ============ ÉLÉMENTS DOM ============
const imgEl = document.getElementById('productImage');
const wrapper = document.getElementById('viewerWrapper');
const currentFrameEl = document.getElementById('currentFrame');
const frameNumberEl = document.getElementById('frameNumber');
const totalFramesEl = document.getElementById('totalFrames');
const progressBar = document.getElementById('progressBar');
const rotateLeftBtn = document.getElementById('rotateLeft');
const rotateRightBtn = document.getElementById('rotateRight');
const autoRotateBtn = document.getElementById('autoRotateToggle');
// Initialisation des totaux
totalFramesEl.textContent = String(TOTAL_FRAMES).padStart(2, '0');
// ============ FONCTIONS ============
function getImageUrl(frame) {
const pad = String(frame).padStart(2, '0');
return `${IMAGE_PATH_PREFIX}${pad}`;
}
function updateUI(frame) {
// Normalisation
let f = frame;
if (f < 1) f = TOTAL_FRAMES;
if (f > TOTAL_FRAMES) f = 1;
currentFrame = f;
// Mise à jour de l'image avec transition
imgEl.style.opacity = '0.6';
imgEl.src = getImageUrl(currentFrame);
imgEl.onload = () => {
imgEl.style.opacity = '1';
};
setTimeout(() => {
imgEl.style.opacity = '1';
}, 50);
// Mise à jour des compteurs
const paddedFrame = String(currentFrame).padStart(2, '0');
currentFrameEl.textContent = paddedFrame;
frameNumberEl.textContent = paddedFrame;
// Mise à jour de la barre de progression
const progressPercent = ((currentFrame - 1) / (TOTAL_FRAMES - 1)) * 100;
progressBar.style.height = `${progressPercent}%`;
}
function rotateLeft() {
updateUI(currentFrame - 1);
}
function rotateRight() {
updateUI(currentFrame + 1);
}
// ============ AUTO-ROTATION ============
function startAutoRotate() {
if (autoRotateTimer) return;
isAutoRotating = true;
autoRotateBtn.classList.add('active');
autoRotateBtn.querySelector('i').className = 'fa-solid fa-pause text-xs';
autoRotateBtn.querySelector('span').textContent = 'Pause';
autoRotateTimer = setInterval(() => {
rotateRight();
}, AUTO_ROTATE_INTERVAL);
}
function stopAutoRotate() {
if (autoRotateTimer) {
clearInterval(autoRotateTimer);
autoRotateTimer = null;
}
isAutoRotating = false;
autoRotateBtn.classList.remove('active');
autoRotateBtn.querySelector('i').className = 'fa-solid fa-play text-xs';
autoRotateBtn.querySelector('span').textContent = 'Auto-rotation';
}
function toggleAutoRotate() {
isAutoRotating ? stopAutoRotate() : startAutoRotate();
}
// ============ GESTION DU DRAG ============
function getEventX(e) {
return e.touches ? e.touches[0].clientX : e.clientX;
}
function onDragStart(e) {
if (e.target.closest('button')) return;
e.preventDefault();
isDragging = true;
dragStartX = getEventX(e);
dragStartFrame = currentFrame;
if (isAutoRotating) stopAutoRotate();
wrapper.style.cursor = 'grabbing';
}
function onDragMove(e) {
if (!isDragging) return;
e.preventDefault();
const currentX = getEventX(e);
const deltaX = currentX - dragStartX;
const frameShift = Math.round(deltaX / DRAG_SENSITIVITY);
let newFrame = dragStartFrame + frameShift;
// Normalisation avec modulo
newFrame = ((newFrame - 1) % TOTAL_FRAMES + TOTAL_FRAMES) % TOTAL_FRAMES + 1;
if (newFrame !== currentFrame) {
updateUI(newFrame);
}
}
function onDragEnd(e) {
if (!isDragging) return;
isDragging = false;
wrapper.style.cursor = 'grab';
dragStartX = 0;
dragStartFrame = currentFrame;
}
// ============ ÉVÉNEMENTS ============
wrapper.addEventListener('mousedown', onDragStart);
window.addEventListener('mousemove', onDragMove);
window.addEventListener('mouseup', onDragEnd);
window.addEventListener('mouseleave', onDragEnd);
wrapper.addEventListener('touchstart', onDragStart, { passive: false });
window.addEventListener('touchmove', onDragMove, { passive: false });
window.addEventListener('touchend', onDragEnd);
window.addEventListener('touchcancel', onDragEnd);
// Empêcher le scroll pendant le drag sur mobile
document.addEventListener('touchmove', function(e) {
if (isDragging) e.preventDefault();
}, { passive: false });
// Boutons
rotateLeftBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (isAutoRotating) stopAutoRotate();
rotateLeft();
});
rotateRightBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (isAutoRotating) stopAutoRotate();
rotateRight();
});
autoRotateBtn.addEventListener('click', (e) => {
e.stopPropagation();
toggleAutoRotate();
});
// Raccourcis clavier
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
e.preventDefault();
if (isAutoRotating) stopAutoRotate();
rotateLeft();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
if (isAutoRotating) stopAutoRotate();
rotateRight();
} else if (e.key === ' ') {
e.preventDefault();
toggleAutoRotate();
}
});
// Curseur par défaut
wrapper.style.cursor = 'grab';
// Initialisation
updateUI(1);
// Nettoyage
window.addEventListener('beforeunload', () => {
stopAutoRotate();
});
console.log('🚀 ThreeSixtyView • Thème sombre activé •', TOTAL_FRAMES, 'frames');
})();
</script>
</body>
</html>
Télécharger le fichier source