Bootstrap 5
Card
Card Product
Ui
Snippets Html
Snippet de carte de produit avec visualisation 360° et design moderne en responsive avec 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>Snippet Product View360 2026 04201406 | AngularForAll</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.product-viewer {
background: white;
border-radius: 20px;
padding: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
width: 700px;
text-align: center;
}
h2 {
color: #2c3e8f;
margin-bottom: 5px;
}
.subtitle {
color: #666;
margin-bottom: 15px;
font-size: 14px;
}
.viewer-container {
position: relative;
margin-bottom: 20px;
background: linear-gradient(135deg, #e0e0e0 0%, #f5f5f5 100%);
border-radius: 15px;
overflow: hidden;
box-shadow: inset 0 0 20px rgba(0,0,0,0.1), 0 5px 15px rgba(0,0,0,0.2);
min-height: 450px;
display: flex;
align-items: center;
justify-content: center;
}
#productImage {
max-width: 100%;
height: auto;
display: block;
transition: transform 0.2s ease;
cursor: grab;
pointer-events: auto;
}
#productImage:active {
cursor: grabbing;
}
.controls {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 15px;
flex-wrap: wrap;
}
button {
background: #2c3e8f;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
font-weight: bold;
}
button:hover {
background: #1e2a5e;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
button:active {
transform: translateY(0);
}
.angle-indicator {
margin-top: 15px;
color: #666;
font-size: 14px;
font-weight: bold;
}
input[type="range"] {
width: 80%;
margin-top: 10px;
cursor: pointer;
height: 5px;
border-radius: 5px;
background: #ddd;
}
input[type="range"]:focus {
outline: none;
}
.instruction {
margin-top: 15px;
color: #999;
font-size: 12px;
background: #f9f9f9;
padding: 8px;
border-radius: 8px;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.loading {
position: absolute;
color: #666;
font-size: 14px;
animation: pulse 1s infinite;
}
</style>
</head>
<body>
<div class="product-viewer">
<h2>Samsung Galaxy A26 5G</h2>
<div class="subtitle">✨ Visualisation 360° ✨</div>
<div class="viewer-container">
<canvas id="phoneCanvas" width="500" height="400" style="width:100%; height:auto; cursor:grab;"></canvas>
</div>
<div class="controls">
<button id="prevBtn">◄ Angle précédent</button>
<button id="autoRotateBtn">▶ Rotation auto</button>
<button id="nextBtn">Angle suivant ►</button>
</div>
<div class="angle-indicator">
🔄 Angle: <span id="angleValue">0</span>° / 360°
</div>
<input type="range" id="angleSlider" min="0" max="360" step="1" value="0">
<div class="instruction">
💡 Astuce : Cliquez et glissez sur le téléphone pour le faire pivoter !
Ou utilisez les boutons pour une rotation précise.
</div>
</div>
<script>
// Configuration du canvas pour dessiner le téléphone en 3D réaliste
const canvas = document.getElementById('phoneCanvas');
const ctx = canvas.getContext('2d');
let currentAngle = 0;
let autoRotateInterval = null;
let isDragging = false;
let dragStartX = 0;
let dragStartAngle = 0;
// Définir la taille réelle du canvas
function resizeCanvas() {
const container = canvas.parentElement;
const size = Math.min(container.clientWidth, 500);
canvas.width = size;
canvas.height = size * 0.8;
canvas.style.width = '100%';
canvas.style.height = 'auto';
drawPhone();
}
// Dessiner le Samsung Galaxy A26 5G sous différents angles
function drawPhone() {
if (!ctx) return;
const w = canvas.width;
const h = canvas.height;
ctx.clearRect(0, 0, w, h);
// Calculer la rotation en radians
const angleRad = (currentAngle * Math.PI) / 180;
// Sauvegarder le contexte
ctx.save();
// Centre du canvas
const centerX = w / 2;
const centerY = h / 2;
// Taille du téléphone (sera réduite sur les côtés pour l'effet 3D)
const phoneWidth = Math.min(w * 0.5, 250);
const phoneHeight = phoneWidth * 2.1; // Ratio téléphone ~2:1
// Effet de perspective (le téléphone devient plus étroit sur les côtés)
const perspective = Math.cos(angleRad) * 0.3 + 0.7;
const currentWidth = phoneWidth * perspective;
const currentHeight = phoneHeight;
// Calculer la position
const x = centerX - currentWidth / 2;
const y = centerY - currentHeight / 2;
// === Corps du téléphone ===
ctx.save();
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// Dégradé pour le corps (couleur Samsung)
const gradient = ctx.createLinearGradient(x, y, x + currentWidth, y + currentHeight);
if (currentAngle < 90 || currentAngle > 270) {
// Face avant
gradient.addColorStop(0, '#2c3e8f');
gradient.addColorStop(0.5, '#3498db');
gradient.addColorStop(1, '#2980b9');
} else {
// Face arrière
gradient.addColorStop(0, '#1a252f');
gradient.addColorStop(0.5, '#2c3e50');
gradient.addColorStop(1, '#1a252f');
}
ctx.fillStyle = gradient;
ctx.fillRect(x, y, currentWidth, currentHeight);
// Bords arrondis (effet)
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 2;
ctx.strokeRect(x, y, currentWidth, currentHeight);
ctx.restore();
// === Écran (uniquement visible de face) ===
if (currentAngle > 270 || currentAngle < 90) {
ctx.save();
const screenMargin = currentWidth * 0.08;
const screenWidth = currentWidth - (screenMargin * 2);
const screenHeight = currentHeight - (screenMargin * 2.5);
const screenX = x + screenMargin;
const screenY = y + screenMargin;
// Écran avec dégradé
const screenGradient = ctx.createLinearGradient(screenX, screenY, screenX + screenWidth, screenY + screenHeight);
screenGradient.addColorStop(0, '#0a0e27');
screenGradient.addColorStop(1, '#1a1f3a');
ctx.fillStyle = screenGradient;
ctx.fillRect(screenX, screenY, screenWidth, screenHeight);
// Contenu de l'écran (simulation)
ctx.fillStyle = '#00ff88';
ctx.font = `bold ${Math.max(12, screenWidth * 0.08)}px Arial`;
ctx.textAlign = 'center';
ctx.fillText('SAMSUNG', screenX + screenWidth/2, screenY + screenHeight/3);
ctx.fillStyle = '#ffffff';
ctx.font = `${Math.max(10, screenWidth * 0.05)}px Arial`;
ctx.fillText('Galaxy A26', screenX + screenWidth/2, screenY + screenHeight/2);
ctx.fillStyle = '#00aaff';
ctx.font = `${Math.max(8, screenWidth * 0.04)}px Arial`;
ctx.fillText('5G', screenX + screenWidth/2, screenY + screenHeight * 0.7);
ctx.restore();
}
// === Caméras arrière (visible de dos) ===
if (currentAngle > 90 && currentAngle < 270) {
ctx.save();
const cameraY = y + currentHeight * 0.25;
const cameraSize = currentWidth * 0.1;
// Module caméra vertical
ctx.fillStyle = '#111';
ctx.shadowBlur = 5;
ctx.fillRect(x + currentWidth * 0.35, cameraY, currentWidth * 0.3, cameraSize * 2.5);
// Lentilles
ctx.fillStyle = '#333';
ctx.beginPath();
ctx.arc(x + currentWidth * 0.45, cameraY + cameraSize * 0.8, cameraSize * 0.35, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#222';
ctx.beginPath();
ctx.arc(x + currentWidth * 0.55, cameraY + cameraSize * 0.8, cameraSize * 0.35, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#444';
ctx.beginPath();
ctx.arc(x + currentWidth * 0.5, cameraY + cameraSize * 1.8, cameraSize * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// === Caméra selfie (toujours visible de face) ===
if (currentAngle > 270 || currentAngle < 90) {
ctx.save();
const selfieX = x + currentWidth * 0.85;
const selfieY = y + currentHeight * 0.05;
const selfieSize = currentWidth * 0.06;
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(selfieX, selfieY, selfieSize, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#1a1a2e';
ctx.beginPath();
ctx.arc(selfieX, selfieY, selfieSize * 0.6, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// === Boutons latéraux (effet 3D selon l'angle) ===
ctx.save();
ctx.fillStyle = '#888';
ctx.shadowBlur = 2;
if (currentAngle > 30 && currentAngle < 150) {
// Bord droit visible
const buttonY = y + currentHeight * 0.3;
ctx.fillRect(x + currentWidth - 3, buttonY, 5, currentHeight * 0.15);
ctx.fillRect(x + currentWidth - 3, buttonY + currentHeight * 0.2, 5, currentHeight * 0.15);
} else if (currentAngle > 210 && currentAngle < 330) {
// Bord gauche visible
const buttonY = y + currentHeight * 0.3;
ctx.fillRect(x - 2, buttonY, 5, currentHeight * 0.15);
ctx.fillRect(x - 2, buttonY + currentHeight * 0.2, 5, currentHeight * 0.15);
}
ctx.restore();
// Texte de marque
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.font = `${Math.max(8, currentWidth * 0.08)}px Arial`;
ctx.textAlign = 'center';
ctx.fillText('SAMSUNG', centerX, y + currentHeight - 10);
}
// Mettre à jour l'affichage
function updateAngle(angle) {
currentAngle = angle;
document.getElementById('angleValue').textContent = currentAngle;
document.getElementById('angleSlider').value = currentAngle;
drawPhone();
}
// Changer l'angle
function nextAngle() {
let newAngle = currentAngle + 10;
if (newAngle >= 360) newAngle = 0;
updateAngle(newAngle);
}
function prevAngle() {
let newAngle = currentAngle - 10;
if (newAngle < 0) newAngle = 350;
updateAngle(newAngle);
}
// Rotation automatique
function toggleAutoRotate() {
if (autoRotateInterval) {
clearInterval(autoRotateInterval);
autoRotateInterval = null;
document.getElementById('autoRotateBtn').innerHTML = '▶ Rotation auto';
document.getElementById('autoRotateBtn').style.background = '#2c3e8f';
} else {
autoRotateInterval = setInterval(() => {
let newAngle = currentAngle + 5;
if (newAngle >= 360) newAngle = 0;
updateAngle(newAngle);
}, 50);
document.getElementById('autoRotateBtn').innerHTML = '⏸ Pause';
document.getElementById('autoRotateBtn').style.background = '#e74c3c';
}
}
// Gestion du glisser pour rotation
function handleMouseDown(e) {
isDragging = true;
dragStartX = e.clientX;
dragStartAngle = currentAngle;
canvas.style.cursor = 'grabbing';
if (autoRotateInterval) toggleAutoRotate();
}
function handleMouseMove(e) {
if (!isDragging) return;
const deltaX = e.clientX - dragStartX;
const angleChange = (deltaX / canvas.width) * 180;
let newAngle = dragStartAngle + angleChange;
newAngle = ((newAngle % 360) + 360) % 360;
updateAngle(Math.round(newAngle));
}
function handleMouseUp() {
isDragging = false;
canvas.style.cursor = 'grab';
}
// Initialiser les événements
function init() {
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
canvas.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);
canvas.style.cursor = 'grab';
document.getElementById('prevBtn').addEventListener('click', () => {
if (autoRotateInterval) toggleAutoRotate();
prevAngle();
});
document.getElementById('nextBtn').addEventListener('click', () => {
if (autoRotateInterval) toggleAutoRotate();
nextAngle();
});
document.getElementById('autoRotateBtn').addEventListener('click', toggleAutoRotate);
document.getElementById('angleSlider').addEventListener('input', (e) => {
if (autoRotateInterval) toggleAutoRotate();
updateAngle(parseInt(e.target.value));
});
// Angle initial
updateAngle(0);
}
// Démarrer
init();
</script>
</body>
</html>
Ouvrir cet aperçu dans un nouvel onglet du navigateur
🔗 Ouvrir dans le navigateur