EasyPieChart.js
Lightweight, Animated Circular Progress Indicators
EasyPieChart is a lightweight plugin to render simple, animated and retina-optimized pie charts. It uses the HTML5 canvas element for rendering and is designed to work well with percentages and visualize progress metrics in a compact, circular format.
These circular charts are perfect for dashboards, project progress tracking, and anywhere you need to show percentage-based information in a visually appealing way.
Installation
EasyPieChart is included in the SmartAdmin theme. There are two ways to use it:
1. Automatic Implementation with Data Attributes
// The easypiechart.js file is already included in SmartAdmin
// and automatically initializes all charts with the 'js-easy-pie-chart' class
// Simply add elements with this class and configuration via data attributes:
<div class="js-easy-pie-chart" data-percent="75" data-piesize="100" data-linewidth="10">
<span class="js-percent">0</span>%
</div>
// No JavaScript needed - charts initialize automatically on page load
2. Manual Implementation with Direct API
// Import the EasyPieChart constructor directly
import EasyPieChart from './thirdparty/easypiechart/easypiechart.es6.js';
document.addEventListener('DOMContentLoaded', function() {
// Basic initialization
const element = document.querySelector('.my-chart');
new EasyPieChart(element, {
barColor: 'var(--primary-500)',
trackColor: 'var(--primary-200)',
scaleColor: false,
lineWidth: 20,
size: 150,
animate: 1000,
onStep: function(from, to, percent) {
this.el.querySelector('.percent').textContent = Math.round(percent);
}
});
// You can update the chart programmatically
// chart.update(65); // Updates to 65%
});
HTML Structure
<!-- For automatic initialization with easypiechart.js -->
<div class="js-easy-pie-chart" data-percent="75">
<span class="js-percent">0</span>%
</div>
<!-- With data attributes for configuration -->
<div class="js-easy-pie-chart"
data-percent="65"
data-piesize="100"
data-linewidth="10"
data-linecap="round">
<span class="js-percent">0</span>%
</div>
<!-- For manual initialization with easypiechart.es6.js -->
<div class="my-chart" data-percent="33">
<svg class="sa-icon">
<use href="img/sprite.svg#star"></use>
</svg>
<div class="percent text-center mt-2">33</div>
</div>
- Automatic Implementation: Best for typical use cases. Just add HTML with the proper class and data attributes - no JavaScript needed.
- Manual Implementation: Use when you need more control over initialization timing, want to update charts dynamically, or need custom behavior.
- The element must have a
data-percent
attribute with the percentage value to display. - For automatic initialization, use
.js-percent
for the percentage text to update. - For manual initialization, use whatever selector you reference in your
onStep
callback. - EasyPieChart uses the HTML5 Canvas element, so it's not supported in very old browsers.
Configuration Options
EasyPieChart offers various configuration options to customize the appearance and behavior of your charts:
Option | Type | Default | Description |
---|---|---|---|
barColor |
String | Function | '#ef1e25' | The color of the progress bar. Can be a CSS color or a function that returns a color based on the current percentage. |
trackColor |
String | '#f2f2f2' | The color of the track (background) of the progress bar. |
scaleColor |
String | Boolean | '#dfe0e0' | The color of the scale lines. Set to false to disable scale lines. |
scaleLength |
Number | 5 | Length of the scale lines (in pixels). |
lineCap |
String | 'round' | Shape of the line cap: 'butt', 'round', or 'square'. |
lineWidth |
Number | 3 | Width of the progress bar line (in pixels). |
size |
Number | 110 | Size of the pie chart (in pixels). |
rotate |
Number | 0 | Rotation of the chart in degrees (0-360). |
animate |
Boolean | Object | {duration: 1000, enabled: true} | Animation configuration. Set to false to disable animation. |
easing |
String | Function | 'linear' | Animation easing function: 'linear', 'easeInOutQuad', etc. |
onStart |
Function | null | Callback function called at the start of animation. |
onStep |
Function | null | Callback function called during each animation step. Receives the from, to, and current percentage values. |
onStop |
Function | null | Callback function called at the end of animation. |
data-
. For example, data-bar-color
, data-track-color
, etc.
Usage Examples
Here are some common usage patterns for EasyPieChart:
Basic Usage
// Basic chart with default values
new EasyPieChart(document.querySelector('.chart-1'), {
barColor: 'var(--primary-500)',
trackColor: 'var(--primary-200)',
scaleColor: false,
lineWidth: 8,
size: 100
});
Dynamic Color Based on Percentage
// Chart color changes based on percentage value
new EasyPieChart(document.querySelector('.chart-2'), {
barColor: function(percent) {
if (percent < 25) {
return 'var(--danger-500)'; // Red for low percentages
} else if (percent < 50) {
return 'var(--warning-500)'; // Orange for medium-low
} else if (percent < 75) {
return 'var(--info-500)'; // Blue for medium-high
} else {
return 'var(--success-500)'; // Green for high percentages
}
},
trackColor: 'var(--gray-300)',
scaleColor: false,
lineWidth: 10,
size: 120
});
With Animation and Callback
// Chart with custom animation and callbacks
new EasyPieChart(document.querySelector('.chart-3'), {
barColor: 'var(--info-500)',
trackColor: 'var(--info-200)',
scaleColor: false,
lineWidth: 15,
size: 150,
animate: {
duration: 2000,
enabled: true
},
easing: 'easeOutBounce',
onStart: function() {
console.log('Animation started');
},
onStep: function(from, to, percent) {
this.el.querySelector('.percent').textContent = Math.round(percent);
},
onStop: function() {
console.log('Animation stopped');
}
});
Updating Chart Values
// Create chart instance
const element = document.querySelector('.chart-4');
const chart = new EasyPieChart(element, {
barColor: 'var(--success-500)',
trackColor: 'var(--success-200)',
scaleColor: false,
lineWidth: 10,
size: 120
});
// Update chart value (e.g., after user action or data refresh)
document.getElementById('update-btn').addEventListener('click', function() {
// Generate random percentage
const newValue = Math.floor(Math.random() * 100);
// Update chart
chart.update(newValue);
// Update display text
element.querySelector('.percent').textContent = newValue;
});
SmartAdmin Integration
Here are some tips for integrating EasyPieChart with SmartAdmin:
Using Color Variables
// Use SmartAdmin CSS variables for consistent colors
new EasyPieChart(element, {
barColor: 'var(--primary-500)', // Primary theme color
trackColor: 'var(--primary-200)', // Lighter shade of primary
scaleColor: false,
lineWidth: 8,
size: 100
});
Creating Panel-Based Widgets
<div class="panel">
<div class="panel-hdr">
<h2>Project <span class="fw-300"><i>Progress</i></span></h2>
<div class="panel-toolbar">
<button class="btn btn-panel" data-action="panel-collapse">
<svg class="sa-icon">
<use href="img/sprite.svg#minus-circle"></use>
</svg>
</button>
</div>
</div>
<div class="panel-container show">
<div class="panel-content">
<div class="row">
<div class="col-md-4 text-center">
<div class="js-pie-chart mb-2" data-percent="65" data-bar-color="var(--primary-500)" data-track-color="var(--primary-200)" data-line-width="5" data-size="100">
<span class="percent"></span>%
</div>
<h5>Phase 1</h5>
</div>
<div class="col-md-4 text-center">
<div class="js-pie-chart mb-2" data-percent="42" data-bar-color="var(--success-500)" data-track-color="var(--success-200)" data-line-width="5" data-size="100">
<span class="percent"></span>%
</div>
<h5>Phase 2</h5>
</div>
<div class="col-md-4 text-center">
<div class="js-pie-chart mb-2" data-percent="10" data-bar-color="var(--info-500)" data-track-color="var(--info-200)" data-line-width="5" data-size="100">
<span class="percent"></span>%
</div>
<h5>Phase 3</h5>
</div>
</div>
</div>
</div>
</div>
Batch Initialization
// Initialize all pie charts on the page
document.addEventListener('DOMContentLoaded', function() {
// Select all elements with the js-pie-chart class
const elements = document.querySelectorAll('.js-pie-chart');
// Initialize each chart with options from data attributes
elements.forEach(function(element) {
// Get options from data attributes
const options = {
barColor: element.dataset.barColor || 'var(--primary-500)',
trackColor: element.dataset.trackColor || 'var(--primary-200)',
scaleColor: element.dataset.scaleColor === 'false' ? false : element.dataset.scaleColor,
lineWidth: parseInt(element.dataset.lineWidth || 3, 10),
size: parseInt(element.dataset.size || 110, 10),
animate: element.dataset.animate === 'false' ? false : parseInt(element.dataset.animate || 1000, 10),
onStep: function(from, to, percent) {
const percentElement = element.querySelector('.percent');
if (percentElement) {
percentElement.textContent = Math.round(percent);
}
}
};
// Initialize the chart
new EasyPieChart(element, options);
});
});
Advanced Techniques
Custom Easing Functions
// Custom easing function
const customEasing = function(t, b, c, d) {
// t: current time, b: beginning value, c: change in value, d: duration
if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
return c / 2 * ((t -= 2) * t * t + 2) + b;
};
// Use custom easing
new EasyPieChart(element, {
barColor: 'var(--primary-500)',
trackColor: 'var(--primary-200)',
scaleColor: false,
lineWidth: 10,
size: 120,
easing: customEasing
});
Responsive Size Adjustment
// Make charts responsive
function initResponsiveCharts() {
const charts = [];
// Initialize charts
document.querySelectorAll('.js-pie-chart').forEach(function(element) {
// Store original size
element.dataset.originalSize = element.dataset.size || '110';
// Initialize with current size
const size = window.innerWidth < 768 ?
Math.floor(parseInt(element.dataset.originalSize, 10) * 0.7) : // Smaller size on mobile
parseInt(element.dataset.originalSize, 10); // Original size on desktop
const chart = new EasyPieChart(element, {
barColor: element.dataset.barColor || 'var(--primary-500)',
trackColor: element.dataset.trackColor || 'var(--primary-200)',
scaleColor: false,
lineWidth: parseInt(element.dataset.lineWidth || 3, 10),
size: size,
animate: 1000
});
// Store chart instance for later updates
charts.push({ element: element, chart: chart });
});
// Update sizes on window resize
window.addEventListener('resize', function() {
charts.forEach(function(item) {
// Recalculate size
const size = window.innerWidth < 768 ?
Math.floor(parseInt(item.element.dataset.originalSize, 10) * 0.7) :
parseInt(item.element.dataset.originalSize, 10);
// Update chart
item.chart.options.size = size;
item.chart.update(parseInt(item.element.dataset.percent, 10));
});
});
}
// Call initialization on DOMContentLoaded
document.addEventListener('DOMContentLoaded', initResponsiveCharts);
Animated Progress Updates
// Function to animate a chart from current value to target value
function animateProgress(chartElement, targetPercent, duration = 1500, steps = 30) {
// Get chart instance
const chart = chartElement.__chart;
if (!chart) return;
// Get current percent
const currentPercent = parseInt(chartElement.dataset.percent, 10);
// Calculate step size
const stepSize = (targetPercent - currentPercent) / steps;
const stepTime = duration / steps;
// Current step counter
let currentStep = 0;
// Update function
function updateStep() {
currentStep++;
// Calculate current value
const newPercent = Math.round(currentPercent + (stepSize * currentStep));
// Update chart
chartElement.dataset.percent = newPercent;
chart.update(newPercent);
// Continue animation if not complete
if (currentStep < steps) {
setTimeout(updateStep, stepTime);
}
}
// Start animation
updateStep();
}
// Example usage
const progressChart = document.querySelector('.project-progress');
document.getElementById('complete-task').addEventListener('click', function() {
animateProgress(progressChart, 75); // Animate to 75%
});
Troubleshooting
- Chart not rendering? Ensure the container element exists and has dimensions before initializing.
- Percentage not updating during animation? Verify that you have included a
.percent
element and properonStep
callback. - Colors not applying? Check that CSS variables are correctly defined and accessible.
- Canvas rendering issues? Ensure the browser supports HTML5 Canvas.
Debugging Tips
// Debug chart initialization
try {
const element = document.querySelector('.chart');
console.log('Chart element:', element);
if (!element) {
console.error('Chart element not found!');
return;
}
// Log dimensions
console.log('Element size:', element.offsetWidth, element.offsetHeight);
// Check if data-percent exists
console.log('Percent value:', element.dataset.percent);
// Initialize with debug callback
const chart = new EasyPieChart(element, {
barColor: 'var(--primary-500)',
trackColor: 'var(--primary-200)',
scaleColor: false,
lineWidth: 10,
size: 100,
onStart: function() {
console.log('Animation started');
},
onStep: function(from, to, percent) {
console.log('Step:', from, to, percent);
const percentElement = element.querySelector('.percent');
if (percentElement) {
percentElement.textContent = Math.round(percent);
} else {
console.warn('No .percent element found inside chart container');
}
},
onStop: function() {
console.log('Animation completed');
}
});
} catch (error) {
console.error('Chart initialization error:', error);
}
Handling Hidden Elements
Charts initialized in hidden elements (e.g., collapsed panels, inactive tabs) may not render properly. Initialize or update them when they become visible:
// Initialize charts in tabs when they become visible
document.querySelectorAll('[data-bs-toggle="tab"]').forEach(function(tab) {
tab.addEventListener('shown.bs.tab', function(e) {
// Get the newly activated tab content
const tabContent = document.querySelector(e.target.getAttribute('href'));
// Find and initialize/update charts
tabContent.querySelectorAll('.js-pie-chart').forEach(function(element) {
// If chart already initialized, update it
if (element.__chart) {
element.__chart.update(parseInt(element.dataset.percent, 10));
} else {
// Otherwise initialize new chart
const chart = new EasyPieChart(element, {
barColor: element.dataset.barColor || 'var(--primary-500)',
trackColor: element.dataset.trackColor || 'var(--primary-200)',
scaleColor: false,
lineWidth: parseInt(element.dataset.lineWidth || 3, 10),
size: parseInt(element.dataset.size || 110, 10)
});
// Store reference to chart instance
element.__chart = chart;
}
});
});
});
Further Resources
For more advanced usage and detailed API documentation, refer to these resources: