Carte Produit - Design Épuré

🏷️ Extraits & Composants HTML 📅 06/04/2026 16:00:00 👤 Mezgani Said
Bootstrap 5 Product Card E Commerce Ui Design Responsive

Carte produit minimaliste avec image, prix, notation étoiles et bouton d'achat. Animation hover fluide et responsive.

<!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 Bootstrap 5 2026 042122 | AngularForAll</title>
<!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- Bootstrap Icons -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">

    <style>
        .product-viewer {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 20px;
            padding: 20px;
            cursor: grab;
            transition: transform 0.3s ease;
            min-height: 500px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .product-viewer:active {
            cursor: grabbing;
        }
        .feature-card {
            transition: all 0.3s ease;
            border: none;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }
        .feature-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(0,0,0,0.15);
        }
        .badge-offer {
            background: linear-gradient(135deg, #ff6b6b, #ee5a24);
            position: absolute;
            top: 20px;
            right: 20px;
            z-index: 10;
        }
        .price {
            font-size: 2rem;
            font-weight: bold;
            color: #2c3e8f;
        }
        .old-price {
            text-decoration: line-through;
            color: #999;
            font-size: 1.2rem;
        }
        .btn-buy {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border: none;
            padding: 12px 30px;
            font-weight: bold;
            transition: all 0.3s ease;
        }
        .btn-buy:hover {
            transform: scale(1.05);
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
        }
        .angle-indicator {
            background: rgba(0,0,0,0.7);
            border-radius: 20px;
            padding: 5px 15px;
            display: inline-block;
        }
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }
        .loading {
            animation: pulse 1s infinite;
        }
    </style>
</head>
<body class="bg-light">
    <div class="container py-5">
        <div class="row g-4">
            <!-- Colonne gauche - Visualisation produit -->
            <div class="col-lg-6">
                <div class="card border-0 shadow-lg overflow-hidden">
                    <div class="badge-offer">
                        <span class="badge bg-danger fs-6 m-2"><i class="bi bi-lightning-charge"></i> -10% OFF</span>
                    </div>

                    <div class="product-viewer" id="viewer3D">
                        <canvas id="phoneCanvas" style="width:100%; height:auto; max-width:500px;"></canvas>
                    </div>

                    <div class="p-4 bg-white">
                        <div class="d-flex justify-content-between align-items-center mb-3">
                            <div class="angle-indicator bg-dark text-white">
                                <i class="bi bi-arrow-repeat"></i> Angle: <span id="angleValue">0</span>° / 360°
                            </div>
                            <div class="btn-group" role="group">
                                <button class="btn btn-outline-primary" id="prevBtn" aria-label="Angle précédent">
                                    <i class="bi bi-chevron-left"></i>
                                </button>
                                <button class="btn btn-outline-primary" id="autoRotateBtn" aria-label="Rotation automatique">
                                    <i class="bi bi-play-fill"></i>
                                </button>
                                <button class="btn btn-outline-primary" id="nextBtn" aria-label="Angle suivant">
                                    <i class="bi bi-chevron-right"></i>
                                </button>
                            </div>
                        </div>

                        <input type="range" id="angleSlider" class="form-range" min="0" max="360" step="1" value="0">

                        <div class="alert alert-info mt-3 mb-0">
                            <i class="bi bi-info-circle"></i>
                            <small>Cliquez et glissez sur le téléphone pour le faire pivoter à 360°</small>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Colonne droite - Informations produit -->
            <div class="col-lg-6">
                <div class="card border-0 shadow-lg">
                    <div class="card-body p-4">
                        <h1 class="h2 mb-2">Samsung Galaxy A26 5G</h1>
                        <div class="mb-3">
                            <i class="bi bi-star-fill text-warning"></i>
                            <i class="bi bi-star-fill text-warning"></i>
                            <i class="bi bi-star-fill text-warning"></i>
                            <i class="bi bi-star-fill text-warning"></i>
                            <i class="bi bi-star-half text-warning"></i>
                            <span class="text-muted ms-2">(234 avis)</span>
                        </div>

                        <div class="mb-4">
                            <span class="price">349€</span>
                            <span class="old-price ms-2">399€</span>
                            <span class="badge bg-success ms-2">Économisez 50€</span>
                        </div>

                        <div class="mb-4">
                            <h6 class="fw-bold">Couleurs disponibles :</h6>
                            <div class="d-flex gap-2 mt-2">
                                <button aria-label="Bleu" class="btn btn-sm rounded-circle" style="width:40px;height:40px;background:#2c3e8f;" onclick="changeColor('blue')"></button>
                                <button aria-label="Noir" class="btn btn-sm rounded-circle" style="width:40px;height:40px;background:#000;" onclick="changeColor('black')"></button>
                                <button aria-label="Blanc" class="btn btn-sm rounded-circle" style="width:40px;height:40px;background:#fff;border:1px solid #ddd;" onclick="changeColor('white')"></button>
                            </div>
                        </div>

                        <div class="mb-4">
                            <h6 class="fw-bold">Capacité :</h6>
                            <div class="btn-group w-100" role="group">
                                <input type="radio" class="btn-check" name="storage" id="storage128" autocomplete="off" checked>
                                <label class="btn btn-outline-primary" for="storage128">128 Go</label>
                                <input type="radio" class="btn-check" name="storage" id="storage256" autocomplete="off">
                                <label class="btn btn-outline-primary" for="storage256">256 Go</label>
                            </div>
                        </div>

                        <button class="btn btn-buy btn-primary w-100 mb-3">
                            <i class="bi bi-cart-plus"></i> Ajouter au panier
                        </button>

                        <div class="row g-2 text-center">
                            <div class="col-4">
                                <div class="feature-card card p-2">
                                    <i class="bi bi-truck fs-2 text-primary"></i>
                                    <small>Livraison offerte</small>
                                </div>
                            </div>
                            <div class="col-4">
                                <div class="feature-card card p-2">
                                    <i class="bi bi-arrow-repeat fs-2 text-primary"></i>
                                    <small>14 jours retour</small>
                                </div>
                            </div>
                            <div class="col-4">
                                <div class="feature-card card p-2">
                                    <i class="bi bi-shield-check fs-2 text-primary"></i>
                                    <small>Garantie 2 ans</small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Caractéristiques techniques -->
                <div class="card border-0 shadow-lg mt-4">
                    <div class="card-body">
                        <h5 class="fw-bold mb-3"><i class="bi bi-gear"></i> Caractéristiques</h5>
                        <div class="row">
                            <div class="col-6 mb-2">
                                <small class="text-muted">Écran</small>
                                <div class="fw-bold">6.7" Super AMOLED</div>
                            </div>
                            <div class="col-6 mb-2">
                                <small class="text-muted">Processeur</small>
                                <div class="fw-bold">Exynos 1380</div>
                            </div>
                            <div class="col-6 mb-2">
                                <small class="text-muted">Appareil photo</small>
                                <div class="fw-bold">50 MP + OIS</div>
                            </div>
                            <div class="col-6 mb-2">
                                <small class="text-muted">Batterie</small>
                                <div class="fw-bold">5000 mAh</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // Configuration du canvas 3D
        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;

        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();
        }

        function drawPhone() {
            if (!ctx) return;

            const w = canvas.width;
            const h = canvas.height;

            ctx.clearRect(0, 0, w, h);

            const angleRad = (currentAngle * Math.PI) / 180;
            ctx.save();

            const centerX = w / 2;
            const centerY = h / 2;
            const phoneWidth = Math.min(w * 0.5, 250);
            const phoneHeight = phoneWidth * 2.1;
            const perspective = Math.cos(angleRad) * 0.3 + 0.7;
            const currentWidth = phoneWidth * perspective;
            const currentHeight = phoneHeight;

            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;

            const gradient = ctx.createLinearGradient(x, y, x + currentWidth, y + currentHeight);
            if (currentAngle < 90 || currentAngle > 270) {
                gradient.addColorStop(0, '#2c3e8f');
                gradient.addColorStop(0.5, '#3498db');
                gradient.addColorStop(1, '#2980b9');
            } else {
                gradient.addColorStop(0, '#1a252f');
                gradient.addColorStop(0.5, '#2c3e50');
                gradient.addColorStop(1, '#1a252f');
            }

            ctx.fillStyle = gradient;
            ctx.fillRect(x, y, currentWidth, currentHeight);
            ctx.restore();

            // Écran
            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;

                ctx.fillStyle = '#0a0e27';
                ctx.fillRect(screenX, screenY, screenWidth, screenHeight);

                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.restore();
            }

            drawPhone();
        }

        function updateAngle(angle) {
            currentAngle = angle;
            document.getElementById('angleValue').textContent = currentAngle;
            document.getElementById('angleSlider').value = currentAngle;
            drawPhone();
        }

        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);
        }

        function toggleAutoRotate() {
            if (autoRotateInterval) {
                clearInterval(autoRotateInterval);
                autoRotateInterval = null;
                document.getElementById('autoRotateBtn').innerHTML = '<i class="bi bi-play-fill"></i>';
            } else {
                autoRotateInterval = setInterval(nextAngle, 100);
                document.getElementById('autoRotateBtn').innerHTML = '<i class="bi bi-pause-fill"></i>';
            }
        }

        function changeColor(color) {
            console.log('Couleur changée:', color);
            // Ici vous pouvez changer la couleur du téléphone
        }

        // Événements
        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';
        }

        // Initialisation
        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));
        });

        resizeCanvas();
        updateAngle(0);

        // Ajout du changement de couleur
        window.changeColor = changeColor;
    </script>

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

Ouvrir cet aperçu dans un nouvel onglet du navigateur

🔗 Ouvrir dans le navigateur