Snippets Card Product - View 360 - Bootstrap 5

🏷️ Extraits & Composants HTML 📅 30/03/2026 18:00:00 👤 Mezgani said
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