Skip to content

Instantly share code, notes, and snippets.

@vitalyrotari
Last active December 21, 2015 12:49
Show Gist options
  • Save vitalyrotari/6308507 to your computer and use it in GitHub Desktop.
Save vitalyrotari/6308507 to your computer and use it in GitHub Desktop.
Canvas Pie Progress
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<style>
canvas {
-webkit-transform: scale3d(2, 2, 0) translate3d(260, 0, 0);
-moz-transform: scale3d(2, 2, 0) translate3d(260, 0, 0);
-ms-transform: scale3d(2, 2, 0) translate3d(260, 0, 0);
-o-transform: scale3d(2, 2, 0) translate3d(260, 0, 0);
transform: scale3d(2, 2, 0) translate3d(260, 0, 0);
border-radius: 125px;
background-position: 50% 40%;
display: block;
width: 250px;
height: 250px;
}
</style>
</head>
<body>
<canvas id="test-progress" width="250" height="250"></canvas>
<button type="button">Set</button>
<input id="percent" type="text" value="10">
<script src="pie-progress.js"></script>
<script src="request-animation.js"></script>
<script>
var progress = new PieProgress('test-progress');
document.querySelector('button').addEventListener('click', function () {
var value = document.querySelector('#percent').value;
progress.set(parseInt(value, 10));
}, false);
</script>
</body>
</html>
var PieProgress = function (id, options) {
if ('object' !== typeof(options) || options === null) {
options = {};
}
var dpr = window.devicePixelRatio || 1
, size = parseInt(options.size, 10) || 250
, line = parseInt(options.lineWidth, 10) || 10
;
this.canvas = document.getElementById(id);
this.ctx = this.canvas.getContext('2d');
this.radius = (size/2 - line/3) * dpr;
this.fps = options.fps || 60;
this.size = size * dpr;
this.position = this.size/2;
this.color = PieProgress.COLORS['0'];
this.canvas.width = this.size;
this.canvas.height = this.size;
this.canvas.style.width = size;
this.canvas.style.height = size;
this.ctx.lineWidth = line;
this.ctx.strokeStyle = this.color;
};
PieProgress.prototype = {
/**
* public
* @type {boolean}
*/
working: false,
/**
* @private
* @type {number}
*/
timerId: 0,
/**
* @private
* @type {number}
*/
animationId: 0,
/**
* @public
* @type {number}
*/
value: 0,
/**
* @private
* @type {number}
*/
current: 0,
/**
* private
* @type {boolean}
*/
inverse: false,
/**
* @private
*/
draw: function () {
this.timerId = setTimeout(function () {
this.animationId = requestAnimationFrame(this.draw.bind(this));
var max = 360/100 * this.value
, percent = this.current * 100 / 360
, stop = this.inverse ? (this.current < max) : (this.current > max)
;
if (stop) {
return this.stop();
}
if (PieProgress.COLORS.hasOwnProperty(percent)) {
this.color = PieProgress.COLORS[percent];
}
this.ctx.clearRect(0, 0, this.size, this.size);
this.ctx.save();
this.ctx.beginPath();
this.ctx.strokeStyle = this.color;
this.ctx.arc(this.position, this.position, this.radius, 0, this.current * (Math.PI/180), false);
this.ctx.stroke();
this.ctx.restore();
if (this.inverse) {
this.current--;
} else {
this.current++;
}
}.bind(this), 1000 / this.fps);
},
/**
* @param {number} value
* @return {PieProgress}
*/
set: function (value) {
if (this.value !== value) {
this.inverse = (value < this.value);
this.value = value;
this.stop().start();
}
return this;
},
/**
* @public
* @return {PieProgress}
*/
start: function() {
if (!this.working) {
this.draw();
this.working = true;
}
return this;
},
/**
* @public
* @return {PieProgress}
*/
stop: function() {
clearTimeout(this.timerId);
cancelAnimationFrame(this.animationId);
this.working = false;
this.timerId = this.animationId = null;
return this;
}
};
PieProgress.COLORS = {
'0': '#3BB600',
'75': '#EE8E45',
'90': '#E02800'
};
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment