From 25d6c2f064ac067c46226943de648e7acd8962f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20K=C3=BCsel?= Date: Thu, 14 Jan 2016 18:17:14 +0100 Subject: [PATCH 1/5] Removes unused context bindings --- src/controllers/controller.bar.js | 2 +- src/controllers/controller.bubble.js | 2 +- src/controllers/controller.doughnut.js | 2 +- src/controllers/controller.line.js | 4 ++-- src/controllers/controller.polarArea.js | 2 +- src/controllers/controller.radar.js | 2 +- src/core/core.controller.js | 18 +++++++++--------- src/core/core.tooltip.js | 8 ++++---- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 1aca0df2e2e..12ccde603c4 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -44,7 +44,7 @@ if (helpers.isDatasetVisible(dataset) && dataset.bar) { ++barCount; } - }, this); + }); return barCount; }, diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index bce235c2ae3..74e38a962bf 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -133,7 +133,7 @@ helpers.each(this.getDataset().metaData, function(point, index) { point.transition(easingDecimal); point.draw(); - }, this); + }); }, diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index a38c904d79a..de121242b40 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -219,7 +219,7 @@ var easingDecimal = ease || 1; helpers.each(this.getDataset().metaData, function(arc, index) { arc.transition(easingDecimal).draw(); - }, this); + }); }, setHoverStyle: function(arc) { diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index fcef8af47f8..a87814eaf49 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -259,9 +259,9 @@ var easingDecimal = ease || 1; // Transition Point Locations - helpers.each(this.getDataset().metaData, function(point, index) { + helpers.each(this.getDataset().metaData, function(point) { point.transition(easingDecimal); - }, this); + }); // Transition and Draw the line if (this.chart.options.showLines) diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 7dbd3ea2874..25b19de56bf 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -195,7 +195,7 @@ var easingDecimal = ease || 1; helpers.each(this.getDataset().metaData, function(arc, index) { arc.transition(easingDecimal).draw(); - }, this); + }); }, setHoverStyle: function(arc) { diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 6d808c67a7f..2955cb32939 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -167,7 +167,7 @@ // Transition Point Locations helpers.each(this.getDataset().metaData, function(point, index) { point.transition(easingDecimal); - }, this); + }); // Transition and Draw the line this.getDataset().metaDataset.transition(easingDecimal).draw(); diff --git a/src/core/core.controller.js b/src/core/core.controller.js index c741359c374..e4da7af74b0 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -110,14 +110,14 @@ if (this.options.scales.xAxes && this.options.scales.xAxes.length) { helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) { xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index); - }, this); + }); } if (this.options.scales.yAxes && this.options.scales.yAxes.length) { // Build the y axes helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) { yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index); - }, this); + }); } } }, @@ -239,7 +239,7 @@ resetElements: function resetElements() { helpers.each(this.data.datasets, function(dataset, datasetIndex) { dataset.controller.reset(); - }, this); + }); }, update: function update(animationDuration, lazy) { @@ -254,12 +254,12 @@ // Make sure all dataset controllers have correct meta data counts helpers.each(this.data.datasets, function(dataset, datasetIndex) { dataset.controller.buildOrUpdateElements(); - }, this); + }); // This will loop through any data and do the appropriate element update for the type helpers.each(this.data.datasets, function(dataset, datasetIndex) { dataset.controller.update(); - }, this); + }); this.render(animationDuration, lazy); }, @@ -310,7 +310,7 @@ if (helpers.isDatasetVisible(dataset)) { dataset.controller.draw(ease); } - }, this); + }); // Finally draw the tooltip this.tooltip.transition(easingDecimal).draw(); @@ -330,9 +330,9 @@ elementsArray.push(element); return elementsArray; } - }, this); + }); } - }, this); + }); return elementsArray; }, @@ -361,7 +361,7 @@ if(helpers.isDatasetVisible(dataset)){ elementsArray.push(dataset.metaData[found._index]); } - }, this); + }); return elementsArray; }, diff --git a/src/core/core.tooltip.js b/src/core/core.tooltip.js index 96b1d80c3a0..37a43e5d398 100644 --- a/src/core/core.tooltip.js +++ b/src/core/core.tooltip.js @@ -265,7 +265,7 @@ backgroundColor: active._view.backgroundColor }); } - }, this); + }); tooltipPosition = this.getAveragePosition(this._active); tooltipPosition.y = this._active[0]._yScale.getPixelForDecimal(0.5); @@ -330,7 +330,7 @@ ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); helpers.each(vm.beforeBody.concat(vm.afterBody), function(line) { size.width = Math.max(size.width, ctx.measureText(line).width); - }, this); + }); helpers.each(vm.body, function(line) { size.width = Math.max(size.width, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0)); }, this); @@ -495,7 +495,7 @@ if (i + 1 === vm.title.length) { pt.y += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing } - }, this); + }); } }, drawBody: function drawBody(pt, vm, ctx, opacity) { @@ -554,7 +554,7 @@ helpers.each(vm.footer, function(footer) { ctx.fillText(footer, pt.x, pt.y); pt.y += vm.footerFontSize + vm.footerSpacing; - }, this); + }); } }, draw: function draw() { From 4f6e86640fe2680f09266ff9d2f25bdb3149bfe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20K=C3=BCsel?= Date: Fri, 15 Jan 2016 13:05:03 +0100 Subject: [PATCH 2/5] Adds a caching system to expensive measureText() function --- src/core/core.helpers.js | 30 +++++++++++++++++++++++++++--- src/core/core.scale.js | 14 +++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/core/core.helpers.js b/src/core/core.helpers.js index 07a4488c775..7d677fd2774 100644 --- a/src/core/core.helpers.js +++ b/src/core/core.helpers.js @@ -756,13 +756,37 @@ helpers.fontString = function(pixelSize, fontStyle, fontFamily) { return fontStyle + " " + pixelSize + "px " + fontFamily; }; - helpers.longestText = function(ctx, font, arrayOfStrings) { + helpers.longestText = function(ctx, font, arrayOfStrings, cache) { + cache = cache || {}; + cache.data = cache.data || {}; + cache.garbageCollect = cache.garbageCollect || []; + + if (cache.font !== font) { + cache.data = {}; + cache.garbageCollect = []; + cache.font = font; + } + ctx.font = font; var longest = 0; helpers.each(arrayOfStrings, function(string) { - var textWidth = ctx.measureText(string).width; - longest = (textWidth > longest) ? textWidth : longest; + var textWidth = cache.data[string]; + if (!textWidth) { + textWidth = cache.data[string] = ctx.measureText(string).width; + cache.garbageCollect.push(string); + } + if (textWidth > longest) + longest = textWidth; }); + + var gcLen = cache.garbageCollect.length / 2; + if (gcLen > arrayOfStrings.length) { + for (var i = 0; i < gcLen; i++) { + var key = cache.garbageCollect.shift(); + delete cache.data[key]; + } + } + return longest; }; helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { diff --git a/src/core/core.scale.js b/src/core/core.scale.js index f614cd8220d..e02e5157b2f 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -204,7 +204,10 @@ this.paddingRight = lastWidth / 2 + 3; this.paddingLeft = firstWidth / 2 + 3; - var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks); + if (!this.longestTextCache) { + this.longestTextCache = {}; + } + var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks, this.longestTextCache); var labelWidth = originalLabelWidth; var cosRotation; var sinRotation; @@ -289,9 +292,15 @@ var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily); + if (!this.longestTextCache) { + this.longestTextCache = {}; + } + + var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks, this.longestTextCache); + if (this.isHorizontal()) { // A horizontal axis is more constrained by the height. - this.longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks); + this.longestLabelWidth = largestTextWidth; // TODO - improve this calculation var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + 1.5 * this.options.ticks.fontSize; @@ -313,7 +322,6 @@ } else { // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first var maxLabelWidth = this.maxWidth - this.minSize.width; - var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks); // Account for padding if (!this.options.ticks.mirror) { From 19613f531cf6ad0e80f81ae40ca62ba12f4e7ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20K=C3=BCsel?= Date: Fri, 15 Jan 2016 13:05:23 +0100 Subject: [PATCH 3/5] Just assign instead of extend --- src/controllers/controller.line.js | 88 ++++++++++++++---------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index a87814eaf49..e557a02ec4b 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -82,30 +82,28 @@ // Update Line if (this.chart.options.showLines) { - helpers.extend(line, { - // Utility - _scale: yScale, - _datasetIndex: this.index, - // Data - _children: points, - // Model - _model: { - // Appearance - tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), - backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor), - borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth), - borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor), - borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle), - borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash), - borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset), - borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle), - fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill), - // Scale - scaleTop: yScale.top, - scaleBottom: yScale.bottom, - scaleZero: scaleBase, - }, - }); + // Utility + line._scale = yScale; + line._datasetIndex = this.index; + // Data + line._children = points; + // Model + line._model = { + // Appearance + tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), + backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor), + borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth), + borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor), + borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle), + borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash), + borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset), + borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle), + fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill), + // Scale + scaleTop: yScale.top, + scaleBottom: yScale.bottom, + scaleZero: scaleBase + }; line.pivot(); } @@ -174,28 +172,26 @@ scaleBase = yScale.getPixelForValue(0); } - helpers.extend(point, { - // Utility - _chart: this.chart.chart, - _xScale: xScale, - _yScale: yScale, - _datasetIndex: this.index, - _index: index, - - // Desired view properties - _model: { - x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo), - y: reset ? scaleBase : this.calculatePointY(this.getDataset().data[index], index, this.index, this.chart.isCombo), - // Appearance - tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), - radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius), - backgroundColor: this.getPointBackgroundColor(point, index), - borderColor: this.getPointBorderColor(point, index), - borderWidth: this.getPointBorderWidth(point, index), - // Tooltip - hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius), - }, - }); + // Utility + point._chart = this.chart.chart; + point._xScale = xScale; + point._yScale = yScale; + point._datasetIndex = this.index; + point._index = index; + + // Desired view properties + point._model = { + x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo), + y: reset ? scaleBase : this.calculatePointY(this.getDataset().data[index], index, this.index, this.chart.isCombo), + // Appearance + tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), + radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius), + backgroundColor: this.getPointBackgroundColor(point, index), + borderColor: this.getPointBorderColor(point, index), + borderWidth: this.getPointBorderWidth(point, index), + // Tooltip + hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius) + }; point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, From fa7baa16278dd0157743d9f9ac74403558316c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20K=C3=BCsel?= Date: Fri, 15 Jan 2016 13:05:51 +0100 Subject: [PATCH 4/5] Do not calculate control points if we want straight lines --- src/controllers/controller.line.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index e557a02ec4b..a89590991ce 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -60,7 +60,7 @@ this.getDataset().metaData.splice(index, 0, point); // Make sure bezier control points are updated - if (this.chart.options.showLines) + if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0) this.updateBezierControlPoints(); }, @@ -112,7 +112,7 @@ this.updateElement(point, index, reset); }, this); - if (this.chart.options.showLines) + if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0) this.updateBezierControlPoints(); }, From c42cb148da2ea3f522fae0809e96eeee0d5f8441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20K=C3=BCsel?= Date: Fri, 15 Jan 2016 13:06:03 +0100 Subject: [PATCH 5/5] Transition optimizations --- src/core/core.element.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core/core.element.js b/src/core/core.element.js index d9c0d35a623..4b43df1c2bf 100644 --- a/src/core/core.element.js +++ b/src/core/core.element.js @@ -26,6 +26,14 @@ if (!this._view) { this._view = helpers.clone(this._model); } + + // No animation -> No Transition + if (ease === 1) { + this._view = this._model; + this._start = null; + return this; + } + if (!this._start) { this.pivot(); } @@ -37,16 +45,16 @@ } // Init if doesn't exist - else if (!this._view[key]) { - if (typeof value === 'number' && isNaN(this._view[key]) === false) { + else if (!this._view.hasOwnProperty(key)) { + if (typeof value === 'number' && !isNaN(this._view[key])) { this._view[key] = value * ease; } else { - this._view[key] = value || null; + this._view[key] = value; } } // No unnecessary computations - else if (this._model[key] === this._view[key]) { + else if (value === this._view[key]) { // It's the same! Woohoo! } @@ -70,9 +78,6 @@ } }, this); - if (ease === 1) { - delete this._start; - } return this; }, tooltipPosition: function() {