Loading Animations & Progress Indicators
A comprehensive collection of modern loading animations and progress indicators with smooth transitions, customizable styles, and performance optimization
Responsive Design
Yes
Dark Mode Support
No
lines
11
Browser Compatibility
No
Live Preview
Interact with the component without leaving the page.
Loading Animations & Progress Indicators
A comprehensive collection of modern loading animations and progress indicators featuring smooth transitions, customizable styles, performance optimization, and accessibility features for enhanced user experience.
Features
- Multiple Animation Types: Spinners, bars, dots, waves, and custom shapes
- Smooth Transitions: CSS-based animations with hardware acceleration
- Progress Tracking: Real-time progress updates with percentage display
- Customizable Styles: Easy theming and color customization
- Performance Optimized: Lightweight animations with minimal CPU usage
- Accessibility: Screen reader support and reduced motion preferences
- Responsive Design: Adapts to all screen sizes and devices
- Modern Design: Clean, minimalist aesthetics with subtle effects
Demo
<div class="loading-container">
<div class="animation-section">
<h3>Spinner Animations</h3>
<div class="spinner-grid">
<div class="spinner-item">
<div class="spinner classic-spinner">
<div class="spinner-circle"></div>
</div>
<span class="spinner-label">Classic</span>
</div>
<div class="spinner-item">
<div class="spinner dots-spinner">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<span class="spinner-label">Dots</span>
</div>
<div class="spinner-item">
<div class="spinner pulse-spinner">
<div class="pulse-ring"></div>
<div class="pulse-ring"></div>
<div class="pulse-ring"></div>
</div>
<span class="spinner-label">Pulse</span>
</div>
<div class="spinner-item">
<div class="spinner wave-spinner">
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
</div>
<span class="spinner-label">Wave</span>
</div>
<div class="spinner-item">
<div class="spinner orbit-spinner">
<div class="orbit-center"></div>
<div class="orbit-path">
<div class="orbit-dot"></div>
</div>
</div>
<span class="spinner-label">Orbit</span>
</div>
<div class="spinner-item">
<div class="spinner square-spinner">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>
<span class="spinner-label">Square</span>
</div>
</div>
</div>
<div class="animation-section">
<h3>Progress Bars</h3>
<div class="progress-item">
<label class="progress-label">Linear Progress</label>
<div class="progress-bar linear-progress" data-progress="65">
<div class="progress-fill"></div>
<div class="progress-text">65%</div>
</div>
</div>
<div class="progress-item">
<label class="progress-label">Gradient Progress</label>
<div class="progress-bar gradient-progress" data-progress="80">
<div class="progress-fill"></div>
<div class="progress-text">80%</div>
</div>
</div>
<div class="progress-item">
<label class="progress-label">Animated Progress</label>
<div class="progress-bar animated-progress" data-progress="45">
<div class="progress-fill">
<div class="progress-shine"></div>
</div>
<div class="progress-text">45%</div>
</div>
</div>
<div class="progress-item">
<label class="progress-label">Step Progress</label>
<div class="step-progress">
<div class="step completed">
<div class="step-circle">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
</div>
<div class="step-label">Step 1</div>
</div>
<div class="step-connector completed"></div>
<div class="step completed">
<div class="step-circle">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
</div>
<div class="step-label">Step 2</div>
</div>
<div class="step-connector active"></div>
<div class="step active">
<div class="step-circle">
<span>3</span>
</div>
<div class="step-label">Step 3</div>
</div>
<div class="step-connector"></div>
<div class="step">
<div class="step-circle">
<span>4</span>
</div>
<div class="step-label">Step 4</div>
</div>
</div>
</div>
</div>
<div class="animation-section">
<h3>Circular Progress</h3>
<div class="circular-grid">
<div class="circular-item">
<div class="circular-progress" data-progress="75">
<svg class="circular-svg" viewBox="0 0 100 100">
<circle class="circular-bg" cx="50" cy="50" r="45"></circle>
<circle class="circular-fill" cx="50" cy="50" r="45"></circle>
</svg>
<div class="circular-text">75%</div>
</div>
<span class="circular-label">Basic</span>
</div>
<div class="circular-item">
<div class="circular-progress gradient-circular" data-progress="60">
<svg class="circular-svg" viewBox="0 0 100 100">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#3b82f6"/>
<stop offset="100%" stop-color="#1d4ed8"/>
</linearGradient>
</defs>
<circle class="circular-bg" cx="50" cy="50" r="45"></circle>
<circle class="circular-fill" cx="50" cy="50" r="45" stroke="url(#gradient1)"></circle>
</svg>
<div class="circular-text">60%</div>
</div>
<span class="circular-label">Gradient</span>
</div>
<div class="circular-item">
<div class="circular-progress multi-layer" data-progress="85">
<svg class="circular-svg" viewBox="0 0 100 100">
<circle class="circular-bg" cx="50" cy="50" r="40"></circle>
<circle class="circular-fill primary" cx="50" cy="50" r="40"></circle>
<circle class="circular-bg secondary" cx="50" cy="50" r="30"></circle>
<circle class="circular-fill secondary" cx="50" cy="50" r="30"></circle>
</svg>
<div class="circular-text">85%</div>
</div>
<span class="circular-label">Multi-layer</span>
</div>
</div>
</div>
<div class="animation-section">
<h3>Loading States</h3>
<div class="loading-states">
<button class="btn-loading" id="loadingBtn1">
<span class="btn-text">Submit</span>
<span class="btn-spinner">
<div class="mini-spinner"></div>
</span>
</button>
<button class="btn-loading success" id="loadingBtn2">
<span class="btn-text">Complete</span>
<span class="btn-success">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
</span>
</button>
<button class="btn-loading error" id="loadingBtn3">
<span class="btn-text">Error</span>
<span class="btn-error">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
</svg>
</span>
</button>
</div>
<div class="loading-card">
<div class="card-skeleton">
<div class="skeleton-avatar"></div>
<div class="skeleton-content">
<div class="skeleton-line long"></div>
<div class="skeleton-line medium"></div>
<div class="skeleton-line short"></div>
</div>
</div>
</div>
</div>
<div class="animation-section">
<h3>Interactive Demo</h3>
<div class="demo-controls">
<button class="demo-btn" data-action="start">Start Loading</button>
<button class="demo-btn" data-action="progress">Simulate Progress</button>
<button class="demo-btn" data-action="complete">Complete</button>
<button class="demo-btn" data-action="reset">Reset</button>
</div>
<div class="demo-progress">
<div class="progress-bar demo-bar" data-progress="0">
<div class="progress-fill"></div>
<div class="progress-text">0%</div>
</div>
<div class="demo-spinner" style="display: none;">
<div class="spinner classic-spinner">
<div class="spinner-circle"></div>
</div>
<span class="demo-status">Loading...</span>
</div>
</div>
</div>
</div>
.loading-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.animation-section {
margin-bottom: 3rem;
padding: 2rem;
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e7eb;
}
.animation-section h3 {
margin: 0 0 2rem 0;
font-size: 1.5rem;
font-weight: 700;
color: #1a1a1a;
text-align: center;
}.spinner-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 2rem;
justify-items: center;
}
.spinner-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.spinner {
width: 60px;
height: 60px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.spinner-label {
font-size: 0.875rem;
color: #6b7280;
font-weight: 500;
}.classic-spinner .spinner-circle {
width: 100%;
height: 100%;
border: 4px solid #e5e7eb;
border-top: 4px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}.dots-spinner {
display: flex;
gap: 8px;
}
.dots-spinner .dot {
width: 12px;
height: 12px;
background: #3b82f6;
border-radius: 50%;
animation: dotPulse 1.4s ease-in-out infinite both;
}
.dots-spinner .dot:nth-child(1) { animation-delay: -0.32s; }
.dots-spinner .dot:nth-child(2) { animation-delay: -0.16s; }
@keyframes dotPulse {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}.pulse-spinner {
position: relative;
}
.pulse-ring {
position: absolute;
width: 100%;
height: 100%;
border: 3px solid #3b82f6;
border-radius: 50%;
animation: pulseRing 2s ease-out infinite;
}
.pulse-ring:nth-child(2) { animation-delay: 0.5s; }
.pulse-ring:nth-child(3) { animation-delay: 1s; }
@keyframes pulseRing {
0% {
transform: scale(0);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}.wave-spinner {
display: flex;
align-items: flex-end;
gap: 4px;
height: 40px;
}
.wave-bar {
width: 6px;
background: #3b82f6;
border-radius: 3px;
animation: waveStretch 1.2s ease-in-out infinite;
}
.wave-bar:nth-child(1) { animation-delay: -1.2s; }
.wave-bar:nth-child(2) { animation-delay: -1.1s; }
.wave-bar:nth-child(3) { animation-delay: -1.0s; }
.wave-bar:nth-child(4) { animation-delay: -0.9s; }
.wave-bar:nth-child(5) { animation-delay: -0.8s; }
@keyframes waveStretch {
0%, 40%, 100% {
height: 10px;
}
20% {
height: 40px;
}
}.orbit-spinner {
position: relative;
}
.orbit-center {
width: 20px;
height: 20px;
background: #3b82f6;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.orbit-path {
width: 100%;
height: 100%;
border: 2px solid transparent;
border-radius: 50%;
position: relative;
animation: orbit 2s linear infinite;
}
.orbit-dot {
width: 12px;
height: 12px;
background: #1d4ed8;
border-radius: 50%;
position: absolute;
top: -6px;
left: 50%;
transform: translateX(-50%);
}
@keyframes orbit {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}.square-spinner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
}
.square-spinner .square {
width: 20px;
height: 20px;
background: #3b82f6;
border-radius: 2px;
animation: squarePulse 1.2s ease-in-out infinite;
}
.square-spinner .square:nth-child(1) { animation-delay: 0s; }
.square-spinner .square:nth-child(2) { animation-delay: 0.1s; }
.square-spinner .square:nth-child(3) { animation-delay: 0.2s; }
.square-spinner .square:nth-child(4) { animation-delay: 0.3s; }
@keyframes squarePulse {
0%, 70%, 100% {
transform: scale(1);
opacity: 1;
}
35% {
transform: scale(0.8);
opacity: 0.7;
}
}.progress-item {
margin-bottom: 2rem;
}
.progress-label {
display: block;
font-weight: 600;
color: #374151;
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
.progress-bar {
position: relative;
width: 100%;
height: 12px;
background: #e5e7eb;
border-radius: 6px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #3b82f6;
border-radius: 6px;
transition: width 0.3s ease;
position: relative;
}
.progress-text {
position: absolute;
top: 50%;
right: 8px;
transform: translateY(-50%);
font-size: 0.75rem;
font-weight: 600;
color: #374151;
}.gradient-progress .progress-fill {
background: linear-gradient(90deg, #3b82f6, #1d4ed8, #7c3aed);
}.animated-progress .progress-fill {
overflow: hidden;
}
.progress-shine {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.4),
transparent
);
animation: shine 2s ease-in-out infinite;
}
@keyframes shine {
0% { left: -100%; }
100% { left: 100%; }
}.step-progress {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 0;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 2;
}
.step-circle {
width: 40px;
height: 40px;
border-radius: 50%;
background: #e5e7eb;
color: #9ca3af;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.3s ease;
margin-bottom: 0.5rem;
}
.step.completed .step-circle {
background: #10b981;
color: white;
}
.step.active .step-circle {
background: #3b82f6;
color: white;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
}
.step-circle svg {
width: 20px;
height: 20px;
}
.step-label {
font-size: 0.75rem;
color: #6b7280;
font-weight: 500;
text-align: center;
}
.step.completed .step-label,
.step.active .step-label {
color: #374151;
font-weight: 600;
}
.step-connector {
flex: 1;
height: 2px;
background: #e5e7eb;
margin: 0 1rem;
position: relative;
z-index: 1;
}
.step-connector.completed {
background: #10b981;
}
.step-connector.active {
background: linear-gradient(90deg, #10b981 50%, #e5e7eb 50%);
}.circular-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 2rem;
justify-items: center;
}
.circular-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.circular-progress {
position: relative;
width: 120px;
height: 120px;
}
.circular-svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.circular-bg {
fill: none;
stroke: #e5e7eb;
stroke-width: 8;
}
.circular-fill {
fill: none;
stroke: #3b82f6;
stroke-width: 8;
stroke-linecap: round;
stroke-dasharray: 283;
stroke-dashoffset: 283;
transition: stroke-dashoffset 0.3s ease;
}
.circular-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.25rem;
font-weight: 700;
color: #374151;
}
.circular-label {
font-size: 0.875rem;
color: #6b7280;
font-weight: 500;
}.multi-layer .circular-fill.primary {
stroke: #3b82f6;
stroke-width: 6;
}
.multi-layer .circular-fill.secondary {
stroke: #10b981;
stroke-width: 4;
stroke-dasharray: 188;
stroke-dashoffset: 188;
}
.multi-layer .circular-bg.secondary {
stroke: #f3f4f6;
stroke-width: 4;
}.loading-states {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.btn-loading {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
min-width: 120px;
background: #3b82f6;
color: white;
}
.btn-loading:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.btn-loading.success {
background: #10b981;
}
.btn-loading.error {
background: #ef4444;
}
.btn-text,
.btn-spinner,
.btn-success,
.btn-error {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.btn-spinner,
.btn-success,
.btn-error {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
transform: translateY(100%);
}
.btn-loading.loading .btn-text {
opacity: 0;
transform: translateY(-100%);
}
.btn-loading.loading .btn-spinner {
opacity: 1;
transform: translateY(0);
}
.btn-loading.success .btn-text,
.btn-loading.success .btn-spinner {
opacity: 0;
transform: translateY(-100%);
}
.btn-loading.success .btn-success {
opacity: 1;
transform: translateY(0);
}
.btn-loading.error .btn-text,
.btn-loading.error .btn-spinner {
opacity: 0;
transform: translateY(-100%);
}
.btn-loading.error .btn-error {
opacity: 1;
transform: translateY(0);
}
.mini-spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.btn-success svg,
.btn-error svg {
width: 20px;
height: 20px;
}.loading-card {
max-width: 400px;
margin: 0 auto;
}
.card-skeleton {
padding: 1.5rem;
background: white;
border-radius: 12px;
border: 1px solid #e5e7eb;
display: flex;
gap: 1rem;
}
.skeleton-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
background-size: 200% 100%;
animation: shimmer 2s ease-in-out infinite;
flex-shrink: 0;
}
.skeleton-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.skeleton-line {
height: 16px;
border-radius: 8px;
background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
background-size: 200% 100%;
animation: shimmer 2s ease-in-out infinite;
}
.skeleton-line.long { width: 100%; }
.skeleton-line.medium { width: 75%; }
.skeleton-line.short { width: 50%; }
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}.demo-controls {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
justify-content: center;
}
.demo-btn {
padding: 0.5rem 1rem;
border: 2px solid #3b82f6;
background: white;
color: #3b82f6;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.demo-btn:hover {
background: #3b82f6;
color: white;
}
.demo-progress {
max-width: 500px;
margin: 0 auto;
}
.demo-bar {
margin-bottom: 2rem;
}
.demo-spinner {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.demo-status {
font-weight: 600;
color: #374151;
}.dark .animation-section {
background: #1f2937;
border-color: #374151;
}
.dark .animation-section h3 {
color: white;
}
.dark .spinner-label,
.dark .circular-label {
color: #d1d5db;
}
.dark .progress-bar {
background: #374151;
}
.dark .progress-text {
color: #e5e7eb;
}
.dark .step-circle {
background: #374151;
color: #d1d5db;
}
.dark .step-connector {
background: #374151;
}
.dark .circular-bg {
stroke: #374151;
}
.dark .circular-text {
color: #e5e7eb;
}
.dark .card-skeleton {
background: #1f2937;
border-color: #374151;
}
.dark .skeleton-avatar,
.dark .skeleton-line {
background: linear-gradient(90deg, #374151 25%, #4b5563 50%, #374151 75%);
background-size: 200% 100%;
}@media (max-width: 768px) {
.loading-container {
padding: 1rem;
}
.animation-section {
padding: 1.5rem;
}
.spinner-grid {
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 1.5rem;
}
.circular-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}
.step-progress {
flex-direction: column;
gap: 1rem;
}
.step-connector {
width: 2px;
height: 30px;
margin: 0;
}
.loading-states {
flex-direction: column;
align-items: center;
}
.demo-controls {
flex-direction: column;
align-items: center;
}
}
@media (max-width: 480px) {
.spinner {
width: 50px;
height: 50px;
}
.circular-progress {
width: 100px;
height: 100px;
}
.btn-loading {
min-width: 100px;
padding: 0.625rem 1.25rem;
}
}@media (prefers-reduced-motion: reduce) {
.spinner-circle,
.dot,
.pulse-ring,
.wave-bar,
.orbit-path,
.square,
.progress-shine,
.mini-spinner,
.skeleton-avatar,
.skeleton-line {
animation: none;
}
.circular-fill {
transition: none;
}
}
class LoadingAnimations {
constructor() {
this.progressBars = document.querySelectorAll('.progress-bar[data-progress]');
this.circularProgress = document.querySelectorAll('.circular-progress[data-progress]');
this.demoControls = document.querySelector('.demo-controls');
this.demoBar = document.querySelector('.demo-bar');
this.demoSpinner = document.querySelector('.demo-spinner');
this.demoStatus = document.querySelector('.demo-status');
this.init();
}
init() {
this.initializeProgressBars();
this.initializeCircularProgress();
this.setupDemoControls();
this.setupButtonStates();
this.observeElements();
}
initializeProgressBars() {
this.progressBars.forEach(bar => {
const progress = parseInt(bar.dataset.progress);
const fill = bar.querySelector('.progress-fill');
const text = bar.querySelector('.progress-text');
if (fill && text) {
setTimeout(() => {
fill.style.width = `${progress}%`;
this.animateNumber(text, 0, progress, 1000);
}, 100);
}
});
}
initializeCircularProgress() {
this.circularProgress.forEach(circle => {
const progress = parseInt(circle.dataset.progress);
const fill = circle.querySelector('.circular-fill');
const text = circle.querySelector('.circular-text');
if (fill && text) {
const circumference = 2 * Math.PI * 45; // radius = 45
const offset = circumference - (progress / 100) * circumference;
fill.style.strokeDasharray = circumference;
fill.style.strokeDashoffset = circumference;
setTimeout(() => {
fill.style.strokeDashoffset = offset;
this.animateNumber(text, 0, progress, 1000, '%');
}, 100);
if (circle.classList.contains('multi-layer')) {
const secondaryFill = circle.querySelector('.circular-fill.secondary');
if (secondaryFill) {
const secondaryCircumference = 2 * Math.PI * 30; // radius = 30
const secondaryProgress = Math.min(progress + 10, 100);
const secondaryOffset = secondaryCircumference - (secondaryProgress / 100) * secondaryCircumference;
secondaryFill.style.strokeDasharray = secondaryCircumference;
secondaryFill.style.strokeDashoffset = secondaryCircumference;
setTimeout(() => {
secondaryFill.style.strokeDashoffset = secondaryOffset;
}, 200);
}
}
}
});
}
animateNumber(element, start, end, duration, suffix = '%') {
const startTime = performance.now();
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const current = Math.floor(start + (end - start) * this.easeOutCubic(progress));
element.textContent = current + suffix;
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
}
easeOutCubic(t) {
return 1 - Math.pow(1 - t, 3);
}
setupDemoControls() {
if (!this.demoControls) return;
const buttons = this.demoControls.querySelectorAll('.demo-btn');
let currentProgress = 0;
let progressInterval;
buttons.forEach(button => {
button.addEventListener('click', () => {
const action = button.dataset.action;
switch (action) {
case 'start':
this.startDemo();
break;
case 'progress':
this.simulateProgress();
break;
case 'complete':
this.completeDemo();
break;
case 'reset':
this.resetDemo();
break;
}
});
});
}
startDemo() {
this.demoSpinner.style.display = 'flex';
this.demoStatus.textContent = 'Initializing...';
setTimeout(() => {
this.demoStatus.textContent = 'Loading...';
}, 1000);
}
simulateProgress() {
this.demoSpinner.style.display = 'none';
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
this.completeDemo();
}
this.updateDemoProgress(progress);
}, 300);
}
updateDemoProgress(progress) {
const fill = this.demoBar.querySelector('.progress-fill');
const text = this.demoBar.querySelector('.progress-text');
fill.style.width = `${progress}%`;
text.textContent = `${Math.floor(progress)}%`;
this.demoBar.dataset.progress = Math.floor(progress);
}
completeDemo() {
this.updateDemoProgress(100);
this.demoSpinner.style.display = 'none';
setTimeout(() => {
const fill = this.demoBar.querySelector('.progress-fill');
fill.style.background = '#10b981';
}, 500);
}
resetDemo() {
this.updateDemoProgress(0);
this.demoSpinner.style.display = 'none';
const fill = this.demoBar.querySelector('.progress-fill');
fill.style.background = '#3b82f6';
}
setupButtonStates() {
const loadingButtons = document.querySelectorAll('#loadingBtn1, #loadingBtn2, #loadingBtn3');
loadingButtons.forEach((button, index) => {
button.addEventListener('click', () => {
if (index === 0) {
this.simulateButtonLoading(button);
}
});
});
}
simulateButtonLoading(button) {
button.classList.add('loading');
button.disabled = true;
setTimeout(() => {
button.classList.remove('loading');
button.classList.add('success');
setTimeout(() => {
button.classList.remove('success');
button.disabled = false;
}, 2000);
}, 2000);
}
observeElements() {
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.animateElement(entry.target);
}
});
}, {
threshold: 0.1,
rootMargin: '50px'
});
const animatedElements = document.querySelectorAll(
'.progress-bar, .circular-progress, .step-progress'
);
animatedElements.forEach(el => observer.observe(el));
}
}
animateElement(element) {
if (element.classList.contains('progress-bar')) {
const progress = parseInt(element.dataset.progress);
const fill = element.querySelector('.progress-fill');
const text = element.querySelector('.progress-text');
if (fill && text && !element.classList.contains('animated')) {
element.classList.add('animated');
fill.style.width = '0%';
setTimeout(() => {
fill.style.width = `${progress}%`;
this.animateNumber(text, 0, progress, 1000);
}, 100);
}
}
if (element.classList.contains('circular-progress')) {
const progress = parseInt(element.dataset.progress);
const fill = element.querySelector('.circular-fill');
const text = element.querySelector('.circular-text');
if (fill && text && !element.classList.contains('animated')) {
element.classList.add('animated');
const circumference = 2 * Math.PI * 45;
const offset = circumference - (progress / 100) * circumference;
fill.style.strokeDashoffset = circumference;
setTimeout(() => {
fill.style.strokeDashoffset = offset;
this.animateNumber(text, 0, progress, 1000, '%');
}, 100);
}
}
}
setProgress(elementId, progress) {
const element = document.getElementById(elementId);
if (!element) return;
element.dataset.progress = progress;
if (element.classList.contains('progress-bar')) {
const fill = element.querySelector('.progress-fill');
const text = element.querySelector('.progress-text');
if (fill && text) {
fill.style.width = `${progress}%`;
text.textContent = `${progress}%`;
}
}
if (element.classList.contains('circular-progress')) {
const fill = element.querySelector('.circular-fill');
const text = element.querySelector('.circular-text');
if (fill && text) {
const circumference = 2 * Math.PI * 45;
const offset = circumference - (progress / 100) * circumference;
fill.style.strokeDashoffset = offset;
text.textContent = `${progress}%`;
}
}
}
showLoading(elementId) {
const element = document.getElementById(elementId);
if (element && element.classList.contains('btn-loading')) {
element.classList.add('loading');
element.disabled = true;
}
}
hideLoading(elementId, state = 'default') {
const element = document.getElementById(elementId);
if (element && element.classList.contains('btn-loading')) {
element.classList.remove('loading');
if (state === 'success') {
element.classList.add('success');
setTimeout(() => {
element.classList.remove('success');
element.disabled = false;
}, 2000);
} else if (state === 'error') {
element.classList.add('error');
setTimeout(() => {
element.classList.remove('error');
element.disabled = false;
}, 2000);
} else {
element.disabled = false;
}
}
}
createSpinner(type = 'classic', size = 'medium') {
const spinner = document.createElement('div');
spinner.className = `spinner ${type}-spinner ${size}`;
switch (type) {
case 'classic':
spinner.innerHTML = '<div class="spinner-circle"></div>';
break;
case 'dots':
spinner.innerHTML = '<div class="dot"></div><div class="dot"></div><div class="dot"></div>';
break;
case 'pulse':
spinner.innerHTML = '<div class="pulse-ring"></div><div class="pulse-ring"></div><div class="pulse-ring"></div>';
break;
default:
spinner.innerHTML = '<div class="spinner-circle"></div>';
}
return spinner;
}
}
document.addEventListener('DOMContentLoaded', () => {
new LoadingAnimations();
}); HTML
4
lines
CSS
7
lines
<div class="loading-container">
<h2>Loading Animations & Progress Indicators</h2>
<p>Modern loading animations and progress indicators</p>
</div>