diff --git a/samples/bar.html b/samples/bar.html index c386c50fa..5146660b8 100644 --- a/samples/bar.html +++ b/samples/bar.html @@ -50,6 +50,8 @@ barChartData.datasets[1].data = [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]; window.myBar.update(); + + console.log(window.barChartData); }); diff --git a/src/Chart.Bar.js b/src/Chart.Bar.js index 8127d520a..0bd7b7e6b 100644 --- a/src/Chart.Bar.js +++ b/src/Chart.Bar.js @@ -75,8 +75,6 @@ } }); - this.datasets = []; - //Set up tooltip events on the chart if (this.options.showTooltips){ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ @@ -85,6 +83,7 @@ this.eachBars(function(bar){ bar.restore(['fillColor', 'strokeColor']); }); + helpers.each(activeBars, function(activeBar){ activeBar.fillColor = activeBar.highlightFill; activeBar.strokeColor = activeBar.highlightStroke; @@ -93,107 +92,83 @@ }); } + + //Declare the extension of the default point, to cater for the options passed in to the constructor this.BarClass = Chart.Rectangle.extend({ - strokeWidth : this.options.barStrokeWidth, - showStroke : this.options.barShowStroke, - ctx : this.chart.ctx + ctx : this.chart.ctx, + _vm: {} }); - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset,datasetIndex){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - bars : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.bars.push(new this.BarClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.strokeColor, - fillColor : dataset.fillColor, - highlightFill : dataset.highlightFill || dataset.fillColor, - highlightStroke : dataset.highlightStroke || dataset.strokeColor - })); - },this); - - },this); - + // Build Scale this.buildScale(data.labels); - this.BarClass.prototype.base = this.scale.endPoint; + //Create a new bar for each piece of data + helpers.each(this.data.datasets,function(dataset,datasetIndex){ + dataset.metaData = []; + helpers.each(dataset.data,function(dataPoint,index){ + dataset.metaData.push(new this.BarClass()); + },this); + },this); + // Set defaults for bars this.eachBars(function(bar, index, datasetIndex){ helpers.extend(bar, { - width : this.scale.calculateBarWidth(this.datasets.length), - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y: this.scale.endPoint + width : this.scale.calculateBarWidth(this.data.datasets.length), + x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, index), + y: this.scale.endPoint, }); + // Copy to view model bar.save(); }, this); - this.render(); + this.update(); }, update : function(){ - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(this.data.datasets,function(dataset,datasetIndex){ - - helpers.extend(this.datasets[datasetIndex], { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - }); - - helpers.each(dataset.data,function(dataPoint,index){ - helpers.extend(this.datasets[datasetIndex].bars[index], { - value : dataPoint, - label : this.data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.strokeColor, - fillColor : dataset.fillColor, - highlightFill : dataset.highlightFill || dataset.fillColor, - highlightStroke : dataset.highlightStroke || dataset.strokeColor - }); - },this); - - },this); this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - this.eachBars(function(bar){ - bar.save(); - }); + this.eachBars(function(bar, index, datasetIndex){ + helpers.extend(bar, { + width : this.scale.calculateBarWidth(this.data.datasets.length), + x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, index), + y: this.scale.calculateY(this.data.datasets[datasetIndex].data[index]), + value : this.data.datasets[datasetIndex].data[index], + label : this.data.labels[index], + datasetLabel: this.data.datasets[datasetIndex].label, + strokeColor : this.data.datasets[datasetIndex].strokeColor, + fillColor : this.data.datasets[datasetIndex].fillColor, + highlightFill : this.data.datasets[datasetIndex].highlightFill || this.data.datasets[datasetIndex].fillColor, + highlightStroke : this.data.datasets[datasetIndex].highlightStroke || this.data.datasets[datasetIndex].strokeColor, + _start: undefined + }); + }, this); + + this.render(); }, eachBars : function(callback){ - helpers.each(this.datasets,function(dataset, datasetIndex){ - helpers.each(dataset.bars, callback, this, datasetIndex); + helpers.each(this.data.datasets,function(dataset, datasetIndex){ + helpers.each(dataset.metaData, callback, this, datasetIndex); + },this); + }, + eachValue : function(callback){ + helpers.each(this.data.datasets,function(dataset, datasetIndex){ + helpers.each(dataset.data, callback, this, datasetIndex); },this); }, getBarsAtEvent : function(e){ var barsArray = [], eventPosition = helpers.getRelativePosition(e), datasetIterator = function(dataset){ - barsArray.push(dataset.bars[barIndex]); + barsArray.push(dataset.metaData[barIndex]); }, barIndex; - for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) { - for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) { - if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){ - helpers.each(this.datasets, datasetIterator); + for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; datasetIndex++) { + for (barIndex = 0; barIndex < this.data.datasets[datasetIndex].metaData.length; barIndex++) { + if (this.data.datasets[datasetIndex].metaData[barIndex].inRange(eventPosition.x,eventPosition.y)){ + helpers.each(this.data.datasets, datasetIterator); return barsArray; } } @@ -206,8 +181,8 @@ var dataTotal = function(){ var values = []; - self.eachBars(function(bar){ - values.push(bar.value); + self.eachValue(function(value){ + values.push(value); }); return values; }; @@ -263,16 +238,16 @@ //Map the values array for each of the datasets helpers.each(valuesArray,function(value,datasetIndex){ //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].bars.push(new this.BarClass({ + this.data.datasets[datasetIndex].bars.push(new this.BarClass({ value : value, label : label, - datasetLabel: this.datasets[datasetIndex].label, - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1), + datasetLabel: this.data.datasets[datasetIndex].label, + x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, this.scale.valuesCount+1), y: this.scale.endPoint, - width : this.scale.calculateBarWidth(this.datasets.length), + width : this.scale.calculateBarWidth(this.data.datasets.length), base : this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].strokeColor, - fillColor : this.datasets[datasetIndex].fillColor + strokeColor : this.data.datasets[datasetIndex].strokeColor, + fillColor : this.data.datasets[datasetIndex].fillColor })); },this); @@ -283,7 +258,7 @@ removeData : function(){ this.scale.removeXLabel(); //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ + helpers.each(this.data.datasets,function(dataset){ dataset.bars.shift(); },this); this.update(); @@ -300,28 +275,21 @@ this.scale.update(newScaleProps); }, draw : function(ease){ + var easingDecimal = ease || 1; this.clear(); - var ctx = this.chart.ctx; - this.scale.draw(easingDecimal); //Draw all the bars for each dataset - helpers.each(this.datasets,function(dataset,datasetIndex){ - helpers.each(dataset.bars,function(bar,index){ - if (bar.hasValue()){ - bar.base = this.scale.endPoint; - //Transition then draw - bar.transition({ - x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y : this.scale.calculateY(bar.value), - width : this.scale.calculateBarWidth(this.datasets.length) - }, easingDecimal).draw(); - } - },this); - - },this); + this.eachBars(function(bar, index, datasetIndex){ + if (bar.hasValue()){ + // Update the bar basepoint + bar.base = this.scale.endPoint; + //Transition + bar.transition(['x','y','width'], easingDecimal).draw(); + } + }, this); } }); diff --git a/src/Chart.Core.js b/src/Chart.Core.js index d0a70336d..bb8f3ece9 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -466,7 +466,6 @@ numberOfSteps = minSteps; stepValue = graphRange / numberOfSteps; } - return { steps : numberOfSteps, stepValue : stepValue, @@ -1021,12 +1020,12 @@ } if (ChartElements.length > 0){ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index - if (this.datasets && this.datasets.length > 1) { + if (this.data.datasets && this.data.datasets.length > 1) { var dataArray, dataIndex; - for (var i = this.datasets.length - 1; i >= 0; i--) { - dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments; + for (var i = this.data.datasets.length - 1; i >= 0; i--) { + dataArray = this.data.datasets[i].metaData; dataIndex = indexOf(dataArray, ChartElements[0]); if (dataIndex !== -1){ break; @@ -1045,8 +1044,8 @@ yMax, xMin, yMin; - helpers.each(this.datasets, function(dataset){ - dataCollection = dataset.points || dataset.bars || dataset.segments; + helpers.each(this.data.datasets, function(dataset){ + dataCollection = dataset.metaData; if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){ Elements.push(dataCollection[dataIndex]); } @@ -1060,8 +1059,8 @@ //Include any colour information about the element tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element)); tooltipColors.push({ - fill: element._saved.fillColor || element.fillColor, - stroke: element._saved.strokeColor || element.strokeColor + fill: element._vm.fillColor || element.fillColor, + stroke: element._vm.strokeColor || element.strokeColor }); }, this); @@ -1176,36 +1175,50 @@ Chart.Element = function(configuration){ extend(this,configuration); this.initialize.apply(this,arguments); - this.save(); }; extend(Chart.Element.prototype,{ initialize : function(){}, - restore : function(props){ - if (!props){ - extend(this,this._saved); - } else { - each(props,function(key){ - this[key] = this._saved[key]; - },this); + save: function(){ + this._vm = clone(this); + delete this._vm._vm; + return this; + }, + transition : function(props, ease){ + if(!this._start){ + this._start = clone(this._vm); } - return this; - }, - save : function(){ - this._saved = clone(this); - delete this._saved._saved; - return this; - }, - update : function(newProps){ - each(newProps,function(value,key){ - this._saved[key] = this[key]; - this[key] = value; - },this); - return this; - }, - transition : function(props,ease){ - each(props,function(value,key){ - this[key] = ((value - this._saved[key]) * ease) + this._saved[key]; + each(this,function(value, key){ + + // Only non-vm properties + if(key === '_vm' || !this.hasOwnProperty(key)){ + return; + } + + // Init if doesn't exist + if(!this._vm[key]){ + this._vm[key] = value || null; + return; + } + + // If transition property, do transition with ease (no pun intended) + if(props.indexOf(key) > -1){ + // Color transitions if possible + if(typeof value === 'string'){ + // TODO support color transitions + return; + } + // Everything else, presumably numbers + this._vm[key] = ((this[key] - this._start[key]) * ease) + this._start[key]; + return; + } + + // Non-transitionals + this._vm[key] = value; + },this); + if(ease === 1){ + delete this._start; + } return this; }, tooltipPosition : function(){ @@ -1325,16 +1338,19 @@ Chart.Rectangle = Chart.Element.extend({ draw : function(){ + + var vm = this._vm; + var ctx = this.ctx, - halfWidth = this.width/2, - leftX = this.x - halfWidth, - rightX = this.x + halfWidth, - top = this.base - (this.base - this.y), - halfStroke = this.strokeWidth / 2; + halfWidth = vm.width/2, + leftX = vm.x - halfWidth, + rightX = vm.x + halfWidth, + top = vm.base - (vm.base - vm.y), + halfStroke = vm.strokeWidth / 2; // Canvas doesn't allow us to stroke inside the width so we can // adjust the sizes to fit if we're setting a stroke on the line - if (this.showStroke){ + if (vm.showStroke){ leftX += halfStroke; rightX -= halfStroke; top += halfStroke; @@ -1342,26 +1358,28 @@ ctx.beginPath(); - ctx.fillStyle = this.fillColor; - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; + ctx.fillStyle = vm.fillColor; + ctx.strokeStyle = vm.strokeColor; + ctx.lineWidth = vm.strokeWidth; // It'd be nice to keep this class totally generic to any rectangle // and simply specify which border to miss out. - ctx.moveTo(leftX, this.base); + ctx.moveTo(leftX, vm.base); ctx.lineTo(leftX, top); ctx.lineTo(rightX, top); - ctx.lineTo(rightX, this.base); + ctx.lineTo(rightX, vm.base); ctx.fill(); - if (this.showStroke){ + if (vm.showStroke){ ctx.stroke(); } }, height : function(){ - return this.base - this.y; + var vm = this._vm; + return vm.base - vm.y; }, inRange : function(chartX,chartY){ - return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base); + var vm = this._vm; + return (chartX >= vm.x - vm.width/2 && chartX <= vm.x + vm.width/2) && (chartY >= vm.y && chartY <= vm.base); } }); @@ -2109,8 +2127,7 @@ return animationWrapper.chartInstance === chartInstance; }); - if (index) - { + if (index){ this.animations.splice(index, 1); } },