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