easy-pie-chart

July 22, 2026 · View on GitHub

Lightweight plugin to render simple, animated and retina optimized pie charts

CI npm

  • highly customizable, no dependencies
  • resolution independent (retina optimized)
  • written in TypeScript, ships its own types
  • ESM + UMD builds, ~2 kB gzipped
  • optional jQuery plugin entry point

Install

npm install easy-pie-chart

Usage

import { EasyPieChart } from 'easy-pie-chart';

const chart = new EasyPieChart(document.querySelector('.chart'), {
  barColor: '#ef1e25',
  size: 110,
});

chart.update(65);

Via a script tag — the UMD bundle exposes a global EasyPieChart:

<script src="node_modules/easy-pie-chart/dist/easypiechart.min.js"></script>
<script>
  new EasyPieChart(document.querySelector('.chart'), { size: 110 }).update(65);
</script>

jQuery

<script src="jquery.js"></script>
<script src="node_modules/easy-pie-chart/dist/jquery.easypiechart.min.js"></script>
<script>
  $('.chart').easyPieChart({ barColor: '#10b981' });

  // the instance lives on the element's data
  $('.chart').data('easyPieChart').update(42);

  // tear it down
  $('.chart').easyPieChart('destroy');
</script>

As a module, when jQuery is not a global:

import { registerJQueryPlugin } from 'easy-pie-chart/jquery';
import $ from 'jquery';

registerJQueryPlugin($);

AngularJS — migrating from 2.x

2.x shipped dist/angular.easypiechart.js. It is not part of 3.x, because AngularJS 1.x has been end-of-life since January 2022 and this package will not advertise support for an unpatched framework.

The core is framework-agnostic, so the directive is a few lines in your own code. This is a drop-in replacement for the 2.x one — same easypiechart module name, same percent and options bindings, same restrict: 'AE':

angular.module('easypiechart', []).directive('easypiechart', () => ({
  restrict: 'AE',
  scope: { percent: '=', options: '=' },
  link(scope, element) {
    const chart = new EasyPieChart(element[0], scope.options || {});

    scope.$watch('percent', (value) => chart.update(value || 0));

    // optional: rebuild when the options object is replaced
    scope.$watch('options', (o) => o && chart.setOptions(o), true);

    // 2.x had no teardown, so charts leaked whenever a scope was destroyed
    scope.$on('$destroy', () => chart.destroy());
  },
}));

Markup is unchanged from 2.x:

<div easypiechart percent="model.percent" options="model.options"></div>

Load the UMD bundle before your app so EasyPieChart is a global, or import it if you bundle. If you would rather not migrate at all, 2.1.7 stays on npm indefinitely — easy-pie-chart@2.1.7/dist/angular.easypiechart.js resolves on unpkg and jsDelivr as it always has.

Options via data-* attributes

Every option except the callbacks can be set on the element. Attributes win over the options object, so you can share defaults in JS and override them per element — the same ordering the 2.x jQuery plugin used:

$('.chart').easyPieChart({ barColor: '#ef1e25' }); // default for all
// <div class="chart" data-bar-color="#10b981"></div>  <- this one is green
<div class="chart" data-percent="65" data-size="140" data-bar-color="#7c3aed"></div>

data-track-color="false" and data-scale-color="false" disable the track and the scale. data-percent sets the initial value.

Options

OptionDefaultDescription
barColor'#ef1e25'CSS color string, gradient/pattern, or (value) => style
trackColor'#f9f9f9'Track color, or false to disable
trackBorderColorfalseHairline along both edges of the track, or false
trackBorderWidth1Width of that hairline in px
fillColorfalseFill color for the disc inside the ring, or false
scaleColor'#dfe0e0'Scale line color, or false to disable
scaleLength5Length of the scale lines in px (reduces the radius)
scaleCount24Number of scale lines
lineCap'round''butt', 'round' or 'square'
lineWidth3Width of the bar in px
trackWidthlineWidthWidth of the track in px
size110Size of the chart in px (always square)
rotate0Rotation of the whole chart in degrees
arcLength360How much of the circle the chart spans, in degrees
max100The value that corresponds to a full bar
responsivefalseResize the chart when the host element resizes
canvasClass'easy-pie-chart-canvas'Class applied to the generated canvas
animate{ duration: 1000, enabled: true }Also accepts a number (duration) or false. duration may be a (from, to) => ms function
easingquadratic ease-in-out(t, b, c, d) => number
onStart(from, to) => void
onStep(from, to, currentValue) => void
onStop(from, to) => void
rendererCanvasRendererCustom renderer implementing IRenderer

Values may be negative — the bar is then drawn counter-clockwise.

Values other than percentages

Set max to the value that should fill the bar. Callbacks and barColor still receive your raw value, so labels need no conversion:

new EasyPieChart(el, {
  max: 250,
  onStep(from, to, value) {
    this.el.querySelector('.label').textContent = `${Math.round(value)} / 250`;
  },
}).update(125); // half a ring

Gauges

arcLength limits the sweep; combine it with rotate to place the opening. A semi-circular gauge is 180 degrees rotated a quarter turn back:

new EasyPieChart(el, { arcLength: 180, rotate: -90, lineWidth: 10 });

Responsive charts

With responsive: true the chart follows the host element's size via ResizeObserver. The host must take its size from CSS or its parent — if it is sized by its content, it and the canvas would size each other:

.chart { width: 100%; aspect-ratio: 1; }
new EasyPieChart(document.querySelector('.chart'), { responsive: true });

Function options (barColor, easing, onStart, onStep, onStop) are bound to the chart instance, so this.el inside them is the host element. Arrow functions keep their own this, as usual.

Gradients

barColor receives the current value and returns any valid canvas stroke style — including a gradient built from the renderer's own context:

const chart = new EasyPieChart(el, {
  barColor() {
    const ctx = this.renderer.getCtx();
    const { size } = this.options;
    const gradient = ctx.createLinearGradient(0, 0, size, 0);
    gradient.addColorStop(0, '#22c55e');
    gradient.addColorStop(1, '#0ea5e9');
    return gradient;
  },
});

The canvas is translated so 0,0 is its centre and rotated so 0% starts at 12 o'clock — take that into account when positioning gradient stops. Use this.renderer.getCanvas() if you need the element itself.

API

MethodDescription
update(value)Animate (or jump) to a new value. Non-numeric values are ignored.
setOptions(options)Apply new options and redraw at the current value.
stop()Stop a running animation at the current frame.
enableAnimation() / disableAnimation()Toggle animated updates.
destroy()Cancel animations and remove the canvas.
valueGetter for the current value.
optionsThe resolved options object.
rendererThe active renderer. getCtx() / getCanvas() on the canvas renderer.
elThe host element.

All methods except destroy() and value return the instance for chaining.

Examples

Run npm run build, then open examples/index.html in a browser.

Migrating from 2.x

  • Distributed as ESM (dist/easypiechart.mjs), CommonJS (dist/easypiechart.cjs) and a minified UMD bundle for script tags (dist/easypiechart.min.js). The UMD global is still EasyPieChart. TypeScript types are correct under node16, nodenext and bundler resolution.
  • The AngularJS 1.x directive was removed — AngularJS has been end-of-life since January 2022. See AngularJS — migrating from 2.x for a drop-in replacement directive, or stay on 2.1.7.
  • Bower and Meteor packaging were removed. Install from npm.
  • The easing signature is now (t, b, c, d). The 2.x form (chart, t, b, c, d) and jQuery easing names are both still accepted, so existing configs keep working.
  • A 0% bar no longer renders a dot when lineCap is 'round'.
  • update() ignores NaN instead of leaving the chart stuck.
  • New: setOptions(), stop(), destroy(), scaleCount, data-* options on the vanilla constructor, and TypeScript types.

License

MIT