Optimize element.point and controller.line

Change some helpers.each() to `for` loops when iterating on a potentially large number of items and use more local variables when appropriate (making the minified build a bit smaller).
This commit is contained in:
Simon Brunel 2016-05-17 13:32:40 +02:00
parent 69521477a1
commit 93c28a4d5f
2 changed files with 225 additions and 202 deletions

View File

@ -23,50 +23,58 @@ module.exports = function(Chart) {
}
};
Chart.controllers.line = Chart.DatasetController.extend({
addElements: function() {
var meta = this.getMeta();
var me = this;
var meta = me.getMeta();
var data = me.getDataset().data || [];
var value, i, ilen;
meta.dataset = meta.dataset || new Chart.elements.Line({
_chart: this.chart.chart,
_datasetIndex: this.index,
_chart: me.chart.chart,
_datasetIndex: me.index,
_points: meta.data
});
helpers.each(this.getDataset().data, function(value, index) {
meta.data[index] = meta.data[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
for (i=0, ilen=data.length; i<ilen; ++i) {
value = data[i];
meta.data[i] = meta.data[i] || new Chart.elements.Point({
_chart: me.chart.chart,
_datasetIndex: me.index,
_index: i
});
}, this);
}
},
addElementAndReset: function(index) {
var me = this;
var options = me.chart.options;
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_chart: me.chart.chart,
_datasetIndex: me.index,
_index: index
});
// Add to the points array and reset it
this.getMeta().data.splice(index, 0, point);
this.updateElement(point, index, true);
me.getMeta().data.splice(index, 0, point);
me.updateElement(point, index, true);
// Make sure bezier control points are updated
if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0)
this.updateBezierControlPoints();
if (options.showLines && options.elements.line.tension !== 0) {
me.updateBezierControlPoints();
}
},
update: function update(reset) {
var meta = this.getMeta();
var me = this;
var meta = me.getMeta();
var line = meta.dataset;
var lineElementOptions = this.chart.options.elements.line;
var points = meta.data;
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var scaleBase;
var points = meta.data || [];
var options = me.chart.options;
var lineElementOptions = options.elements.line;
var yScale = me.getScaleForId(meta.yAxisID);
var xScale = me.getScaleForId(meta.xAxisID);
var scaleBase, i, ilen, dataset, custom;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
@ -77,21 +85,21 @@ module.exports = function(Chart) {
}
// Update Line
if (this.chart.options.showLines) {
// Utility
line._scale = yScale;
line._datasetIndex = this.index;
// Data
line._children = points;
// Model
if (options.showLines) {
dataset = me.getDataset();
custom = line.custom || {};
var dataset = this.getDataset();
var custom = line.custom || {};
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
dataset.lineTension = dataset.tension;
}
// Utility
line._scale = yScale;
line._datasetIndex = me.index;
// Data
line._children = points;
// Model
line._model = {
// Appearance
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
@ -108,16 +116,17 @@ module.exports = function(Chart) {
scaleBottom: yScale.bottom,
scaleZero: scaleBase
};
line.pivot();
}
// Update Points
helpers.each(points, function(point, index) {
this.updateElement(point, index, reset);
}, this);
for (i=0, ilen=points.length; i<ilen; ++i) {
me.updateElement(points[i], i, reset);
}
if (this.chart.options.showLines && lineElementOptions.tension !== 0) {
this.updateBezierControlPoints();
if (options.showLines && lineElementOptions.tension !== 0) {
me.updateBezierControlPoints();
}
},
@ -136,6 +145,7 @@ module.exports = function(Chart) {
return backgroundColor;
},
getPointBorderColor: function(point, index) {
var borderColor = this.chart.options.elements.point.borderColor;
var dataset = this.getDataset();
@ -151,6 +161,7 @@ module.exports = function(Chart) {
return borderColor;
},
getPointBorderWidth: function(point, index) {
var borderWidth = this.chart.options.elements.point.borderWidth;
var dataset = this.getDataset();
@ -168,12 +179,16 @@ module.exports = function(Chart) {
},
updateElement: function(point, index, reset) {
var meta = this.getMeta();
var me = this;
var meta = me.getMeta();
var custom = point.custom || {};
var dataset = this.getDataset();
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var scaleBase;
var dataset = me.getDataset();
var datasetIndex = me.index;
var value = dataset.data[index];
var yScale = me.getScaleForId(meta.yAxisID);
var xScale = me.getScaleForId(meta.xAxisID);
var pointOptions = me.chart.options.elements.point;
var scaleBase, x, y;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
@ -183,56 +198,56 @@ module.exports = function(Chart) {
scaleBase = yScale.getPixelForValue(0);
}
// Utility
point._chart = this.chart.chart;
point._xScale = xScale;
point._yScale = yScale;
point._datasetIndex = this.index;
point._index = index;
// Desired view properties
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined))
{
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
dataset.pointRadius = dataset.radius;
}
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined))
{
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
dataset.pointHitRadius = dataset.hitRadius;
}
x = xScale.getPixelForValue(value, index, datasetIndex, me.chart.isCombo);
y = reset ? scaleBase : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo);
// Utility
point._chart = me.chart.chart;
point._xScale = xScale;
point._yScale = yScale;
point._datasetIndex = datasetIndex;
point._index = index;
// Desired view properties
point._model = {
x: xScale.getPixelForValue(dataset.data[index], index, this.index, this.chart.isCombo),
y: reset ? scaleBase : this.calculatePointY(dataset.data[index], index, this.index, this.chart.isCombo),
x: x,
y: y,
skip: custom.skip || isNaN(x) || isNaN(y),
// Appearance
radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius),
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, this.chart.options.elements.point.pointStyle),
backgroundColor: this.getPointBackgroundColor(point, index),
borderColor: this.getPointBorderColor(point, index),
borderWidth: this.getPointBorderWidth(point, index),
radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
backgroundColor: me.getPointBackgroundColor(point, index),
borderColor: me.getPointBorderColor(point, index),
borderWidth: me.getPointBorderWidth(point, index),
tension: meta.dataset._model ? meta.dataset._model.tension : 0,
// Tooltip
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, this.chart.options.elements.point.hitRadius)
hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
};
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
},
calculatePointY: function(value, index, datasetIndex, isCombo) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var me = this;
var chart = me.chart;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
var yScale = me.getScaleForId(meta.yAxisID);
var sumPos = 0;
var sumNeg = 0;
var i, ds, dsMeta;
if (yScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
var ds = this.chart.data.datasets[i];
var dsMeta = this.chart.getDatasetMeta(i);
if (dsMeta.type === 'line' && this.chart.isDatasetVisible(i)) {
for (i = 0; i < datasetIndex; i++) {
ds = chart.data.datasets[i];
dsMeta = chart.getDatasetMeta(i);
if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
@ -252,45 +267,52 @@ module.exports = function(Chart) {
},
updateBezierControlPoints: function() {
// Update bezier control points
var meta = this.getMeta();
helpers.each(meta.data, function(point, index) {
var controlPoints = helpers.splineCurve(
helpers.previousItem(meta.data, index)._model,
point._model,
helpers.nextItem(meta.data, index)._model,
var area = this.chart.chartArea;
var points = meta.data || [];
var i, ilen, point, model, controlPoints;
for (i=0, ilen=points.length; i<ilen; ++i) {
point = points[i];
model = point._model;
controlPoints = helpers.splineCurve(
helpers.previousItem(points, i)._model,
model,
helpers.nextItem(points, i)._model,
meta.dataset._model.tension
);
// Prevent the bezier going outside of the bounds of the graph
point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, area.right), area.left);
model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, area.bottom), area.top);
model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, area.right), area.left);
model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, area.bottom), area.top);
// Now pivot the point for animation
point.pivot();
}, this);
}
},
draw: function(ease) {
var meta = this.getMeta();
var points = meta.data || [];
var easingDecimal = ease || 1;
var i, ilen;
// Transition Point Locations
helpers.each(meta.data, function(point) {
point.transition(easingDecimal);
});
for (i=0, ilen=points.length; i<ilen; ++i) {
points[i].transition(easingDecimal);
}
// Transition and Draw the line
if (this.chart.options.showLines)
if (this.chart.options.showLines) {
meta.dataset.transition(easingDecimal).draw();
}
// Draw the points
helpers.each(meta.data, function(point) {
point.draw();
});
for (i=0, ilen=points.length; i<ilen; ++i) {
points[i].draw();
}
},
setHoverStyle: function(point) {
@ -300,28 +322,28 @@ module.exports = function(Chart) {
var custom = point.custom || {};
var model = point._model;
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
},
removeHoverStyle: function(point) {
var dataset = this.chart.data.datasets[point._datasetIndex];
var me = this;
var dataset = me.chart.data.datasets[point._datasetIndex];
var index = point._index;
var custom = point.custom || {};
var model = point._model;
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined))
{
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
dataset.pointRadius = dataset.radius;
}
model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius);
model.backgroundColor = this.getPointBackgroundColor(point, index);
model.borderColor = this.getPointBorderColor(point, index);
model.borderWidth = this.getPointBorderWidth(point, index);
model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
model.backgroundColor = me.getPointBackgroundColor(point, index);
model.borderColor = me.getPointBorderColor(point, index);
model.borderWidth = me.getPointBorderWidth(point, index);
}
});
};

View File

@ -18,7 +18,6 @@ module.exports = function(Chart) {
hoverBorderWidth: 1
};
Chart.elements.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
@ -37,33 +36,34 @@ module.exports = function(Chart) {
};
},
draw: function() {
var vm = this._view,
x = vm.x,
y = vm.y;
var vm = this._view;
var ctx = this._chart.ctx;
var pointStyle = vm.pointStyle;
var radius = vm.radius;
var x = vm.x;
var y = vm.y;
var type, edgeLength, xOffset, yOffset, height, size;
if (vm.skip) {
return;
}
var pointStyle = vm.pointStyle;
if (typeof pointStyle === 'object' && ((pointStyle.toString() === '[object HTMLImageElement]') || (pointStyle.toString() === '[object HTMLCanvasElement]'))) {
if (typeof pointStyle === 'object') {
type = pointStyle.toString();
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
return;
}
}
if (!isNaN(vm.radius) && vm.radius > 0) {
if (isNaN(radius) || radius <= 0) {
return;
}
ctx.strokeStyle = vm.borderColor || defaultColor;
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
ctx.fillStyle = vm.backgroundColor || defaultColor;
var radius = vm.radius;
var xOffset,
yOffset;
switch (pointStyle) {
// Default includes circle
default:
@ -74,8 +74,8 @@ module.exports = function(Chart) {
break;
case 'triangle':
ctx.beginPath();
var edgeLength = 3 * radius / Math.sqrt(3);
var height = edgeLength * Math.sqrt(3) / 2;
edgeLength = 3 * radius / Math.sqrt(3);
height = edgeLength * Math.sqrt(3) / 2;
ctx.moveTo(x - edgeLength / 2, y + height / 3);
ctx.lineTo(x + edgeLength / 2, y + height / 3);
ctx.lineTo(x, y - 2 * height / 3);
@ -83,14 +83,16 @@ module.exports = function(Chart) {
ctx.fill();
break;
case 'rect':
ctx.fillRect(x - 1 / Math.SQRT2 * radius, y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
ctx.strokeRect(x - 1 / Math.SQRT2 * radius, y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
size = 1 / Math.SQRT2 * radius;
ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
break;
case 'rectRot':
ctx.translate(x, y);
ctx.rotate(Math.PI / 4);
ctx.fillRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
ctx.strokeRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
size = 1 / Math.SQRT2 * radius;
ctx.fillRect(-size, -size, 2 * size, 2 * size);
ctx.strokeRect(-size, -size, 2 * size, 2 * size);
ctx.setTransform(1, 0, 0, 1, 0, 0);
break;
case 'cross':
@ -141,6 +143,5 @@ module.exports = function(Chart) {
ctx.stroke();
}
}
});
};