Created
June 12, 2013 05:08
-
-
Save impronunciable/5762955 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Chart.js | |
* http://chartjs.org/ | |
* | |
* Copyright 2013 Nick Downie | |
* Released under the MIT license | |
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md | |
*/ | |
//Define the global Chart Variable as a class. | |
module.exports = function(context){ | |
var chart = this; | |
//Easing functions adapted from Robert Penner's easing equations | |
//http://www.robertpenner.com/easing/ | |
var animationOptions = { | |
linear : function (t){ | |
return t; | |
}, | |
easeInQuad: function (t) { | |
return t*t; | |
}, | |
easeOutQuad: function (t) { | |
return -1 *t*(t-2); | |
}, | |
easeInOutQuad: function (t) { | |
if ((t/=1/2) < 1) return 1/2*t*t; | |
return -1/2 * ((--t)*(t-2) - 1); | |
}, | |
easeInCubic: function (t) { | |
return t*t*t; | |
}, | |
easeOutCubic: function (t) { | |
return 1*((t=t/1-1)*t*t + 1); | |
}, | |
easeInOutCubic: function (t) { | |
if ((t/=1/2) < 1) return 1/2*t*t*t; | |
return 1/2*((t-=2)*t*t + 2); | |
}, | |
easeInQuart: function (t) { | |
return t*t*t*t; | |
}, | |
easeOutQuart: function (t) { | |
return -1 * ((t=t/1-1)*t*t*t - 1); | |
}, | |
easeInOutQuart: function (t) { | |
if ((t/=1/2) < 1) return 1/2*t*t*t*t; | |
return -1/2 * ((t-=2)*t*t*t - 2); | |
}, | |
easeInQuint: function (t) { | |
return 1*(t/=1)*t*t*t*t; | |
}, | |
easeOutQuint: function (t) { | |
return 1*((t=t/1-1)*t*t*t*t + 1); | |
}, | |
easeInOutQuint: function (t) { | |
if ((t/=1/2) < 1) return 1/2*t*t*t*t*t; | |
return 1/2*((t-=2)*t*t*t*t + 2); | |
}, | |
easeInSine: function (t) { | |
return -1 * Math.cos(t/1 * (Math.PI/2)) + 1; | |
}, | |
easeOutSine: function (t) { | |
return 1 * Math.sin(t/1 * (Math.PI/2)); | |
}, | |
easeInOutSine: function (t) { | |
return -1/2 * (Math.cos(Math.PI*t/1) - 1); | |
}, | |
easeInExpo: function (t) { | |
return (t==0) ? 1 : 1 * Math.pow(2, 10 * (t/1 - 1)); | |
}, | |
easeOutExpo: function (t) { | |
return (t==1) ? 1 : 1 * (-Math.pow(2, -10 * t/1) + 1); | |
}, | |
easeInOutExpo: function (t) { | |
if (t==0) return 0; | |
if (t==1) return 1; | |
if ((t/=1/2) < 1) return 1/2 * Math.pow(2, 10 * (t - 1)); | |
return 1/2 * (-Math.pow(2, -10 * --t) + 2); | |
}, | |
easeInCirc: function (t) { | |
if (t>=1) return t; | |
return -1 * (Math.sqrt(1 - (t/=1)*t) - 1); | |
}, | |
easeOutCirc: function (t) { | |
return 1 * Math.sqrt(1 - (t=t/1-1)*t); | |
}, | |
easeInOutCirc: function (t) { | |
if ((t/=1/2) < 1) return -1/2 * (Math.sqrt(1 - t*t) - 1); | |
return 1/2 * (Math.sqrt(1 - (t-=2)*t) + 1); | |
}, | |
easeInElastic: function (t) { | |
var s=1.70158;var p=0;var a=1; | |
if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3; | |
if (a < Math.abs(1)) { a=1; var s=p/4; } | |
else var s = p/(2*Math.PI) * Math.asin (1/a); | |
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )); | |
}, | |
easeOutElastic: function (t) { | |
var s=1.70158;var p=0;var a=1; | |
if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3; | |
if (a < Math.abs(1)) { a=1; var s=p/4; } | |
else var s = p/(2*Math.PI) * Math.asin (1/a); | |
return a*Math.pow(2,-10*t) * Math.sin( (t*1-s)*(2*Math.PI)/p ) + 1; | |
}, | |
easeInOutElastic: function (t) { | |
var s=1.70158;var p=0;var a=1; | |
if (t==0) return 0; if ((t/=1/2)==2) return 1; if (!p) p=1*(.3*1.5); | |
if (a < Math.abs(1)) { a=1; var s=p/4; } | |
else var s = p/(2*Math.PI) * Math.asin (1/a); | |
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )); | |
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )*.5 + 1; | |
}, | |
easeInBack: function (t) { | |
var s = 1.70158; | |
return 1*(t/=1)*t*((s+1)*t - s); | |
}, | |
easeOutBack: function (t) { | |
var s = 1.70158; | |
return 1*((t=t/1-1)*t*((s+1)*t + s) + 1); | |
}, | |
easeInOutBack: function (t) { | |
var s = 1.70158; | |
if ((t/=1/2) < 1) return 1/2*(t*t*(((s*=(1.525))+1)*t - s)); | |
return 1/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2); | |
}, | |
easeInBounce: function (t) { | |
return 1 - animationOptions.easeOutBounce (1-t); | |
}, | |
easeOutBounce: function (t) { | |
if ((t/=1) < (1/2.75)) { | |
return 1*(7.5625*t*t); | |
} else if (t < (2/2.75)) { | |
return 1*(7.5625*(t-=(1.5/2.75))*t + .75); | |
} else if (t < (2.5/2.75)) { | |
return 1*(7.5625*(t-=(2.25/2.75))*t + .9375); | |
} else { | |
return 1*(7.5625*(t-=(2.625/2.75))*t + .984375); | |
} | |
}, | |
easeInOutBounce: function (t) { | |
if (t < 1/2) return animationOptions.easeInBounce (t*2) * .5; | |
return animationOptions.easeOutBounce (t*2-1) * .5 + 1*.5; | |
} | |
}; | |
//Variables global to the chart | |
var width = context.canvas.width; | |
var height = context.canvas.height; | |
/* TODO: Check this out | |
//High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. | |
if (window.devicePixelRatio) { | |
context.canvas.style.width = width + "px"; | |
context.canvas.style.height = height + "px"; | |
context.canvas.height = height * window.devicePixelRatio; | |
context.canvas.width = width * window.devicePixelRatio; | |
context.scale(window.devicePixelRatio, window.devicePixelRatio); | |
} | |
*/ | |
this.PolarArea = function(data,options){ | |
chart.PolarArea.defaults = { | |
scaleOverlay : true, | |
scaleOverride : false, | |
scaleSteps : null, | |
scaleStepWidth : null, | |
scaleStartValue : null, | |
scaleShowLine : true, | |
scaleLineColor : "rgba(0,0,0,.1)", | |
scaleLineWidth : 1, | |
scaleShowLabels : true, | |
scaleLabel : "<%=value%>", | |
scaleFontFamily : "'menlo'", | |
scaleFontSize : 12, | |
scaleFontStyle : "normal", | |
scaleFontColor : "#666", | |
scaleShowLabelBackdrop : true, | |
scaleBackdropColor : "rgba(255,255,255,0.75)", | |
scaleBackdropPaddingY : 2, | |
scaleBackdropPaddingX : 2, | |
segmentShowStroke : true, | |
segmentStrokeColor : "#fff", | |
segmentStrokeWidth : 2, | |
animation : true, | |
animationSteps : 100, | |
animationEasing : "easeOutBounce", | |
animateRotate : true, | |
animateScale : false, | |
onAnimationComplete : null | |
}; | |
var config = (options)? mergeChartConfig(chart.PolarArea.defaults,options) : chart.PolarArea.defaults; | |
return new PolarArea(data,config,context); | |
}; | |
this.Radar = function(data,options){ | |
chart.Radar.defaults = { | |
scaleOverlay : false, | |
scaleOverride : false, | |
scaleSteps : null, | |
scaleStepWidth : null, | |
scaleStartValue : null, | |
scaleShowLine : true, | |
scaleLineColor : "rgba(0,0,0,.1)", | |
scaleLineWidth : 1, | |
scaleShowLabels : false, | |
scaleLabel : "<%=value%>", | |
scaleFontFamily : "'menlo'", | |
scaleFontSize : 12, | |
scaleFontStyle : "normal", | |
scaleFontColor : "#666", | |
scaleShowLabelBackdrop : true, | |
scaleBackdropColor : "rgba(255,255,255,0.75)", | |
scaleBackdropPaddingY : 2, | |
scaleBackdropPaddingX : 2, | |
angleShowLineOut : true, | |
angleLineColor : "rgba(0,0,0,.1)", | |
angleLineWidth : 1, | |
pointLabelFontFamily : "'menlo'", | |
pointLabelFontStyle : "normal", | |
pointLabelFontSize : 12, | |
pointLabelFontColor : "#666", | |
pointDot : true, | |
pointDotRadius : 3, | |
pointDotStrokeWidth : 1, | |
datasetStroke : true, | |
datasetStrokeWidth : 2, | |
datasetFill : true, | |
animation : true, | |
animationSteps : 60, | |
animationEasing : "easeOutQuart", | |
onAnimationComplete : null | |
}; | |
var config = (options)? mergeChartConfig(chart.Radar.defaults,options) : chart.Radar.defaults; | |
return new Radar(data,config,context); | |
}; | |
this.Pie = function(data,options){ | |
chart.Pie.defaults = { | |
segmentShowStroke : true, | |
segmentStrokeColor : "#fff", | |
segmentStrokeWidth : 2, | |
animation : true, | |
animationSteps : 100, | |
animationEasing : "easeOutBounce", | |
animateRotate : true, | |
animateScale : false, | |
onAnimationComplete : null | |
}; | |
var config = (options)? mergeChartConfig(chart.Pie.defaults,options) : chart.Pie.defaults; | |
return new Pie(data,config,context); | |
}; | |
this.Doughnut = function(data,options){ | |
chart.Doughnut.defaults = { | |
segmentShowStroke : true, | |
segmentStrokeColor : "#fff", | |
segmentStrokeWidth : 2, | |
percentageInnerCutout : 50, | |
animation : true, | |
animationSteps : 100, | |
animationEasing : "easeOutBounce", | |
animateRotate : true, | |
animateScale : false, | |
onAnimationComplete : null | |
}; | |
var config = (options)? mergeChartConfig(chart.Doughnut.defaults,options) : chart.Doughnut.defaults; | |
return new Doughnut(data,config,context); | |
}; | |
this.Line = function(data,options){ | |
chart.Line.defaults = { | |
scaleOverlay : false, | |
scaleOverride : false, | |
scaleSteps : null, | |
scaleStepWidth : null, | |
scaleStartValue : null, | |
scaleLineColor : "rgba(0,0,0,.1)", | |
scaleLineWidth : 1, | |
scaleShowLabels : true, | |
scaleLabel : "<%=value%>", | |
scaleFontFamily : "'menlo'", | |
scaleFontSize : 12, | |
scaleFontStyle : "normal", | |
scaleFontColor : "#666", | |
scaleShowGridLines : true, | |
scaleGridLineColor : "rgba(0,0,0,.05)", | |
scaleGridLineWidth : 1, | |
bezierCurve : true, | |
pointDot : true, | |
pointDotRadius : 4, | |
pointDotStrokeWidth : 2, | |
datasetStroke : true, | |
datasetStrokeWidth : 2, | |
datasetFill : true, | |
animation : true, | |
animationSteps : 60, | |
animationEasing : "easeOutQuart", | |
onAnimationComplete : null | |
}; | |
var config = (options) ? mergeChartConfig(chart.Line.defaults,options) : chart.Line.defaults; | |
return new Line(data,config,context); | |
} | |
this.Bar = function(data,options){ | |
chart.Bar.defaults = { | |
scaleOverlay : false, | |
scaleOverride : false, | |
scaleSteps : null, | |
scaleStepWidth : null, | |
scaleStartValue : null, | |
scaleLineColor : "rgba(0,0,0,.1)", | |
scaleLineWidth : 1, | |
scaleShowLabels : true, | |
scaleLabel : "<%=value%>", | |
scaleFontFamily : "'menlo'", | |
scaleFontSize : 12, | |
scaleFontStyle : "normal", | |
scaleFontColor : "#666", | |
scaleShowGridLines : true, | |
scaleGridLineColor : "rgba(0,0,0,.05)", | |
scaleGridLineWidth : 1, | |
barShowStroke : true, | |
barStrokeWidth : 2, | |
barValueSpacing : 5, | |
barDatasetSpacing : 1, | |
animation : true, | |
animationSteps : 60, | |
animationEasing : "easeOutQuart", | |
onAnimationComplete : null | |
}; | |
var config = (options) ? mergeChartConfig(chart.Bar.defaults,options) : chart.Bar.defaults; | |
return new Bar(data,config,context); | |
} | |
var clear = function(c){ | |
c.clearRect(0, 0, width, height); | |
}; | |
var PolarArea = function(data,config,ctx){ | |
var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString; | |
calculateDrawingSizes(); | |
valueBounds = getValueBounds(); | |
labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null; | |
//Check and set the scale | |
if (!config.scaleOverride){ | |
calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); | |
} | |
else { | |
calculatedScale = { | |
steps : config.scaleSteps, | |
stepValue : config.scaleStepWidth, | |
graphMin : config.scaleStartValue, | |
labels : [] | |
} | |
populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); | |
} | |
scaleHop = maxSize/(calculatedScale.steps); | |
//Wrap in an animation loop wrapper | |
animationLoop(config,drawScale,drawAllSegments,ctx); | |
function calculateDrawingSizes(){ | |
maxSize = (Min([width,height])/2); | |
//Remove whatever is larger - the font size or line width. | |
maxSize -= Max([config.scaleFontSize*0.5,config.scaleLineWidth*0.5]); | |
labelHeight = config.scaleFontSize*2; | |
//If we're drawing the backdrop - add the Y padding to the label height and remove from drawing region. | |
if (config.scaleShowLabelBackdrop){ | |
labelHeight += (2 * config.scaleBackdropPaddingY); | |
maxSize -= config.scaleBackdropPaddingY*1.5; | |
} | |
scaleHeight = maxSize; | |
//If the label height is less than 5, set it to 5 so we don't have lines on top of each other. | |
labelHeight = Default(labelHeight,5); | |
} | |
function drawScale(){ | |
for (var i=0; i<calculatedScale.steps; i++){ | |
//If the line object is there | |
if (config.scaleShowLine){ | |
ctx.beginPath(); | |
ctx.arc(width/2, height/2, scaleHop * (i + 1), 0, (Math.PI * 2), true); | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.stroke(); | |
} | |
if (config.scaleShowLabels){ | |
ctx.textAlign = "center"; | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize + "px " + config.scaleFontFamily; | |
var label = calculatedScale.labels[i]; | |
//If the backdrop object is within the font object | |
if (config.scaleShowLabelBackdrop){ | |
var textWidth = ctx.measureText(label).width; | |
ctx.fillStyle = config.scaleBackdropColor; | |
ctx.beginPath(); | |
ctx.rect( | |
Math.round(width/2 - textWidth/2 - config.scaleBackdropPaddingX), //X | |
Math.round(height/2 - (scaleHop * (i + 1)) - config.scaleFontSize*0.5 - config.scaleBackdropPaddingY),//Y | |
Math.round(textWidth + (config.scaleBackdropPaddingX*2)), //Width | |
Math.round(config.scaleFontSize + (config.scaleBackdropPaddingY*2)) //Height | |
); | |
ctx.fill(); | |
} | |
ctx.textBaseline = "middle"; | |
ctx.fillStyle = config.scaleFontColor; | |
ctx.fillText(label,width/2,height/2 - (scaleHop * (i + 1))); | |
} | |
} | |
} | |
function drawAllSegments(animationDecimal){ | |
var startAngle = -Math.PI/2, | |
angleStep = (Math.PI*2)/data.length, | |
scaleAnimation = 1, | |
rotateAnimation = 1; | |
if (config.animation) { | |
if (config.animateScale) { | |
scaleAnimation = animationDecimal; | |
} | |
if (config.animateRotate){ | |
rotateAnimation = animationDecimal; | |
} | |
} | |
for (var i=0; i<data.length; i++){ | |
ctx.beginPath(); | |
ctx.arc(width/2,height/2,scaleAnimation * calculateOffset(data[i].value,calculatedScale,scaleHop),startAngle, startAngle + rotateAnimation*angleStep, false); | |
ctx.lineTo(width/2,height/2); | |
ctx.closePath(); | |
ctx.fillStyle = data[i].color; | |
ctx.fill(); | |
if(config.segmentShowStroke){ | |
ctx.strokeStyle = config.segmentStrokeColor; | |
ctx.lineWidth = config.segmentStrokeWidth; | |
ctx.stroke(); | |
} | |
startAngle += rotateAnimation*angleStep; | |
} | |
} | |
function getValueBounds() { | |
var upperValue = Number.MIN_VALUE; | |
var lowerValue = Number.MAX_VALUE; | |
for (var i=0; i<data.length; i++){ | |
if (data[i].value > upperValue) {upperValue = data[i].value;} | |
if (data[i].value < lowerValue) {lowerValue = data[i].value;} | |
}; | |
var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); | |
var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); | |
return { | |
maxValue : upperValue, | |
minValue : lowerValue, | |
maxSteps : maxSteps, | |
minSteps : minSteps | |
}; | |
} | |
} | |
var Radar = function (data,config,ctx) { | |
var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString; | |
//If no labels are defined set to an empty array, so referencing length for looping doesn't blow up. | |
if (!data.labels) data.labels = []; | |
calculateDrawingSizes(); | |
var valueBounds = getValueBounds(); | |
labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null; | |
//Check and set the scale | |
if (!config.scaleOverride){ | |
calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); | |
} | |
else { | |
calculatedScale = { | |
steps : config.scaleSteps, | |
stepValue : config.scaleStepWidth, | |
graphMin : config.scaleStartValue, | |
labels : [] | |
} | |
populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); | |
} | |
scaleHop = maxSize/(calculatedScale.steps); | |
animationLoop(config,drawScale,drawAllDataPoints,ctx); | |
//Radar specific functions. | |
function drawAllDataPoints(animationDecimal){ | |
var rotationDegree = (2*Math.PI)/data.datasets[0].data.length; | |
ctx.save(); | |
//translate to the centre of the canvas. | |
ctx.translate(width/2,height/2); | |
//We accept multiple data sets for radar charts, so show loop through each set | |
for (var i=0; i<data.datasets.length; i++){ | |
ctx.beginPath(); | |
ctx.moveTo(0,animationDecimal*(-1*calculateOffset(data.datasets[i].data[0],calculatedScale,scaleHop))); | |
for (var j=1; j<data.datasets[i].data.length; j++){ | |
ctx.rotate(rotationDegree); | |
ctx.lineTo(0,animationDecimal*(-1*calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop))); | |
} | |
ctx.closePath(); | |
ctx.fillStyle = data.datasets[i].fillColor; | |
ctx.strokeStyle = data.datasets[i].strokeColor; | |
ctx.lineWidth = config.datasetStrokeWidth; | |
ctx.fill(); | |
ctx.stroke(); | |
if (config.pointDot){ | |
ctx.fillStyle = data.datasets[i].pointColor; | |
ctx.strokeStyle = data.datasets[i].pointStrokeColor; | |
ctx.lineWidth = config.pointDotStrokeWidth; | |
for (var k=0; k<data.datasets[i].data.length; k++){ | |
ctx.rotate(rotationDegree); | |
ctx.beginPath(); | |
ctx.arc(0,animationDecimal*(-1*calculateOffset(data.datasets[i].data[k],calculatedScale,scaleHop)),config.pointDotRadius,2*Math.PI,false); | |
ctx.fill(); | |
ctx.stroke(); | |
} | |
} | |
ctx.rotate(rotationDegree); | |
} | |
ctx.restore(); | |
} | |
function drawScale(){ | |
var rotationDegree = (2*Math.PI)/data.datasets[0].data.length; | |
ctx.save(); | |
ctx.translate(width / 2, height / 2); | |
if (config.angleShowLineOut){ | |
ctx.strokeStyle = config.angleLineColor; | |
ctx.lineWidth = config.angleLineWidth; | |
for (var h=0; h<data.datasets[0].data.length; h++){ | |
ctx.rotate(rotationDegree); | |
ctx.beginPath(); | |
ctx.moveTo(0,0); | |
ctx.lineTo(0,-maxSize); | |
ctx.stroke(); | |
} | |
} | |
for (var i=0; i<calculatedScale.steps; i++){ | |
ctx.beginPath(); | |
if(config.scaleShowLine){ | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.moveTo(0,-scaleHop * (i+1)); | |
for (var j=0; j<data.datasets[0].data.length; j++){ | |
ctx.rotate(rotationDegree); | |
ctx.lineTo(0,-scaleHop * (i+1)); | |
} | |
ctx.closePath(); | |
ctx.stroke(); | |
} | |
if (config.scaleShowLabels){ | |
ctx.textAlign = 'center'; | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; | |
ctx.textBaseline = "middle"; | |
if (config.scaleShowLabelBackdrop){ | |
var textWidth = ctx.measureText(calculatedScale.labels[i]).width; | |
ctx.fillStyle = config.scaleBackdropColor; | |
ctx.beginPath(); | |
ctx.rect( | |
Math.round(- textWidth/2 - config.scaleBackdropPaddingX), //X | |
Math.round((-scaleHop * (i + 1)) - config.scaleFontSize*0.5 - config.scaleBackdropPaddingY),//Y | |
Math.round(textWidth + (config.scaleBackdropPaddingX*2)), //Width | |
Math.round(config.scaleFontSize + (config.scaleBackdropPaddingY*2)) //Height | |
); | |
ctx.fill(); | |
} | |
ctx.fillStyle = config.scaleFontColor; | |
ctx.fillText(calculatedScale.labels[i],0,-scaleHop*(i+1)); | |
} | |
} | |
for (var k=0; k<data.labels.length; k++){ | |
ctx.font = config.pointLabelFontStyle + " " + config.pointLabelFontSize+"px " + config.pointLabelFontFamily; | |
ctx.fillStyle = config.pointLabelFontColor; | |
var opposite = Math.sin(rotationDegree*k) * (maxSize + config.pointLabelFontSize); | |
var adjacent = Math.cos(rotationDegree*k) * (maxSize + config.pointLabelFontSize); | |
if(rotationDegree*k == Math.PI || rotationDegree*k == 0){ | |
ctx.textAlign = "center"; | |
} | |
else if(rotationDegree*k > Math.PI){ | |
ctx.textAlign = "right"; | |
} | |
else{ | |
ctx.textAlign = "left"; | |
} | |
ctx.textBaseline = "middle"; | |
ctx.fillText(data.labels[k],opposite,-adjacent); | |
} | |
ctx.restore(); | |
}; | |
function calculateDrawingSizes(){ | |
maxSize = (Min([width,height])/2); | |
labelHeight = config.scaleFontSize*2; | |
var labelLength = 0; | |
for (var i=0; i<data.labels.length; i++){ | |
ctx.font = config.pointLabelFontStyle + " " + config.pointLabelFontSize+"px " + config.pointLabelFontFamily; | |
var textMeasurement = ctx.measureText(data.labels[i]).width; | |
if(textMeasurement>labelLength) labelLength = textMeasurement; | |
} | |
//Figure out whats the largest - the height of the text or the width of what's there, and minus it from the maximum usable size. | |
maxSize -= Max([labelLength,((config.pointLabelFontSize/2)*1.5)]); | |
maxSize -= config.pointLabelFontSize; | |
maxSize = CapValue(maxSize, null, 0); | |
scaleHeight = maxSize; | |
//If the label height is less than 5, set it to 5 so we don't have lines on top of each other. | |
labelHeight = Default(labelHeight,5); | |
}; | |
function getValueBounds() { | |
var upperValue = Number.MIN_VALUE; | |
var lowerValue = Number.MAX_VALUE; | |
for (var i=0; i<data.datasets.length; i++){ | |
for (var j=0; j<data.datasets[i].data.length; j++){ | |
if (data.datasets[i].data[j] > upperValue){upperValue = data.datasets[i].data[j]} | |
if (data.datasets[i].data[j] < lowerValue){lowerValue = data.datasets[i].data[j]} | |
} | |
} | |
var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); | |
var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); | |
return { | |
maxValue : upperValue, | |
minValue : lowerValue, | |
maxSteps : maxSteps, | |
minSteps : minSteps | |
}; | |
} | |
} | |
var Pie = function(data,config,ctx){ | |
var segmentTotal = 0; | |
//In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge. | |
var pieRadius = Min([height/2,width/2]) - 5; | |
for (var i=0; i<data.length; i++){ | |
segmentTotal += data[i].value; | |
} | |
animationLoop(config,null,drawPieSegments,ctx); | |
function drawPieSegments (animationDecimal){ | |
var cumulativeAngle = -Math.PI/2, | |
scaleAnimation = 1, | |
rotateAnimation = 1; | |
if (config.animation) { | |
if (config.animateScale) { | |
scaleAnimation = animationDecimal; | |
} | |
if (config.animateRotate){ | |
rotateAnimation = animationDecimal; | |
} | |
} | |
for (var i=0; i<data.length; i++){ | |
var segmentAngle = rotateAnimation * ((data[i].value/segmentTotal) * (Math.PI*2)); | |
ctx.beginPath(); | |
ctx.arc(width/2,height/2,scaleAnimation * pieRadius,cumulativeAngle,cumulativeAngle + segmentAngle); | |
ctx.lineTo(width/2,height/2); | |
ctx.closePath(); | |
ctx.fillStyle = data[i].color; | |
ctx.fill(); | |
if(config.segmentShowStroke){ | |
ctx.lineWidth = config.segmentStrokeWidth; | |
ctx.strokeStyle = config.segmentStrokeColor; | |
ctx.stroke(); | |
} | |
cumulativeAngle += segmentAngle; | |
} | |
} | |
} | |
var Doughnut = function(data,config,ctx){ | |
var segmentTotal = 0; | |
//In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge. | |
var doughnutRadius = Min([height/2,width/2]) - 5; | |
var cutoutRadius = doughnutRadius * (config.percentageInnerCutout/100); | |
for (var i=0; i<data.length; i++){ | |
segmentTotal += data[i].value; | |
} | |
animationLoop(config,null,drawPieSegments,ctx); | |
function drawPieSegments (animationDecimal){ | |
var cumulativeAngle = -Math.PI/2, | |
scaleAnimation = 1, | |
rotateAnimation = 1; | |
if (config.animation) { | |
if (config.animateScale) { | |
scaleAnimation = animationDecimal; | |
} | |
if (config.animateRotate){ | |
rotateAnimation = animationDecimal; | |
} | |
} | |
for (var i=0; i<data.length; i++){ | |
var segmentAngle = rotateAnimation * ((data[i].value/segmentTotal) * (Math.PI*2)); | |
ctx.beginPath(); | |
ctx.arc(width/2,height/2,scaleAnimation * doughnutRadius,cumulativeAngle,cumulativeAngle + segmentAngle,false); | |
ctx.arc(width/2,height/2,scaleAnimation * cutoutRadius,cumulativeAngle + segmentAngle,cumulativeAngle,true); | |
ctx.closePath(); | |
ctx.fillStyle = data[i].color; | |
ctx.fill(); | |
if(config.segmentShowStroke){ | |
ctx.lineWidth = config.segmentStrokeWidth; | |
ctx.strokeStyle = config.segmentStrokeColor; | |
ctx.stroke(); | |
} | |
cumulativeAngle += segmentAngle; | |
} | |
} | |
} | |
var Line = function(data,config,ctx){ | |
var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY, rotateLabels = 0; | |
calculateDrawingSizes(); | |
valueBounds = getValueBounds(); | |
//Check and set the scale | |
labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : ""; | |
if (!config.scaleOverride){ | |
calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); | |
} | |
else { | |
calculatedScale = { | |
steps : config.scaleSteps, | |
stepValue : config.scaleStepWidth, | |
graphMin : config.scaleStartValue, | |
labels : [] | |
} | |
populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); | |
} | |
scaleHop = Math.floor(scaleHeight/calculatedScale.steps); | |
calculateXAxisSize(); | |
animationLoop(config,drawScale,drawLines,ctx); | |
function drawLines(animPc){ | |
for (var i=0; i<data.datasets.length; i++){ | |
ctx.strokeStyle = data.datasets[i].strokeColor; | |
ctx.lineWidth = config.datasetStrokeWidth; | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX, xAxisPosY - animPc*(calculateOffset(data.datasets[i].data[0],calculatedScale,scaleHop))) | |
for (var j=1; j<data.datasets[i].data.length; j++){ | |
if (config.bezierCurve){ | |
ctx.bezierCurveTo(xPos(j-0.5),yPos(i,j-1),xPos(j-0.5),yPos(i,j),xPos(j),yPos(i,j)); | |
} | |
else{ | |
ctx.lineTo(xPos(j),yPos(i,j)); | |
} | |
} | |
ctx.stroke(); | |
if (config.datasetFill){ | |
ctx.lineTo(yAxisPosX + (valueHop*(data.datasets[i].data.length-1)),xAxisPosY); | |
ctx.lineTo(yAxisPosX,xAxisPosY); | |
ctx.closePath(); | |
ctx.fillStyle = data.datasets[i].fillColor; | |
ctx.fill(); | |
} | |
else{ | |
ctx.closePath(); | |
} | |
if(config.pointDot){ | |
ctx.fillStyle = data.datasets[i].pointColor; | |
ctx.strokeStyle = data.datasets[i].pointStrokeColor; | |
ctx.lineWidth = config.pointDotStrokeWidth; | |
for (var k=0; k<data.datasets[i].data.length; k++){ | |
ctx.beginPath(); | |
ctx.arc(yAxisPosX + (valueHop *k),xAxisPosY - animPc*(calculateOffset(data.datasets[i].data[k],calculatedScale,scaleHop)),config.pointDotRadius,0,Math.PI*2,true); | |
ctx.fill(); | |
ctx.stroke(); | |
} | |
} | |
} | |
function yPos(dataSet,iteration){ | |
return xAxisPosY - animPc*(calculateOffset(data.datasets[dataSet].data[iteration],calculatedScale,scaleHop)); | |
} | |
function xPos(iteration){ | |
return yAxisPosX + (valueHop * iteration); | |
} | |
} | |
function drawScale(){ | |
//X axis line | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.beginPath(); | |
ctx.moveTo(width-widestXLabel/2+5,xAxisPosY); | |
ctx.lineTo(width-(widestXLabel/2)-xAxisLength-5,xAxisPosY); | |
ctx.stroke(); | |
if (rotateLabels > 0){ | |
ctx.save(); | |
ctx.textAlign = "right"; | |
} | |
else{ | |
ctx.textAlign = "center"; | |
} | |
ctx.fillStyle = config.scaleFontColor; | |
for (var i=0; i<data.labels.length; i++){ | |
ctx.save(); | |
if (rotateLabels > 0){ | |
ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize); | |
ctx.rotate(-(rotateLabels * (Math.PI/180))); | |
ctx.fillText(data.labels[i], 0,0); | |
ctx.restore(); | |
} | |
else{ | |
ctx.fillText(data.labels[i], yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize+3); | |
} | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY+3); | |
//Check i isnt 0, so we dont go over the Y axis twice. | |
if(config.scaleShowGridLines && i>0){ | |
ctx.lineWidth = config.scaleGridLineWidth; | |
ctx.strokeStyle = config.scaleGridLineColor; | |
ctx.lineTo(yAxisPosX + i * valueHop, 5); | |
} | |
else{ | |
ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY+3); | |
} | |
ctx.stroke(); | |
} | |
//Y axis | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX,xAxisPosY+5); | |
ctx.lineTo(yAxisPosX,5); | |
ctx.stroke(); | |
ctx.textAlign = "right"; | |
ctx.textBaseline = "middle"; | |
for (var j=0; j<calculatedScale.steps; j++){ | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX-3,xAxisPosY - ((j+1) * scaleHop)); | |
if (config.scaleShowGridLines){ | |
ctx.lineWidth = config.scaleGridLineWidth; | |
ctx.strokeStyle = config.scaleGridLineColor; | |
ctx.lineTo(yAxisPosX + xAxisLength + 5,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
else{ | |
ctx.lineTo(yAxisPosX-0.5,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
ctx.stroke(); | |
if (config.scaleShowLabels){ | |
ctx.fillText(calculatedScale.labels[j],yAxisPosX-8,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
} | |
} | |
function calculateXAxisSize(){ | |
var longestText = 1; | |
//if we are showing the labels | |
if (config.scaleShowLabels){ | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; | |
for (var i=0; i<calculatedScale.labels.length; i++){ | |
var measuredText = ctx.measureText(calculatedScale.labels[i]).width; | |
longestText = (measuredText > longestText)? measuredText : longestText; | |
} | |
//Add a little extra padding from the y axis | |
longestText +=10; | |
} | |
xAxisLength = width - longestText - widestXLabel; | |
valueHop = Math.floor(xAxisLength/(data.labels.length-1)); | |
yAxisPosX = width-widestXLabel/2-xAxisLength; | |
xAxisPosY = scaleHeight + config.scaleFontSize/2; | |
} | |
function calculateDrawingSizes(){ | |
maxSize = height; | |
//Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees. | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; | |
widestXLabel = 1; | |
for (var i=0; i<data.labels.length; i++){ | |
var textLength = ctx.measureText(data.labels[i]).width; | |
//If the text length is longer - make that equal to longest text! | |
widestXLabel = (textLength > widestXLabel)? textLength : widestXLabel; | |
} | |
if (width/data.labels.length < widestXLabel){ | |
rotateLabels = 45; | |
if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){ | |
rotateLabels = 90; | |
maxSize -= widestXLabel; | |
} | |
else{ | |
maxSize -= Math.sin(rotateLabels) * widestXLabel; | |
} | |
} | |
else{ | |
maxSize -= config.scaleFontSize; | |
} | |
//Add a little padding between the x line and the text | |
maxSize -= 5; | |
labelHeight = config.scaleFontSize; | |
maxSize -= labelHeight; | |
//Set 5 pixels greater than the font size to allow for a little padding from the X axis. | |
scaleHeight = maxSize; | |
//Then get the area above we can safely draw on. | |
} | |
function getValueBounds() { | |
var upperValue = Number.MIN_VALUE; | |
var lowerValue = Number.MAX_VALUE; | |
for (var i=0; i<data.datasets.length; i++){ | |
for (var j=0; j<data.datasets[i].data.length; j++){ | |
if ( data.datasets[i].data[j] > upperValue) { upperValue = data.datasets[i].data[j] }; | |
if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] }; | |
} | |
}; | |
var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); | |
var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); | |
return { | |
maxValue : upperValue, | |
minValue : lowerValue, | |
maxSteps : maxSteps, | |
minSteps : minSteps | |
}; | |
} | |
} | |
var Bar = function(data,config,ctx){ | |
var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY,barWidth, rotateLabels = 0; | |
calculateDrawingSizes(); | |
valueBounds = getValueBounds(); | |
//Check and set the scale | |
labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : ""; | |
if (!config.scaleOverride){ | |
calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); | |
} | |
else { | |
calculatedScale = { | |
steps : config.scaleSteps, | |
stepValue : config.scaleStepWidth, | |
graphMin : config.scaleStartValue, | |
labels : [] | |
} | |
populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); | |
} | |
scaleHop = Math.floor(scaleHeight/calculatedScale.steps); | |
calculateXAxisSize(); | |
animationLoop(config,drawScale,drawBars,ctx); | |
function drawBars(animPc){ | |
ctx.lineWidth = config.barStrokeWidth; | |
for (var i=0; i<data.datasets.length; i++){ | |
ctx.fillStyle = data.datasets[i].fillColor; | |
ctx.strokeStyle = data.datasets[i].strokeColor; | |
for (var j=0; j<data.datasets[i].data.length; j++){ | |
var barOffset = yAxisPosX + config.barValueSpacing + valueHop*j + barWidth*i + config.barDatasetSpacing*i + config.barStrokeWidth*i; | |
ctx.beginPath(); | |
ctx.moveTo(barOffset, xAxisPosY); | |
ctx.lineTo(barOffset, xAxisPosY - animPc*calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop)+(config.barStrokeWidth/2)); | |
ctx.lineTo(barOffset + barWidth, xAxisPosY - animPc*calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop)+(config.barStrokeWidth/2)); | |
ctx.lineTo(barOffset + barWidth, xAxisPosY); | |
if(config.barShowStroke){ | |
ctx.stroke(); | |
} | |
ctx.closePath(); | |
ctx.fill(); | |
} | |
} | |
} | |
function drawScale(){ | |
//X axis line | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.beginPath(); | |
ctx.moveTo(width-widestXLabel/2+5,xAxisPosY); | |
ctx.lineTo(width-(widestXLabel/2)-xAxisLength-5,xAxisPosY); | |
ctx.stroke(); | |
if (rotateLabels > 0){ | |
ctx.save(); | |
ctx.textAlign = "right"; | |
} | |
else{ | |
ctx.textAlign = "center"; | |
} | |
ctx.fillStyle = config.scaleFontColor; | |
for (var i=0; i<data.labels.length; i++){ | |
ctx.save(); | |
if (rotateLabels > 0){ | |
ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize); | |
ctx.rotate(-(rotateLabels * (Math.PI/180))); | |
ctx.fillText(data.labels[i], 0,0); | |
ctx.restore(); | |
} | |
else{ | |
ctx.fillText(data.labels[i], yAxisPosX + i*valueHop + valueHop/2,xAxisPosY + config.scaleFontSize+3); | |
} | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX + (i+1) * valueHop, xAxisPosY+3); | |
//Check i isnt 0, so we dont go over the Y axis twice. | |
ctx.lineWidth = config.scaleGridLineWidth; | |
ctx.strokeStyle = config.scaleGridLineColor; | |
ctx.lineTo(yAxisPosX + (i+1) * valueHop, 5); | |
ctx.stroke(); | |
} | |
//Y axis | |
ctx.lineWidth = config.scaleLineWidth; | |
ctx.strokeStyle = config.scaleLineColor; | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX,xAxisPosY+5); | |
ctx.lineTo(yAxisPosX,5); | |
ctx.stroke(); | |
ctx.textAlign = "right"; | |
ctx.textBaseline = "middle"; | |
for (var j=0; j<calculatedScale.steps; j++){ | |
ctx.beginPath(); | |
ctx.moveTo(yAxisPosX-3,xAxisPosY - ((j+1) * scaleHop)); | |
if (config.scaleShowGridLines){ | |
ctx.lineWidth = config.scaleGridLineWidth; | |
ctx.strokeStyle = config.scaleGridLineColor; | |
ctx.lineTo(yAxisPosX + xAxisLength + 5,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
else{ | |
ctx.lineTo(yAxisPosX-0.5,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
ctx.stroke(); | |
if (config.scaleShowLabels){ | |
ctx.fillText(calculatedScale.labels[j],yAxisPosX-8,xAxisPosY - ((j+1) * scaleHop)); | |
} | |
} | |
} | |
function calculateXAxisSize(){ | |
var longestText = 1; | |
//if we are showing the labels | |
if (config.scaleShowLabels){ | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; | |
for (var i=0; i<calculatedScale.labels.length; i++){ | |
var measuredText = ctx.measureText(calculatedScale.labels[i]).width; | |
longestText = (measuredText > longestText)? measuredText : longestText; | |
} | |
//Add a little extra padding from the y axis | |
longestText +=10; | |
} | |
xAxisLength = width - longestText - widestXLabel; | |
valueHop = Math.floor(xAxisLength/(data.labels.length)); | |
barWidth = (valueHop - config.scaleGridLineWidth*2 - (config.barValueSpacing*2) - (config.barDatasetSpacing*data.datasets.length-1) - ((config.barStrokeWidth/2)*data.datasets.length-1))/data.datasets.length; | |
yAxisPosX = width-widestXLabel/2-xAxisLength; | |
xAxisPosY = scaleHeight + config.scaleFontSize/2; | |
} | |
function calculateDrawingSizes(){ | |
maxSize = height; | |
//Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees. | |
ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; | |
widestXLabel = 1; | |
for (var i=0; i<data.labels.length; i++){ | |
var textLength = ctx.measureText(data.labels[i]).width; | |
//If the text length is longer - make that equal to longest text! | |
widestXLabel = (textLength > widestXLabel)? textLength : widestXLabel; | |
} | |
if (width/data.labels.length < widestXLabel){ | |
rotateLabels = 45; | |
if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){ | |
rotateLabels = 90; | |
maxSize -= widestXLabel; | |
} | |
else{ | |
maxSize -= Math.sin(rotateLabels) * widestXLabel; | |
} | |
} | |
else{ | |
maxSize -= config.scaleFontSize; | |
} | |
//Add a little padding between the x line and the text | |
maxSize -= 5; | |
labelHeight = config.scaleFontSize; | |
maxSize -= labelHeight; | |
//Set 5 pixels greater than the font size to allow for a little padding from the X axis. | |
scaleHeight = maxSize; | |
//Then get the area above we can safely draw on. | |
} | |
function getValueBounds() { | |
var upperValue = Number.MIN_VALUE; | |
var lowerValue = Number.MAX_VALUE; | |
for (var i=0; i<data.datasets.length; i++){ | |
for (var j=0; j<data.datasets[i].data.length; j++){ | |
if ( data.datasets[i].data[j] > upperValue) { upperValue = data.datasets[i].data[j] }; | |
if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] }; | |
} | |
}; | |
var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); | |
var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); | |
return { | |
maxValue : upperValue, | |
minValue : lowerValue, | |
maxSteps : maxSteps, | |
minSteps : minSteps | |
}; | |
} | |
} | |
function calculateOffset(val,calculatedScale,scaleHop){ | |
var outerValue = calculatedScale.steps * calculatedScale.stepValue; | |
var adjustedValue = val - calculatedScale.graphMin; | |
var scalingFactor = CapValue(adjustedValue/outerValue,1,0); | |
return (scaleHop*calculatedScale.steps) * scalingFactor; | |
} | |
function animationLoop(config,drawScale,drawData,ctx){ | |
var animFrameAmount = (config.animation)? 1/CapValue(config.animationSteps,Number.MAX_VALUE,1) : 1, | |
easingFunction = animationOptions[config.animationEasing], | |
percentAnimComplete =(config.animation)? 0 : 1; | |
if (typeof drawScale !== "function") drawScale = function(){}; | |
requestAnimFrame(animLoop); | |
function animateFrame(){ | |
var easeAdjustedAnimationPercent =(config.animation)? CapValue(easingFunction(percentAnimComplete),null,0) : 1; | |
clear(ctx); | |
if(config.scaleOverlay){ | |
drawData(easeAdjustedAnimationPercent); | |
drawScale(); | |
} else { | |
drawScale(); | |
drawData(easeAdjustedAnimationPercent); | |
} | |
} | |
function animLoop(){ | |
//We need to check if the animation is incomplete (less than 1), or complete (1). | |
percentAnimComplete += animFrameAmount; | |
animateFrame(); | |
context.canvas.render(); | |
//Stop the loop continuing forever | |
if (percentAnimComplete <= 1){ | |
requestAnimFrame(animLoop); | |
} | |
else{ | |
if (typeof config.onAnimationComplete == "function") config.onAnimationComplete(); | |
} | |
} | |
} | |
//Declare global functions to be called within this namespace here. | |
// shim layer with setTimeout fallback | |
var requestAnimFrame = (function(){ | |
return function(callback) { | |
setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
function calculateScale(drawingHeight,maxSteps,minSteps,maxValue,minValue,labelTemplateString){ | |
var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum; | |
valueRange = maxValue - minValue; | |
rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange); | |
graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); | |
graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); | |
graphRange = graphMax - graphMin; | |
stepValue = Math.pow(10, rangeOrderOfMagnitude); | |
numberOfSteps = Math.round(graphRange / stepValue); | |
//Compare number of steps to the max and min for that size graph, and add in half steps if need be. | |
while(numberOfSteps < minSteps || numberOfSteps > maxSteps) { | |
if (numberOfSteps < minSteps){ | |
stepValue /= 2; | |
numberOfSteps = Math.round(graphRange/stepValue); | |
} | |
else{ | |
stepValue *=2; | |
numberOfSteps = Math.round(graphRange/stepValue); | |
} | |
}; | |
var labels = []; | |
populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue); | |
return { | |
steps : numberOfSteps, | |
stepValue : stepValue, | |
graphMin : graphMin, | |
labels : labels | |
} | |
function calculateOrderOfMagnitude(val){ | |
return Math.floor(Math.log(val) / Math.LN10); | |
} | |
} | |
//Populate an array of all the labels by interpolating the string. | |
function populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue) { | |
if (labelTemplateString) { | |
//Fix floating point errors by setting to fixed the on the same decimal as the stepValue. | |
for (var i = 1; i < numberOfSteps + 1; i++) { | |
labels.push(tmpl(labelTemplateString, {value: (graphMin + (stepValue * i)).toFixed(getDecimalPlaces(stepValue))})); | |
} | |
} | |
} | |
//Max value from array | |
function Max( array ){ | |
return Math.max.apply( Math, array ); | |
}; | |
//Min value from array | |
function Min( array ){ | |
return Math.min.apply( Math, array ); | |
}; | |
//Default if undefined | |
function Default(userDeclared,valueIfFalse){ | |
if(!userDeclared){ | |
return valueIfFalse; | |
} else { | |
return userDeclared; | |
} | |
}; | |
//Is a number function | |
function isNumber(n) { | |
return !isNaN(parseFloat(n)) && isFinite(n); | |
} | |
//Apply cap a value at a high or low number | |
function CapValue(valueToCap, maxValue, minValue){ | |
if(isNumber(maxValue)) { | |
if( valueToCap > maxValue ) { | |
return maxValue; | |
} | |
} | |
if(isNumber(minValue)){ | |
if ( valueToCap < minValue ){ | |
return minValue; | |
} | |
} | |
return valueToCap; | |
} | |
function getDecimalPlaces (num){ | |
var numberOfDecimalPlaces; | |
if (num%1!=0){ | |
return num.toString().split(".")[1].length | |
} | |
else{ | |
return 0; | |
} | |
} | |
function mergeChartConfig(defaults,userDefined){ | |
var returnObj = {}; | |
for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; } | |
for (var attrname in userDefined) { returnObj[attrname] = userDefined[attrname]; } | |
return returnObj; | |
} | |
//Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ | |
var cache = {}; | |
function tmpl(str, data){ | |
// Figure out if we're getting a template, or if we need to | |
// load the template - and be sure to cache the result. | |
var fn = !/\W/.test(str) ? | |
cache[str] = cache[str] || | |
tmpl(document.getElementById(str).innerHTML) : | |
// Generate a reusable function that will serve as a template | |
// generator (and which will be cached). | |
new Function("obj", | |
"var p=[],print=function(){p.push.apply(p,arguments);};" + | |
// Introduce the data as local variables using with(){} | |
"with(obj){p.push('" + | |
// Convert the template into pure JavaScript | |
str | |
.replace(/[\r\t\n]/g, " ") | |
.split("<%").join("\t") | |
.replace(/((^|%>)[^\t]*)'/g, "$1\r") | |
.replace(/\t=(.*?)%>/g, "',$1,'") | |
.split("\t").join("');") | |
.split("%>").join("p.push('") | |
.split("\r").join("\\'") | |
+ "');}return p.join('');"); | |
// Provide some basic currying to the user | |
return data ? fn( data ) : fn; | |
}; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Chart = require('./chart.js') | |
var ac = require('ansi-canvas'); | |
var canvas = ac({small: true}); | |
var context = canvas.getContext('2d'); | |
var data = { | |
labels : ["January","February","March","April","May","June","July"], | |
datasets : [ | |
{ | |
fillColor : "rgba(220,220,220,0.5)", | |
strokeColor : "rgba(220,220,220,1)", | |
pointColor : "rgba(220,220,220,1)", | |
pointStrokeColor : "#fff", | |
data : [65,59,90,81,56,55,40] | |
}, | |
{ | |
fillColor : "rgba(151,187,205,0.5)", | |
strokeColor : "rgba(151,187,205,1)", | |
pointColor : "rgba(151,187,205,1)", | |
pointStrokeColor : "#fff", | |
data : [28,48,40,19,96,27,100] | |
} | |
] | |
} | |
var myNewChart = new Chart(context).Line(data); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment