From: Tanner Linsley Date: Tue, 16 Jun 2015 01:14:11 +0000 (-0600) Subject: Core improvements, arc & doughnut partially working X-Git-Tag: 2.0.0-alpha3~10^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=74f161b548300b0144bf5cf520ef8d4e61bfce48;p=thirdparty%2FChart.js.git Core improvements, arc & doughnut partially working --- diff --git a/samples/doughnut.html b/samples/doughnut.html index dc874e754..7e529acd3 100644 --- a/samples/doughnut.html +++ b/samples/doughnut.html @@ -31,6 +31,7 @@ }; var config = { + type: 'doughnut', data: { datasets: [{ data: [ @@ -93,7 +94,8 @@ window.onload = function() { var ctx = document.getElementById("chart-area").getContext("2d"); - window.myDoughnut = new Chart(ctx).Doughnut(config); + window.myDoughnut = new Chart(ctx, config); + console.log(window.myDoughnut); }; $('#randomizeData').click(function() { diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index 2324a7850..21ae0c96a 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -1,33 +1,179 @@ (function() { "use strict"; - return; - var root = this, Chart = root.Chart, //Cache a local reference to Chart.helpers helpers = Chart.helpers; - var defaultConfig = { - + Chart.defaults.doughnut = { animation: { //Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, - //Boolean - Whether we animate scaling the Doughnut from the centre animateScale: false, }, - hover: { mode: 'single' }, - //The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, }; + + Chart.controllers.doughnut = function(chart, datasetIndex) { + this.initialize.call(this, chart, datasetIndex); + }; + + helpers.extend(Chart.controllers.doughnut.prototype, { + + initialize: function(chart, datasetIndex) { + this.chart = chart; + this.index = datasetIndex; + this.linkScales(); + this.addElements(); + }, + + linkScales: function() { + // no scales for doughnut + }, + + getDataset: function() { + return this.chart.data.datasets[this.index]; + }, + + getScaleForId: function(scaleID) { + return this.chart.scales[scaleID]; + }, + + addElements: function() { + this.getDataset().metaData = this.getDataset().metaData || []; + helpers.each(this.getDataset().data, function(value, index) { + this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({ + _chart: this.chart.chart, + _datasetIndex: this.index, + _index: index, + }); + }, this); + }, + + reset: function() { + this.update(true); + }, + + update: function(reset) { + + this.chart.outerRadius = (helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2; + this.chart.innerRadius = this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1; + this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length; + + + this.getDataset().total = 0; + helpers.each(this.getDataset().data, function(value) { + this.getDataset().total += Math.abs(value); + }, this); + + this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index); + this.innerRadius = this.chart.outerRadius - this.chart.radiusLength; + + helpers.each(this.getDataset().metaData, function(arc, index) { + + var resetModel = { + x: this.chart.chart.width / 2, + y: this.chart.chart.height / 2, + startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function + circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(this.getDataset().data[index]), + outerRadius: (this.chart.options.animation.animateScale) ? 0 : this.outerRadius, + innerRadius: (this.chart.options.animation.animateScale) ? 0 : this.innerRadius, + }; + + helpers.extend(arc, { + // Utility + _chart: this.chart.chart, + _datasetIndex: this.index, + _index: index, + + // Desired view properties + _model: reset ? resetModel : { + x: this.chart.chart.width / 2, + y: this.chart.chart.height / 2, + circumference: this.calculateCircumference(this.getDataset().data[index]), + outerRadius: this.outerRadius, + innerRadius: this.innerRadius, + + backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor), + hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor), + borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth), + borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor), + + label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index]) + }, + }); + + if (!reset) { + + if (index === 0) { + arc._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function + } else { + arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle; + } + + arc._model.endAngle = arc._model.startAngle + arc._model.circumference; + + + //Check to see if it's the last arc, if not get the next and update its start angle + if (index < this.getDataset().data.length - 1) { + this.getDataset().metaData[index + 1]._model.startAngle = arc._model.endAngle; + } + } + + arc.pivot(); + }, this); + }, + + draw: function(ease) { + var easingDecimal = ease || 1; + helpers.each(this.getDataset().metaData, function(arc, index) { + arc.transition(easingDecimal).draw(); + }, this); + }, + + + + setHoverStyle: function(rectangle) { + var dataset = this.chart.data.datasets[rectangle._datasetIndex]; + var index = rectangle._index; + + rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); + rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString()); + rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangle._model.borderWidth); + }, + + removeHoverStyle: function(rectangle) { + // TODO + }, + + calculateCircumference: function(value) { + if (this.getDataset().total > 0) { + return (Math.PI * 2) * (value / this.getDataset().total); + } else { + return 0; + } + }, + + }); + + + + + + + + + + return; + Chart.Type.extend({ //Passing in a name registers this chart in the Chart namespace name: "Doughnut", @@ -38,7 +184,7 @@ initialize: function() { //Set up tooltip events on the chart - helpers.bindEvents(this, this.options.events, this.events); + helpers.bindEvents(this, this.chart.options.events, this.events); //Create a new bar for each piece of data helpers.each(this.data.datasets, function(dataset, datasetIndex) { @@ -67,16 +213,9 @@ }, - calculateCircumference: function(dataset, value) { - if (dataset.total > 0) { - return (Math.PI * 2) * (value / dataset.total); - } else { - return 0; - } - }, resetElements: function() { - this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2; - this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1; + this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2; + this.innerRadius = this.chart.options.cutoutPercentage ? (this.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1; this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length; // Update the points @@ -90,102 +229,40 @@ dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex); dataset.innerRadius = dataset.outerRadius - this.radiusLength; - helpers.each(dataset.metaData, function(slice, index) { - helpers.extend(slice, { + helpers.each(dataset.metaData, function(arc, index) { + helpers.extend(arc, { _model: { x: this.chart.width / 2, y: this.chart.height / 2, startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function - circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value), - outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius, - innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius, + circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value), + outerRadius: (this.chart.options.animation.animateScale) ? 0 : dataset.outerRadius, + innerRadius: (this.chart.options.animation.animateScale) ? 0 : dataset.innerRadius, - backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor), - hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor), - borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth), - borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor), + backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.chart.options.elements.arc.backgroundColor), + hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor), + borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.chart.options.elements.arc.borderWidth), + borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.chart.options.elements.arc.borderColor), - label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index]) + label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.chart.data.labels[index]) }, }); - slice.pivot(); + arc.pivot(); }, this); }, this); }, update: function(animationDuration) { - this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2; - this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1; - this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length; - - - // Update the points - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - - dataset.total = 0; - helpers.each(dataset.data, function(value) { - dataset.total += Math.abs(value); - }, this); - - - dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex); - - dataset.innerRadius = dataset.outerRadius - this.radiusLength; - - helpers.each(dataset.metaData, function(slice, index) { - - helpers.extend(slice, { - // Utility - _chart: this.chart, - _datasetIndex: datasetIndex, - _index: index, - - // Desired view properties - _model: { - x: this.chart.width / 2, - y: this.chart.height / 2, - circumference: this.calculateCircumference(dataset, dataset.data[index]), - outerRadius: dataset.outerRadius, - innerRadius: dataset.innerRadius, - - backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor), - hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor), - borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth), - borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor), - - label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index]) - }, - }); - - if (index === 0) { - slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function - } else { - slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle; - } - - slice._model.endAngle = slice._model.startAngle + slice._model.circumference; - - - //Check to see if it's the last slice, if not get the next and update its start angle - if (index < dataset.data.length - 1) { - dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle; - } - - slice.pivot(); - }, this); - - }, this); - this.render(animationDuration); }, draw: function(easeDecimal) { easeDecimal = easeDecimal || 1; this.clear(); - this.eachElement(function(slice) { - slice.transition(easeDecimal).draw(); + this.eachElement(function(arc) { + arc.transition(easeDecimal).draw(); }, this); this.tooltip.transition(easeDecimal).draw(); @@ -200,7 +277,7 @@ } else { this.active = function() { - switch (this.options.hover.mode) { + switch (this.chart.options.hover.mode) { case 'single': return this.getSliceAtEvent(e); case 'label': @@ -214,13 +291,13 @@ } // On Hover hook - if (this.options.hover.onHover) { - this.options.hover.onHover.call(this, this.active); + if (this.chart.options.hover.onHover) { + this.chart.options.hover.onHover.call(this, this.active); } if (e.type == 'mouseup' || e.type == 'click') { - if (this.options.onClick) { - this.options.onClick.call(this, e, this.active); + if (this.chart.options.onClick) { + this.chart.options.onClick.call(this, e, this.active); } } @@ -228,23 +305,23 @@ var index; // Remove styling for last active (even if it may still be active) if (this.lastActive.length) { - switch (this.options.hover.mode) { + switch (this.chart.options.hover.mode) { case 'single': dataset = this.data.datasets[this.lastActive[0]._datasetIndex]; index = this.lastActive[0]._index; - this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor); - this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor); - this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth); + this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.chart.options.elements.arc.backgroundColor); + this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.chart.options.elements.arc.borderColor); + this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.chart.options.elements.arc.borderWidth); break; case 'label': for (var i = 0; i < this.lastActive.length; i++) { dataset = this.data.datasets[this.lastActive[i]._datasetIndex]; index = this.lastActive[i]._index; - this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor); - this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor); - this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth); + this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.chart.options.elements.arc.backgroundColor); + this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.chart.options.elements.arc.borderColor); + this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.chart.options.elements.arc.borderWidth); } break; case 'dataset': @@ -255,8 +332,8 @@ } // Built in hover styling - if (this.active.length && this.options.hover.mode) { - switch (this.options.hover.mode) { + if (this.active.length && this.chart.options.hover.mode) { + switch (this.chart.options.hover.mode) { case 'single': dataset = this.data.datasets[this.active[0]._datasetIndex]; index = this.active[0]._index; @@ -284,7 +361,7 @@ // Built in Tooltips - if (this.options.tooltips.enabled) { + if (this.chart.options.tooltips.enabled) { // The usual updates this.tooltip.initialize(); @@ -323,7 +400,7 @@ (this.lastActive.length && this.active.length && changed)) { this.stop(); - this.render(this.options.hover.animationDuration); + this.render(this.chart.options.hover.animationDuration); } } @@ -336,9 +413,9 @@ var location = helpers.getRelativePosition(e); - this.eachElement(function(slice, index) { - if (slice.inRange(location.x, location.y)) { - elements.push(slice); + this.eachElement(function(arc, index) { + if (arc.inRange(location.x, location.y)) { + elements.push(arc); } }, this); return elements; @@ -348,9 +425,9 @@ var location = helpers.getRelativePosition(e); - this.eachElement(function(slice, index) { - if (slice.inLabelRange(location.x, location.y)) { - elements.push(slice); + this.eachElement(function(arc, index) { + if (arc.inLabelRange(location.x, location.y)) { + elements.push(arc); } }, this); return elements; diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 55c4159d3..b7be643f0 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -89,30 +89,36 @@ this.scales = {}; // Build the x axes - helpers.each(this.options.scales.xAxes, function(xAxisOptions) { - var ScaleClass = Chart.scaleService.getScaleConstructor(xAxisOptions.type); - var scale = new ScaleClass({ - ctx: this.chart.ctx, - options: xAxisOptions, - data: this.data, - id: xAxisOptions.id, - }); - - this.scales[scale.id] = scale; - }, this); + if (this.options.scales) { + if (this.options.scales.xAxes && this.options.scales.xAxes.length) { + helpers.each(this.options.scales.xAxes, function(xAxisOptions) { + var ScaleClass = Chart.scaleService.getScaleConstructor(xAxisOptions.type); + var scale = new ScaleClass({ + ctx: this.chart.ctx, + options: xAxisOptions, + data: this.data, + id: xAxisOptions.id, + }); + + this.scales[scale.id] = scale; + }, this); + } - // Build the y axes - helpers.each(this.options.scales.yAxes, function(yAxisOptions) { - var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type); - var scale = new ScaleClass({ - ctx: this.chart.ctx, - options: yAxisOptions, - data: this.data, - id: yAxisOptions.id, - }); - - this.scales[scale.id] = scale; - }, this); + if (this.options.scales.yAxes && this.options.scales.yAxes.length) { + // Build the y axes + helpers.each(this.options.scales.yAxes, function(yAxisOptions) { + var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type); + var scale = new ScaleClass({ + ctx: this.chart.ctx, + options: yAxisOptions, + data: this.data, + id: yAxisOptions.id, + }); + + this.scales[scale.id] = scale; + }, this); + } + } Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height); }, diff --git a/src/elements/element.arc.js b/src/elements/element.arc.js index f5a3c628a..e09f0cf57 100644 --- a/src/elements/element.arc.js +++ b/src/elements/element.arc.js @@ -23,7 +23,7 @@ borderWidth: 2 }; - Chart.Arc = Chart.Element.extend({ + Chart.elements.Arc = Chart.Element.extend({ inLabelRange: function(mouseX) { var vm = this._view;