# EasyPieChart.js

###### Lightweight, Animated Circular Progress Indicators.

## 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.

Pro Tip: EasyPieChart works seamlessly with SmartAdmin's color system and can be combined with other elements like counters, icons, or text to create rich, informative widgets.

## 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>

When to use each approach:

- 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.

Important:

- 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.

Pro Tip: You can also set these options using HTML data attributes by prefixing the option name with `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

Common Issues:

- 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 proper `onStep` 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:

- EasyPieChart GitHub Repository
- EasyPieChart Demo Page
- Official EasyPieChart Documentation

##### On This Page

- Introduction
- Installation
- Configuration Options
- Usage Examples
- SmartAdmin Integration
- Advanced Techniques
- Troubleshooting
- Further Resources

#### Hi Sunny,

how can I help you today?
Analyze my data

Create a new report

Summarize my Calendar

App Builder
Unlock limitless design potential with 16+ layout combinations and extensive customization options—tailor your dashboard to fit your exact needs.
Header position fixed
Navigation full height
Navigation position fixed
Navigation collapsed
Navigation Minified
Dark Navigation
Colorblind Mode
High Contrast Mode