<div class="progress-circle-container">
<div class="progress-circle-demo">
<div class="demo-header">
<h2>Circular Progress Indicators</h2>
<p>Animated progress circles with customizable themes</p>
</div>
<div class="circles-wrapper">
<div class="circle-item">
<div class="progress-circle" id="progressCircle1">
<svg viewBox="0 0 100 100" class="progress-ring">
<circle class="progress-ring-bg" cx="50" cy="50" r="45" fill="none" stroke="#f1f5f9" stroke-width="8"></circle>
<circle class="progress-ring-fill" cx="50" cy="50" r="45" fill="none" stroke="#667eea" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" stroke-linecap="round"></circle>
</svg>
<div class="progress-text">
<span class="progress-value" id="progressValue1">0</span>
<span class="progress-percent">%</span>
</div>
</div>
<div class="circle-label">Project Completion</div>
</div>
<div class="circle-item">
<div class="progress-circle success" id="progressCircle2">
<svg viewBox="0 0 100 100" class="progress-ring">
<circle class="progress-ring-bg" cx="50" cy="50" r="45" fill="none" stroke="#f1f5f9" stroke-width="8"></circle>
<circle class="progress-ring-fill" cx="50" cy="50" r="45" fill="none" stroke="#27ae60" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" stroke-linecap="round"></circle>
</svg>
<div class="progress-text">
<span class="progress-value" id="progressValue2">0</span>
<span class="progress-percent">%</span>
</div>
</div>
<div class="circle-label">Task Success</div>
</div>
<div class="circle-item">
<div class="progress-circle warning" id="progressCircle3">
<svg viewBox="0 0 100 100" class="progress-ring">
<circle class="progress-ring-bg" cx="50" cy="50" r="45" fill="none" stroke="#f1f5f9" stroke-width="8"></circle>
<circle class="progress-ring-fill" cx="50" cy="50" r="45" fill="none" stroke="#f39c12" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" stroke-linecap="round"></circle>
</svg>
<div class="progress-text">
<span class="progress-value" id="progressValue3">0</span>
<span class="progress-percent">%</span>
</div>
</div>
<div class="circle-label">Resource Usage</div>
</div>
</div>
<div class="controls-wrapper">
<button class="btn btn-primary" id="startAnimationBtn">Start Animation</button>
<button class="btn btn-secondary" id="resetAnimationBtn">Reset</button>
</div>
</div>
</div>
.progress-circle-container {
background: linear-gradient(135deg, #f8f9ff 0%, #ffffff 100%);
padding: 40px 20px;
border-radius: 20px;
max-width: 800px;
margin: 0 auto;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.05);
}
.progress-circle-demo {
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.03);
}
.demo-header {
text-align: center;
margin-bottom: 30px;
}
.demo-header h2 {
margin: 0 0 10px 0;
color: #333;
font-size: 2rem;
font-weight: 700;
}
.demo-header p {
color: #666;
font-size: 1.1rem;
margin: 0;
}
.circles-wrapper {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 40px;
margin-bottom: 30px;
}
.circle-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.progress-circle {
position: relative;
width: 150px;
height: 150px;
animation: fadeIn 0.6s ease-out;
}
.progress-circle:nth-child(1) { animation-delay: 0.1s; }
.progress-circle:nth-child(2) { animation-delay: 0.2s; }
.progress-circle:nth-child(3) { animation-delay: 0.3s; }
.progress-ring {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.progress-ring-bg {
stroke: #f1f5f9;
}
.progress-ring-fill {
transition: stroke-dashoffset 1s cubic-bezier(0.42, 0, 0.58, 1);
transform-origin: center;
}
.progress-circle.success .progress-ring-fill {
stroke: #27ae60;
}
.progress-circle.warning .progress-ring-fill {
stroke: #f39c12;
}
.progress-circle.error .progress-ring-fill {
stroke: #e74c3c;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.progress-value {
display: block;
font-size: 2rem;
font-weight: 700;
color: #333;
line-height: 1;
}
.progress-percent {
font-size: 1rem;
color: #666;
font-weight: 500;
}
.progress-circle.success .progress-value {
color: #27ae60;
}
.progress-circle.success .progress-percent {
color: #27ae60;
}
.progress-circle.warning .progress-value {
color: #f39c12;
}
.progress-circle.warning .progress-percent {
color: #f39c12;
}
.progress-circle.error .progress-value {
color: #e74c3c;
}
.progress-circle.error .progress-percent {
color: #e74c3c;
}
.circle-label {
color: #666;
font-size: 1rem;
font-weight: 600;
text-align: center;
transition: all 0.3s ease;
}
.circle-item:hover .circle-label {
color: #333;
transform: translateY(-2px);
}
.controls-wrapper {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 20px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #f1f5f9;
color: #666;
}
.btn-secondary:hover {
background: #e2e8f0;
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
}
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
50% {
transform: scale(1.05);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
}
100% {
transform: scale(1);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
}
.progress-circle.pulse {
animation: pulse 2s infinite;
}
.progress-ring-fill.animate {
animation: drawCircle 1s ease-out forwards;
}
@keyframes drawCircle {
from {
stroke-dashoffset: 283;
}
to {
stroke-dashoffset: 0;
}
}
/* Responsive */
@media (max-width: 768px) {
.progress-circle-container {
padding: 30px 15px;
}
.progress-circle-demo {
padding: 25px;
}
.demo-header h2 {
font-size: 1.7rem;
}
.circles-wrapper {
gap: 30px;
}
.progress-circle {
width: 120px;
height: 120px;
}
.progress-value {
font-size: 1.7rem;
}
.progress-percent {
font-size: 0.9rem;
}
.controls-wrapper {
flex-direction: column;
gap: 10px;
}
.btn {
width: 100%;
}
}
@media (max-width: 480px) {
.circles-wrapper {
gap: 20px;
}
.progress-circle {
width: 100px;
height: 100px;
}
.progress-value {
font-size: 1.5rem;
}
.circle-label {
font-size: 0.9rem;
}
}
document.addEventListener('DOMContentLoaded', function() {
// Get DOM elements
const progressCircles = document.querySelectorAll('.progress-circle');
const progressValues = document.querySelectorAll('.progress-value');
const progressRingFills = document.querySelectorAll('.progress-ring-fill');
const startAnimationBtn = document.getElementById('startAnimationBtn');
const resetAnimationBtn = document.getElementById('resetAnimationBtn');
// Progress data
const progressData = [
{ id: 'progressCircle1', value: 75, color: '#667eea' },
{ id: 'progressCircle2', value: 90, color: '#27ae60' },
{ id: 'progressCircle3', value: 65, color: '#f39c12' }
];
// Initialize progress circles
function initProgressCircles() {
setupEventListeners();
resetProgressCircles();
}
// Setup event listeners
function setupEventListeners() {
// Start animation button
startAnimationBtn.addEventListener('click', function() {
animateProgressCircles();
// Add pulse animation to button
this.classList.add('pulse');
setTimeout(() => {
this.classList.remove('pulse');
}, 1000);
});
// Reset animation button
resetAnimationBtn.addEventListener('click', function() {
resetProgressCircles();
// Add shake animation to button
this.classList.add('shake');
setTimeout(() => {
this.classList.remove('shake');
}, 500);
});
// Progress circle hover effects
progressCircles.forEach(circle => {
circle.addEventListener('mouseenter', function() {
this.classList.add('pulse');
});
circle.addEventListener('mouseleave', function() {
this.classList.remove('pulse');
});
});
}
// Reset progress circles
function resetProgressCircles() {
progressData.forEach((data, index) => {
const ringFill = progressRingFills[index];
const progressValue = progressValues[index];
// Reset stroke dashoffset
ringFill.style.strokeDashoffset = '283';
// Reset progress value
progressValue.textContent = '0';
// Add fade out animation
ringFill.parentElement.style.animation = 'fadeOut 0.3s ease';
setTimeout(() => {
ringFill.parentElement.style.animation = '';
}, 300);
});
}
// Animate progress circles
function animateProgressCircles() {
progressData.forEach((data, index) => {
const ringFill = progressRingFills[index];
const progressValue = progressValues[index];
// Calculate stroke dashoffset
const circumference = 2 * Math.PI * 45;
const offset = circumference - (data.value / 100) * circumference;
// Animate stroke dashoffset
ringFill.style.strokeDashoffset = circumference;
setTimeout(() => {
ringFill.style.transition = 'stroke-dashoffset 2s ease-in-out';
ringFill.style.strokeDashoffset = offset;
}, 100);
// Animate progress value
let currentValue = 0;
const increment = data.value / 50;
const interval = setInterval(() => {
currentValue += increment;
if (currentValue >= data.value) {
currentValue = data.value;
clearInterval(interval);
}
progressValue.textContent = Math.round(currentValue);
}, 40);
// Add rotation animation
ringFill.parentElement.style.animation = 'rotate 2s linear';
setTimeout(() => {
ringFill.parentElement.style.animation = '';
}, 2000);
});
}
// Update progress circle
function updateProgressCircle(circleId, percentage) {
const circle = document.getElementById(circleId);
const ringFill = circle.querySelector('.progress-ring-fill');
const progressValue = circle.querySelector('.progress-value');
// Calculate stroke dashoffset
const circumference = 2 * Math.PI * 45;
const offset = circumference - (percentage / 100) * circumference;
// Update stroke dashoffset
ringFill.style.strokeDashoffset = offset;
// Update progress value
progressValue.textContent = percentage;
// Add animation class
circle.classList.add('animate');
setTimeout(() => {
circle.classList.remove('animate');
}, 1000);
}
// Add hover effects to progress circles
progressCircles.forEach(circle => {
circle.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.1)';
this.style.transition = 'transform 0.3s ease';
});
circle.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
});
// Initialize the progress circles
initProgressCircles();
});