From: Tanner Linsley Date: Sat, 16 May 2015 04:34:08 +0000 (-0600) Subject: Doughnut/Pie Chart refactor X-Git-Tag: v2.0-alpha~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=872c92cd4b3893101d368f845a90b5f31a4bb6d4;p=thirdparty%2FChart.js.git Doughnut/Pie Chart refactor --- diff --git a/samples/doughnut.html b/samples/doughnut.html index 125beaf46..145ff450a 100644 --- a/samples/doughnut.html +++ b/samples/doughnut.html @@ -30,34 +30,36 @@ return Math.round(Math.random() * 255) }; - var doughnutData = [{ - value: randomScalingFactor(), - color: "#F7464A", - highlight: "#FF5A5E", - label: "Red" - }, { - value: randomScalingFactor(), - color: "#46BFBD", - highlight: "#5AD3D1", - label: "Green" - }, { - value: randomScalingFactor(), - color: "#FDB45C", - highlight: "#FFC870", - label: "Yellow" - }, { - value: randomScalingFactor(), - color: "#949FB1", - highlight: "#A8B3C5", - label: "Grey" - }, { - value: randomScalingFactor(), - color: "#4D5360", - highlight: "#616774", - label: "Dark Grey" - } + var doughnutData = { + data: [{ + value: randomScalingFactor(), + backgroundColor: "#F7464A", + hoverBackgroundColor: "#FF5A5E", + label: "Red" + }, { + value: randomScalingFactor(), + backgroundColor: "#46BFBD", + hoverBackgroundColor: "#5AD3D1", + label: "Green" + }, { + value: randomScalingFactor(), + backgroundColor: "#FDB45C", + hoverBackgroundColor: "#FFC870", + label: "Yellow" + }, { + value: randomScalingFactor(), + backgroundColor: "#949FB1", + hoverBackgroundColor: "#A8B3C5", + label: "Grey" + }, { + value: randomScalingFactor(), + backgroundColor: "#4D5360", + hoverBackgroundColor: "#616774", + label: "Dark Grey" + } - ]; + ] + }; window.onload = function() { var ctx = document.getElementById("chart-area").getContext("2d"); @@ -67,9 +69,9 @@ }; $('#randomizeData').click(function() { - $.each(doughnutData, function(i, piece) { - doughnutData[i].value = randomScalingFactor(); - doughnutData[i].color = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)'; + $.each(doughnutData.data, function(i, datapoint) { + datapoint.value = randomScalingFactor(); + datapoint.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)'; }); window.myDoughnut.update(); }); diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 1d6651ab7..fbb6f5397 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -1299,28 +1299,27 @@ y: this.y + (Math.sin(centreAngle) * rangeFromCentre) }; }, - draw: function(animationPercent) { - - var easingDecimal = animationPercent || 1; + draw: function() { - var ctx = this.ctx; + var ctx = this._chart.ctx; + var vm = this._vm; ctx.beginPath(); - ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle); + ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle); - ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true); + ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true); ctx.closePath(); - ctx.strokeStyle = this.borderColor; - ctx.lineWidth = this.borderWidth; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; - ctx.fillStyle = this.backgroundColor; + ctx.fillStyle = vm.backgroundColor; ctx.fill(); ctx.lineJoin = 'bevel'; - if (this.showBorder) { + if (vm.borderWidth) { ctx.stroke(); } } diff --git a/src/Chart.Doughnut.js b/src/Chart.Doughnut.js index b9633aac2..64e3634ab 100644 --- a/src/Chart.Doughnut.js +++ b/src/Chart.Doughnut.js @@ -14,16 +14,16 @@ segmentStrokeColor: "#fff", //Number - The width of each segment stroke - segmentStrokeWidth: 2, + borderWidth: 2, //The percentage of the chart that we cut out of the middle. - percentageInnerCutout: 50, + cutoutPercentage: 50, - //Number - Amount of animation steps - animationSteps: 100, + // The duration of animations triggered by hover events + hoverAnimationDuration: 400, //String - Animation easing effect - animationEasing: "easeOutBounce", + animationEasing: "easeOutQuart", //Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, @@ -59,26 +59,29 @@ if (this.options.showTooltips) { helpers.bindEvents(this, this.options.tooltipEvents, this.onHover); } - this.calculateTotal(data); // Create new slice for each piece of data - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - dataset.metaData = []; - helpers.each(dataset.data, function(slice, index) { - var metaSlice = new this.Slice(); - if (typeof slice == 'number') { - helpers.extend(metaSlice, { - value: slice - }); - } else { - helpers.extend(metaSlice, slice); - } - if (!metaSlice.color) { - slice.color = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)'; - } - metaSlice.save(); - dataset.metaData.push(metaSlice); - }, this); + this.data.metaData = []; + helpers.each(this.data.data, function(slice, index) { + var metaSlice = new this.Slice(); + if (typeof slice == 'number') { + helpers.extend(metaSlice, { + value: slice + }); + } else { + helpers.extend(metaSlice, slice); + } + helpers.extend(metaSlice, { + startAngle: Math.PI * 1.5, + circumference: (this.options.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value), + outerRadius: (this.options.animateScale) ? 0 : this.outerRadius, + innerRadius: (this.options.animateScale) ? 0 : (this.outerRadius / 100) * this.options.percentageInnerCutout, + }); + if (!metaSlice.backgroundColor) { + slice.backgroundColor = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)'; + } + metaSlice.save(); + this.data.metaData.push(metaSlice); }, this); // Create tooltip instance exclusively for this chart with some defaults. @@ -90,7 +93,7 @@ this.update(); }, - onHover: function(evt) { + onHover: function(e) { // If exiting chart if (e.type == 'mouseout') { @@ -100,18 +103,7 @@ this.lastActive = this.lastActive || []; // Find Active Elements - this.active = function() { - switch (this.options.hoverMode) { - case 'single': - return this.getElementAtEvent(e); - case 'label': - return this.getElementsAtEvent(e); - case 'dataset': - return this.getDatasetAtEvent(e); - default: - return e; - } - }.call(this); + this.active = this.getSliceAtEvent(e); // On Hover hook if (this.options.onHover) { @@ -120,41 +112,14 @@ // Remove styling for last active (even if it may still be active) if (this.lastActive.length) { - switch (this.options.hoverMode) { - case 'single': - this.lastActive[0].backgroundColor = this.data.datasets[0].backgroundColor; - break; - case 'label': - for (var i = 0; i < this.lastActive.length; i++) { - this.lastActive[i].backgroundColor = this.data.datasets[this.lastActive[i]._datasetIndex].backgroundColor; - } - break; - case 'dataset': - break; - default: - // Don't change anything - } + this.lastActive[0].backgroundColor = this.data.data[this.lastActive[0]._index].backgroundColor; } // Built in hover styling if (this.active.length && this.options.hoverMode) { - switch (this.options.hoverMode) { - case 'single': - this.active[0].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.5).darken(0.35).rgbString(); - break; - case 'label': - for (var i = 0; i < this.active.length; i++) { - this.active[i].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.5).darken(0.35).rgbString(); - } - break; - case 'dataset': - break; - default: - // Don't change anything - } + this.active[0].backgroundColor = this.data.data[this.active[0]._index].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.5).darken(0.35).rgbString(); } - // Built in Tooltips if (this.options.showTooltips) { @@ -205,15 +170,15 @@ return this; }, - getSegmentsAtEvent: function(e) { - var segmentsArray = []; + getSliceAtEvent: function(e) { + var elements = []; var location = helpers.getRelativePosition(e); - helpers.each(this.segments, function(segment) { - if (segment.inRange(location.x, location.y)) segmentsArray.push(segment); + helpers.each(this.data.metaData, function(slice, index) { + if (slice.inRange(location.x, location.y)) elements.push(slice); }, this); - return segmentsArray; + return elements; }, calculateCircumference: function(value) { if (this.total > 0) { @@ -222,97 +187,72 @@ return 0; } }, - calculateTotal: function(data) { - this.total = 0; - helpers.each(data, function(segment) { - this.total += Math.abs(segment.value); - }, this); - }, update: function() { - this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.segmentStrokeWidth / 2) / 2; + // Calc Total + this.total = 0; + helpers.each(this.data.data, function(slice) { + this.total += Math.abs(slice.value); + }, this); + this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.borderWidth / 2) / 2; // Map new data to data points - helpers.each(this.data, function(segment, i) { - helpers.extend(this.segments[i], { + helpers.each(this.data.metaData, function(slice, index) { + + var datapoint = this.data.data[index]; + + helpers.extend(slice, { + _index: index, x: this.chart.width / 2, y: this.chart.height / 2, - value: segment.value, - backgroundColor: segment.color, - hoverBackgroundColor: segment.highlight || segment.color, - borderWidth: this.options.segmentStrokeWidth, + value: datapoint.value, + label: datapoint.label, + circumference: this.calculateCircumference(datapoint.value), + outerRadius: this.outerRadius, + innerRadius: (this.outerRadius / 100) * this.options.cutoutPercentage, + + backgroundColor: datapoint.backgroundColor, + hoverBackgroundColor: datapoint.hoverBackgroundColor || datapoint.backgroundColor, + borderWidth: this.options.borderWidth, borderColor: this.options.segmentStrokeColor, - label: segment.label, - startAngle: Math.PI * 1.5, - circumference: (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), - innerRadius: (this.options.animateScale) ? 0 : (this.outerRadius / 100) * this.options.percentageInnerCutout, - outerRadius: (this.options.animateScale) ? 0 : this.outerRadius, }); - }, this); - this.calculateTotal(this.segments); + helpers.extend(slice, { + endAngle: slice.startAngle + slice.circumference, + }); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement) { - activeElement.restore(['backgroundColor']); - }); + if (index === 0) { + slice.startAngle = Math.PI * 1.5; + } - helpers.each(this.segments, function(segment) { - segment.save(); - }); - this.render(); - }, + //Check to see if it's the last slice, if not get the next and update its start angle + if (index < this.data.data.length - 1) { + this.data.metaData[index + 1].startAngle = slice.endAngle; + } - removeData: function(atIndex) { - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length - 1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, + slice.pivot(); - reflow: function() { - helpers.extend(this.Slice.prototype, { - x: this.chart.width / 2, - y: this.chart.height / 2 - }); - this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.segmentStrokeWidth / 2) / 2; - helpers.each(this.segments, function(segment) { - segment.update({ - outerRadius: this.outerRadius, - innerRadius: (this.outerRadius / 100) * this.options.percentageInnerCutout - }); }, this); + + this.render(); }, draw: function(easeDecimal) { - var animDecimal = (easeDecimal) ? easeDecimal : 1; + easeDecimal = easeDecimal || 1; this.clear(); - helpers.each(this.segments, function(segment, index) { - segment.transition({ - circumference: this.calculateCircumference(segment.value), - outerRadius: this.outerRadius, - innerRadius: (this.outerRadius / 100) * this.options.percentageInnerCutout - }, animDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - segment.draw(); - if (index === 0) { - segment.startAngle = Math.PI * 1.5; - } - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length - 1) { - this.segments[index + 1].startAngle = segment.endAngle; - } + helpers.each(this.data.metaData, function(slice, index) { + slice.transition(easeDecimal).draw(); }, this); + this.tooltip.transition(easeDecimal).draw(); } }); Chart.types.Doughnut.extend({ name: "Pie", defaults: helpers.merge(defaultConfig, { - percentageInnerCutout: 0 + cutoutPercentage: 0 }) });