From f79ebdaa88c7d351258e5668cd5c210ada6a7b4e Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 15 May 2015 00:04:42 -0600 Subject: [PATCH] Stable Bar Chart, DRY core functions --- src/Chart.Bar.js | 123 +++++++++++----------------------------------- src/Chart.Core.js | 66 ++++++++++++++++++++----- 2 files changed, 83 insertions(+), 106 deletions(-) diff --git a/src/Chart.Bar.js b/src/Chart.Bar.js index 5ccdcba3f..6699924d7 100644 --- a/src/Chart.Bar.js +++ b/src/Chart.Bar.js @@ -101,11 +101,11 @@ },this); // Set defaults for bars - this.eachBars(function(bar, index, datasetIndex){ + this.eachElement(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.calculateBarBase(), + y: this.calculateBaseY(), _datasetIndex: datasetIndex, _index: index, }); @@ -128,7 +128,7 @@ // If exiting chart if(e.type == 'mouseout'){ - return false; + return this; } this.lastActive = this.lastActive || []; @@ -137,9 +137,9 @@ this.active = function(){ switch(this.options.hoverMode){ case 'single': - return this.getBarAtEvent(e); + return this.getElementAtEvent(e); case 'label': - return this.getBarsAtEvent(e); + return this.getElementsAtEvent(e); case 'dataset': return this.getDatasetAtEvent(e); default: @@ -153,7 +153,7 @@ } // Remove styling for last active (even if it may still be active) - if(this.lastActive){ + if(this.lastActive.length){ switch(this.options.hoverMode){ case 'single': this.lastActive[0].backgroundColor = this.data.datasets[this.lastActive[0]._datasetIndex].backgroundColor; @@ -170,11 +170,11 @@ case 'dataset': break; default: - // do nothing + // Don't change anything } } - // Built in hover actions + // Built in hover styling if(this.active.length && this.options.hoverMode){ switch(this.options.hoverMode){ case 'single': @@ -190,7 +190,7 @@ case 'dataset': break; default: - // do nothing + // Don't change anything } } @@ -219,16 +219,8 @@ } + // Hover animations if(!this.animating){ - - // If entering - if(!this.lastActive.length && this.active.length){ - console.log('entering'); - this.tooltip.pivot(); - this.stop(); - this.render(false, this.options.hoverAnimationDuration); - } - var changed; helpers.each(this.active, function(element, index){ @@ -237,33 +229,22 @@ } }, this); - // If different element - if(this.lastActive.length && this.active.length && changed){ - console.log('changing'); - this.tooltip.pivot(); - this.stop(); - this.render(false, this.options.hoverAnimationDuration); - } + // If entering, leaving, or changing elements, animate the change via pivot + if ((!this.lastActive.length && this.active.length) || + (this.lastActive.length && !this.active.length)|| + (this.lastActive.length && this.active.length && changed)){ - // if Leaving - if (this.lastActive.length && !this.active.length){ - console.log('leaving'); this.tooltip.pivot(); - this.stop(); - this.render(false, this.options.hoverAnimationDuration); + this.render(this.options.hoverAnimationDuration); } - } // Remember Last Active - this.lastActive = this.active; + return this; }, // Calculate the base point for the bar. - // If the scale has a 0 point, use that as the base - // If the scale min and max are both positive, use the bottom as a base - // If the scale min and max are both negative, use the top as a base - calculateBarBase: function() { + calculateBaseY: function() { var base = this.scale.endPoint; if (this.scale.beginAtZero || ((this.scale.min <= 0 && this.scale.max >= 0) || (this.scale.min >= 0 && this.scale.max <= 0))) @@ -283,7 +264,7 @@ this.scale.update(); - this.eachBars(function(bar, index, datasetIndex){ + this.eachElement(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), @@ -302,51 +283,6 @@ this.render(); }, - eachBars : function(callback){ - 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.metaData[elementIndex]); - }, - elementIndex; - - for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; datasetIndex++) { - for (elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; elementIndex++) { - if (this.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x,eventPosition.y)){ - helpers.each(this.data.datasets, datasetIterator); - } - } - } - - return barsArray.length ? barsArray : []; - }, - // Get the single bar that was clicked on - // @return : An object containing the dataset index and bar index of the matching bar. Also contains the rectangle that was drawn - getBarAtEvent : function(e) { - var bar = []; - var eventPosition = helpers.getRelativePosition(e); - - for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; ++datasetIndex) { - for (var elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; ++elementIndex) { - if (this.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x, eventPosition.y)) { - bar.push(this.data.datasets[datasetIndex].metaData[elementIndex]); - return bar; - } - } - } - - return []; - }, buildScale : function(labels){ var self = this; @@ -406,17 +342,16 @@ this.scale = new this.ScaleClass(scaleOptions); }, // This should be incorportated into the init as something like a default value. "Reflow" seems like a weird word for a fredraw function - /*reflow : function(){ - helpers.extend(this.BarClass.prototype,{ - y: this.calculateBarBase(), // so that we animate from the baseline - base : this.calculateBarBase() + redraw : function(){ + var base = this.calculateBaseY(); + this.eachElement(function(element, index, datasetIndex){ + helpers.extend(element,{ + y: base, + base : base + }); }); - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - },*/ + render(); + }, draw : function(ease){ var easingDecimal = ease || 1; @@ -425,10 +360,10 @@ this.scale.draw(easingDecimal); //Draw all the bars for each dataset - this.eachBars(function(bar, index, datasetIndex){ + this.eachElement(function(bar, index, datasetIndex){ if (bar.hasValue()){ // Update the bar basepoint - bar.base = this.calculateBarBase(); + bar.base = this.calculateBaseY(); //Transition bar.transition(easingDecimal).draw(); } diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 6c5c4f285..a038299ad 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -945,15 +945,12 @@ } return this; }, - reflow : noop, - render : function(reflow, customDuration){ - if (reflow){ - this.reflow(); - } + redraw : noop, + render : function(duration){ - if (this.options.animation && !reflow){ + if (this.options.animation){ var animation = new Chart.Animation(); - animation.numSteps = (customDuration || this.options.animationDuration) / 16.66; //60 fps + animation.numSteps = (duration || this.options.animationDuration) / 16.66; //60 fps animation.easing = this.options.animationEasing; // render function @@ -969,7 +966,7 @@ animation.onAnimationProgress = this.options.onAnimationProgress; animation.onAnimationComplete = this.options.onAnimationComplete; - Chart.animationService.addAnimation(this, animation, customDuration); + Chart.animationService.addAnimation(this, animation, duration); } else{ this.draw(); @@ -977,6 +974,51 @@ } return this; }, + eachElement : function(callback){ + 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); + }, + getElementsAtEvent : function(e){ + var elementsArray = [], + eventPosition = helpers.getRelativePosition(e), + datasetIterator = function(dataset){ + elementsArray.push(dataset.metaData[elementIndex]); + }, + elementIndex; + + for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; datasetIndex++) { + for (elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; elementIndex++) { + if (this.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x,eventPosition.y)){ + helpers.each(this.data.datasets, datasetIterator); + } + } + } + + return elementsArray.length ? elementsArray : []; + }, + // Get the single element that was clicked on + // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was drawn + getElementAtEvent : function(e) { + var element = []; + var eventPosition = helpers.getRelativePosition(e); + + for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; ++datasetIndex) { + for (var elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; ++elementIndex) { + if (this.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x, eventPosition.y)) { + element.push(this.data.datasets[datasetIndex].metaData[elementIndex]); + return element; + } + } + } + + return []; + }, generateLegend : function(){ return template(this.options.legendTemplate,this); }, @@ -1337,10 +1379,10 @@ switch(this._options.hoverMode){ case 'single': helpers.extend(this, { - text: template(this._options.tooltipTemplate, this._active), + text: template(this._options.tooltipTemplate, this._active[0]), }); var tooltipPosition = this._active[0].tooltipPosition(); - helpers.extend(this.tooltip, { + helpers.extend(this, { x: Math.round(tooltipPosition.x), y: Math.round(tooltipPosition.y), }); @@ -2116,9 +2158,9 @@ frameDuration: 17, animations: [], dropFrames: 0, - addAnimation: function(chartInstance, animationObject, customDuration) { + addAnimation: function(chartInstance, animationObject, duration) { - if(!customDuration){ + if(!duration){ chartInstance.animating = true; }