Loader Animé - Skeleton Screen

🏷️ Extraits & Composants HTML 📅 07/04/2026 15:00:00 👤 Mezgani Said
Bootstrap 5 Loader Skeleton Shimmer Effect Ui Component

Loader avec effet skeleton screen et animation shimmer. Améliore l'expérience utilisateur pendant le chargement des données.

<!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 Loader Bootstrap 5 2026 042155 | 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>
        body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        /* Loader Text Animation */
        .loader-text {
            display: inline-block;
            position: relative;
        }

        .dot-loader {
            display: inline-flex;
            gap: 4px;
            align-items: center;
        }

        .dot-loader span {
            width: 8px;
            height: 8px;
            background: #667eea;
            border-radius: 50%;
            display: inline-block;
            animation: dotBounce 1.4s infinite ease-in-out both;
        }

        .dot-loader span:nth-child(1) { animation-delay: -0.32s; }
        .dot-loader span:nth-child(2) { animation-delay: -0.16s; }
        .dot-loader span:nth-child(3) { animation-delay: 0s; }

        @keyframes dotBounce {
            0%, 80%, 100% { transform: scale(0); opacity: 0.3; }
            40% { transform: scale(1); opacity: 1; }
        }

        /* Skeleton Loader pour Image */
        .skeleton-image {
            background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
            background-size: 200% 100%;
            animation: shimmer 1.5s infinite;
            border-radius: 10px;
        }

        @keyframes shimmer {
            0% { background-position: 200% 0; }
            100% { background-position: -200% 0; }
        }

        /* Loader Video */
        .video-loader {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 10;
        }

        .spinner-video {
            width: 50px;
            height: 50px;
            border: 4px solid rgba(255,255,255,0.3);
            border-top: 4px solid #667eea;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Loader Card */
        .card-loader {
            background: white;
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }

        .card-skeleton {
            padding: 20px;
        }

        .skeleton-title {
            height: 24px;
            width: 70%;
            background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
            background-size: 200% 100%;
            animation: shimmer 1.5s infinite;
            border-radius: 5px;
            margin-bottom: 15px;
        }

        .skeleton-text {
            height: 14px;
            width: 100%;
            background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
            background-size: 200% 100%;
            animation: shimmer 1.5s infinite;
            border-radius: 5px;
            margin-bottom: 10px;
        }

        .skeleton-text.short {
            width: 60%;
        }

        .skeleton-avatar {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
            background-size: 200% 100%;
            animation: shimmer 1.5s infinite;
        }

        /* Loader bouton */
        .btn-loader {
            position: relative;
            pointer-events: none;
        }

        .btn-loader .btn-text {
            opacity: 0;
        }

        .btn-loader .spinner-border {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        /* Animation fade in */
        .fade-in {
            animation: fadeIn 0.5s ease-in;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }

        /* Conteneur de démonstration */
        .demo-container {
            background: white;
            border-radius: 20px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
        }

        .demo-container:hover {
            transform: translateY(-5px);
            box-shadow: 0 15px 40px rgba(0,0,0,0.25);
        }

        .badge-loader {
            background: #667eea;
            color: white;
            padding: 5px 12px;
            border-radius: 20px;
            font-size: 12px;
        }

        /* Conteneur vidéo */
        .video-wrapper {
            position: relative;
            background: #000;
            border-radius: 10px;
            overflow: hidden;
        }

        video {
            width: 100%;
            border-radius: 10px;
        }
    </style>
</head>
<body>
    <div class="container py-5">

        <!-- En-tête -->
        <div class="text-center mb-5">
            <h1 class="text-white display-4 fw-bold mb-2">
                <i class="bi bi-arrow-repeat"></i> Loaders Animés
            </h1>
            <p class="text-white-50 fs-5">Découvrez 4 types de loaders différents</p>
        </div>

        <div class="row g-4">

            <!-- ============================================ -->
            <!-- 1. LOADER TEXT -->
            <!-- ============================================ -->
            <div class="col-lg-6">
                <div class="demo-container">
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <h3 class="h5 mb-0">
                            <i class="bi bi-chat-text text-primary"></i> Loader Text
                        </h3>
                        <span class="badge-loader">Animation points</span>
                    </div>

                    <div class="bg-light rounded p-4 text-center">
                        <!-- Exemple 1 : Points qui dansent -->
                        <div class="mb-4">
                            <p class="text-muted mb-2">Chargement en cours</p>
                            <div class="dot-loader">
                                <span></span>
                                <span></span>
                                <span></span>
                            </div>
                        </div>

                        <!-- Exemple 2 : Texte avec effet tape -->
                        <div class="mb-3">
                            <div id="typingText" class="loader-text fs-5 fw-semibold text-primary"></div>
                        </div>

                        <!-- Bouton de contrôle -->
                        <button id="toggleTextLoader" class="btn btn-sm btn-outline-primary mt-2">
                            <i class="bi bi-play-circle"></i> Simuler chargement
                        </button>
                    </div>

                    <div class="mt-3 small text-muted">
                        <i class="bi bi-code-square"></i> Animation de texte avec points et effet machine à écrire
                    </div>
                </div>
            </div>

            <!-- ============================================ -->
            <!-- 2. LOADER IMAGE -->
            <!-- ============================================ -->
            <div class="col-lg-6">
                <div class="demo-container">
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <h3 class="h5 mb-0">
                            <i class="bi bi-image text-success"></i> Loader Image
                        </h3>
                        <span class="badge-loader">Skeleton + Shimmer</span>
                    </div>

                    <div class="bg-light rounded p-4 text-center">
                        <div id="imageContainer" style="min-height: 200px;">
                            <!-- Skeleton loader -->
                            <div id="imageSkeleton" class="skeleton-image" style="width: 100%; height: 200px;"></div>
                            <!-- Image cachée au début -->
                            <img id="loadedImage" src="" alt="Image chargée" class="img-fluid rounded" style="display: none; width: 100%;">
                        </div>

                        <button id="loadImageBtn" class="btn btn-sm btn-outline-success mt-3">
                            <i class="bi bi-cloud-download"></i> Charger l'image
                        </button>
                        <button id="resetImageBtn" class="btn btn-sm btn-outline-secondary mt-3">
                            <i class="bi bi-arrow-repeat"></i> Réinitialiser
                        </button>
                    </div>

                    <div class="mt-3 small text-muted">
                        <i class="bi bi-code-square"></i> Skeleton loader avec effet shimmer
                    </div>
                </div>
            </div>

            <!-- ============================================ -->
            <!-- 3. LOADER VIDEO -->
            <!-- ============================================ -->
            <div class="col-lg-6">
                <div class="demo-container">
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <h3 class="h5 mb-0">
                            <i class="bi bi-camera-reels text-danger"></i> Loader Video
                        </h3>
                        <span class="badge-loader">Spinner + Buffering</span>
                    </div>

                    <div class="bg-light rounded p-3">
                        <div class="video-wrapper">
                            <div id="videoLoader" class="video-loader" style="display: none;">
                                <div class="spinner-video"></div>
                                <p class="text-white mt-2 small">Chargement...</p>
                            </div>
                            <video id="demoVideo" controls preload="none" style="width: 100%;">
                                <source src="public/video-robot.webm" type="video/webm">
                                <source src="public/video-robot.mp4" type="video/mp4">
                                Votre navigateur ne supporte pas la vidéo.
                            </video>
                        </div>

                        <div class="mt-3 text-center">
                            <button id="loadVideoBtn" class="btn btn-sm btn-outline-danger">
                                <i class="bi bi-play-circle"></i> Charger la vidéo
                            </button>
                            <button id="resetVideoBtn" class="btn btn-sm btn-outline-secondary">
                                <i class="bi bi-arrow-repeat"></i> Réinitialiser
                            </button>
                        </div>
                    </div>

                    <div class="mt-3 small text-muted">
                        <i class="bi bi-code-square"></i> Loader personnalisé pendant le chargement de la vidéo
                    </div>
                </div>
            </div>

            <!-- ============================================ -->
            <!-- 4. LOADER CARD -->
            <!-- ============================================ -->
            <div class="col-lg-6">
                <div class="demo-container">
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <h3 class="h5 mb-0">
                            <i class="bi bi-card-list text-warning"></i> Loader Card
                        </h3>
                        <span class="badge-loader">Skeleton Card</span>
                    </div>

                    <div class="bg-light rounded p-3">
                        <div id="cardLoaderContainer">
                            <!-- Skeleton Card -->
                            <div id="cardSkeleton" class="card-loader">
                                <div class="skeleton-image" style="height: 150px;"></div>
                                <div class="card-skeleton">
                                    <div class="d-flex align-items-center gap-3 mb-3">
                                        <div class="skeleton-avatar"></div>
                                        <div>
                                            <div class="skeleton-title" style="width: 120px;"></div>
                                            <div class="skeleton-text" style="width: 80px;"></div>
                                        </div>
                                    </div>
                                    <div class="skeleton-text"></div>
                                    <div class="skeleton-text"></div>
                                    <div class="skeleton-text short"></div>
                                    <div class="mt-3">
                                        <div class="skeleton-text" style="width: 100px; height: 35px;"></div>
                                    </div>
                                </div>
                            </div>

                            <!-- Carte réelle cachée -->
                            <div id="realCard" style="display: none;">
                                <div class="card-loader border-0">
                                    <img src="public/mosquee.jpeg" class="w-100" style="height: 150px; object-fit: cover;" alt="Article">
                                    <div class="card-skeleton">
                                        <div class="d-flex align-items-center gap-3 mb-3">
                                            <img src="public/avatar.jpeg" class="rounded-circle" width="60" height="60" alt="Avatar">
                                            <div>
                                                <h5 class="mb-0">John Doe</h5>
                                                <small class="text-muted">Développeur Web</small>
                                            </div>
                                        </div>
                                        <p class="mb-1">Découvrez les dernières tendances du développement web en 2024.</p>
                                        <p class="mb-1">Les animations CSS et JavaScript transforment l'expérience utilisateur.</p>
                                        <button class="btn btn-primary btn-sm mt-2">Lire l'article</button>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="text-center mt-3">
                            <button id="loadCardBtn" class="btn btn-sm btn-outline-warning">
                                <i class="bi bi-cloud-download"></i> Charger la carte
                            </button>
                            <button id="resetCardBtn" class="btn btn-sm btn-outline-secondary">
                                <i class="bi bi-arrow-repeat"></i> Réinitialiser
                            </button>
                        </div>
                    </div>

                    <div class="mt-3 small text-muted">
                        <i class="bi bi-code-square"></i> Skeleton loader pour carte avec avatar, titre et contenu
                    </div>
                </div>
            </div>
        </div>

        <!-- Section explication -->
        <div class="row mt-5">
            <div class="col-12">
                <div class="alert alert-dark">
                    <h5 class="alert-heading">
                        <i class="bi bi-info-circle-fill"></i> Types de loaders utilisés :
                    </h5>
                    <div class="row mt-3">
                        <div class="col-md-3">
                            <strong>📝 Loader Text</strong><br>
                            <small>Points animés + effet machine à écrire</small>
                        </div>
                        <div class="col-md-3">
                            <strong>🖼️ Loader Image</strong><br>
                            <small>Skeleton + effet shimmer</small>
                        </div>
                        <div class="col-md-3">
                            <strong>🎬 Loader Video</strong><br>
                            <small>Spinner CSS personnalisé</small>
                        </div>
                        <div class="col-md-3">
                            <strong>🃏 Loader Card</strong><br>
                            <small>Skeleton complet (image, avatar, texte)</small>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // ============================================
        // 1. LOADER TEXT - Effet machine à écrire
        // ============================================
        const typingText = document.getElementById('typingText');
        const toggleTextLoader = document.getElementById('toggleTextLoader');
        let textInterval = null;

        function startTypingAnimation() {
            if (textInterval) clearInterval(textInterval);
            const messages = ['Chargement', 'Chargement.', 'Chargement..', 'Chargement...'];
            let index = 0;
            textInterval = setInterval(() => {
                typingText.innerHTML = messages[index % messages.length];
                index++;
            }, 500);
        }

        function stopTypingAnimation() {
            if (textInterval) {
                clearInterval(textInterval);
                textInterval = null;
            }
            typingText.innerHTML = 'Prêt !';
            setTimeout(() => {
                if (!textInterval) {
                    typingText.innerHTML = '';
                }
            }, 1500);
        }

        let textLoading = false;
        toggleTextLoader.addEventListener('click', () => {
            if (!textLoading) {
                startTypingAnimation();
                toggleTextLoader.innerHTML = '<i class="bi bi-stop-circle"></i> Arrêter';
                textLoading = true;
            } else {
                stopTypingAnimation();
                toggleTextLoader.innerHTML = '<i class="bi bi-play-circle"></i> Simuler chargement';
                textLoading = false;
            }
        });

        // ============================================
        // 2. LOADER IMAGE
        // ============================================
        const imageSkeleton = document.getElementById('imageSkeleton');
        const loadedImage = document.getElementById('loadedImage');
        const loadImageBtn = document.getElementById('loadImageBtn');
        const resetImageBtn = document.getElementById('resetImageBtn');

        loadImageBtn.addEventListener('click', () => {
            // Afficher le skeleton
            imageSkeleton.style.display = 'block';
            loadedImage.style.display = 'none';

            // Simuler un délai de chargement
            setTimeout(() => {
                // Image aléatoire
                const randomId = Math.floor(Math.random() * 200);
                loadedImage.src = `public/mosquee.jpeg`;
                loadedImage.onload = () => {
                    imageSkeleton.style.display = 'none';
                    loadedImage.style.display = 'block';
                    loadedImage.classList.add('fade-in');
                };
            }, 2000);
        });

        resetImageBtn.addEventListener('click', () => {
            imageSkeleton.style.display = 'block';
            loadedImage.style.display = 'none';
            loadedImage.src = '';
        });

        // ============================================
        // 3. LOADER VIDEO
        // ============================================
        const video = document.getElementById('demoVideo');
        const videoLoader = document.getElementById('videoLoader');
        const loadVideoBtn = document.getElementById('loadVideoBtn');
        const resetVideoBtn = document.getElementById('resetVideoBtn');

        loadVideoBtn.addEventListener('click', () => {
            videoLoader.style.display = 'block';
            video.load();

            video.addEventListener('canplay', function onCanPlay() {
                videoLoader.style.display = 'none';
                video.removeEventListener('canplay', onCanPlay);
            });

            video.addEventListener('play', () => {
                videoLoader.style.display = 'none';
            });
        });

        resetVideoBtn.addEventListener('click', () => {
            video.pause();
            video.src = '';
            videoLoader.style.display = 'none';
        });

        // ============================================
        // 4. LOADER CARD
        // ============================================
        const cardSkeleton = document.getElementById('cardSkeleton');
        const realCard = document.getElementById('realCard');
        const loadCardBtn = document.getElementById('loadCardBtn');
        const resetCardBtn = document.getElementById('resetCardBtn');

        loadCardBtn.addEventListener('click', () => {
            cardSkeleton.style.display = 'block';
            realCard.style.display = 'none';

            setTimeout(() => {
                cardSkeleton.style.display = 'none';
                realCard.style.display = 'block';
                realCard.classList.add('fade-in');
            }, 2000);
        });

        resetCardBtn.addEventListener('click', () => {
            cardSkeleton.style.display = 'block';
            realCard.style.display = 'none';
        });

        // Initialisation
        console.log('Page des loaders chargée !');
    </script>

    <!-- Bootstrap JS -->
    <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