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">
<!-- Spinner Animations -->
<div class="animation-section">
<h3>Spinner Animations</h3>
<div class="spinner-grid">
<!-- Classic Spinner -->
<div class="spinner-item">
<div class="spinner classic-spinner">
<div class="spinner-circle"></div>
</div>
<span class="spinner-label">Classic</span>
</div>
<!-- Dots Spinner -->
<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>
<!-- Pulse Spinner -->
<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>
<!-- Wave Spinner -->
<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>
<!-- Orbit Spinner -->
<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>
<!-- Square Spinner -->
<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>
<!-- Progress Bars -->
<div class="animation-section">
<h3>Progress Bars</h3>
<!-- Linear Progress Bar -->
<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>
<!-- Gradient Progress Bar -->
<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>
<!-- Animated Progress Bar -->
<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>
<!-- Step Progress -->
<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>
<!-- Circular Progress -->
<div class="animation-section">
<h3>Circular Progress</h3>
<div class="circular-grid">
<!-- Basic Circular Progress -->
<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>
<!-- Gradient Circular Progress -->
<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>
<!-- Multi-layer Circular Progress -->
<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>
<!-- Loading States -->
<div class="animation-section">
<h3>Loading States</h3>
<!-- Button Loading States -->
<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>
<!-- Card Loading State -->
<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>
<!-- Interactive Demo -->
<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 Animations */
.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 */
.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 */
.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 */
.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 */
.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 */
.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 */
.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 Bars */
.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 */
.gradient-progress .progress-fill {
background: linear-gradient(90deg, #3b82f6, #1d4ed8, #7c3aed);
}
/* Animated Progress */
.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 */
.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 Progress */
.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 */
.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 */
.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;
}
/* Card Loading State */
.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; }
}
/* Interactive Demo */
.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 Theme */
.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%;
}
/* Responsive Design */
@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;
}
}
/* Reduced Motion */
@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) {
// Animate on load
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;
// Animate on load
setTimeout(() => {
fill.style.strokeDashoffset = offset;
this.animateNumber(text, 0, progress, 1000, '%');
}, 100);
// Handle multi-layer
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';
// Show success state
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'
});
// Observe all animated elements
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);
}
}
}
// Public API
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;
}
}
// Initialize when DOM is ready
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>