New controllers, bar drawing (with errors)

This commit is contained in:
Tanner Linsley 2015-06-15 14:36:02 -06:00
parent aaa2484e1b
commit b0ece8b516
7 changed files with 509 additions and 463 deletions

View File

@ -0,0 +1,431 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.controllers.bar = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.controllers.bar.prototype, {
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
linkScales: function() {
if (!this.getDataset().xAxisID) {
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
}
if (!this.getDataset().yAxisID) {
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
}
},
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.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
});
}, this);
},
reset: function() {
this.update(true);
},
update: function(reset) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
helpers.each(this.getDataset().metaData, function(rectangle, index) {
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// all less than 0. use the top
yScalePoint = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
yScalePoint = yScale.getPixelForValue(yScale.min);
} else {
yScalePoint = yScale.getPixelForValue(0);
}
helpers.extend(rectangle, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.chart.data.datasets.length, this.index, index),
y: reset ? yScalePoint : yScale.getPixelForValue(this.getDataset().data[index]),
// Tooltip
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
// Appearance
base: yScale.calculateBarBase(this.index, index),
width: xScale.calculateBarWidth(this.chart.data.datasets.length),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
},
});
rectangle.pivot();
}, this);
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(rectangle, index) {
rectangle.transition(easingDecimal).draw();
}, this);
},
// eachLine: function eachLine(callback) {
// helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
// if (dataset.metaDataset && dataset.metaDataset instanceof Chart.Line) {
// callback.call(this, dataset, datasetIndex);
// }
// }, this);
// },
// eachRectangle: function(callback) {
// helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
// helpers.each(dataset.metaData, function(element, index) {
// if (element instanceof Chart.Rectangle) {
// callback(element, index, dataset, datasetIndex);
// }
// }, this);
// }, this);
// },
// addLine: function addLine(dataset, datasetIndex) {
// if (dataset) {
// dataset.metaDataset = new Chart.Line({
// _chart: this.chart.chart,
// _datasetIndex: datasetIndex,
// _points: dataset.metaData,
// });
// }
// },
// addPoint: function addPoint(dataset, datasetIndex, index) {
// if (dataset) {
// dataset.metaData = dataset.metaData || new Array(this.chart.data.datasets[datasetIndex].data.length);
// if (index < dataset.metaData.length) {
// dataset.metaData[index] = new Chart.Point({
// _datasetIndex: datasetIndex,
// _index: index,
// _chart: this.chart.chart,
// _model: {
// x: 0,
// y: 0,
// },
// });
// }
// }
// },
// resetElements: function resetElements() {
// helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
// // All elements must be the same type for the given dataset so we are fine to check just the first one
// if (dataset.metaData[0] instanceof Chart.Point) {
// // Have points. Update all of them
// this.resetDatasetPoints(dataset, datasetIndex);
// } else if (dataset.metaData[0] instanceof Chart.Rectangle) {
// // Have rectangles (bars)
// this.resetDatasetRectangles(dataset, datasetIndex);
// }
// }, this);
// },
// resetDatasetPoints: function resetDatasetPoints(dataset, datasetIndex) {
// helpers.each(dataset.metaData, function(point, index) {
// var xScale = this.getScaleForId(this.chart.data.datasets[datasetIndex].xAxisID);
// var yScale = this.getScaleForId(this.chart.data.datasets[datasetIndex].yAxisID);
// var yScalePoint;
// if (yScale.min < 0 && yScale.max < 0) {
// // all less than 0. use the top
// yScalePoint = yScale.getPixelForValue(yScale.max);
// } else if (yScale.min > 0 && yScale.max > 0) {
// yScalePoint = yScale.getPixelForValue(yScale.min);
// } else {
// yScalePoint = yScale.getPixelForValue(0);
// }
// helpers.extend(point, {
// // Utility
// _chart: this.chart.chart, //WTF
// _xScale: xScale,
// _yScale: yScale,
// _datasetIndex: datasetIndex,
// _index: index,
// // Desired view properties
// _model: {
// x: xScale.getPointPixelForValue(this.chart.data.datasets[datasetIndex].data[index], index, datasetIndex),
// y: yScalePoint,
// },
// });
// this.updatePointElementAppearance(point, datasetIndex, index);
// }, this);
// this.updateBezierControlPoints(dataset);
// },
// resetDatasetRectangles: function resetDatasetRectangles(dataset, datasetIndex) {
// },
// resetElementAppearance: function(element, datasetIndex, index) {
// if (element instanceof Chart.Point) {
// this.updatePointElementAppearance(element, datasetIndex, index);
// } else if (element instanceof Chart.Rectangle) {
// this.updateRectangleElementAppearance(element, datasetIndex, index);
// }
// },
// updateElements: function updateElements() {
// // Update the lines
// this.updateLines();
// helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
// // All elements must be the same type for the given dataset so we are fine to check just the first one
// if (dataset.metaData[0] instanceof Chart.Point) {
// // Have points. Update all of them
// this.updatePoints(dataset, datasetIndex);
// } else if (dataset.metaData[0] instanceof Chart.Rectangle) {
// // Have rectangles (bars)
// this.updateRectangles(dataset, datasetIndex);
// }
// }, this);
// },
// updateLines: function updateLines() {
// this.eachLine(function(dataset, datasetIndex) {
// var yScale = this.getScaleForId(dataset.yAxisID);
// var scaleBase;
// if (yScale.min < 0 && yScale.max < 0) {
// scaleBase = yScale.getPixelForValue(yScale.max);
// } else if (yScale.min > 0 && yScale.max > 0) {
// scaleBase = yScale.getPixelForValue(yScale.min);
// } else {
// scaleBase = yScale.getPixelForValue(0);
// }
// helpers.extend(dataset.metaDataset, {
// // Utility
// _scale: yScale,
// _datasetIndex: datasetIndex,
// // Data
// _children: dataset.metaData,
// // Model
// _model: {
// // Appearance
// tension: dataset.metaDataset.custom && dataset.metaDataset.custom.tension ? dataset.metaDataset.custom.tension : (dataset.tension || this.chart.options.elements.line.tension),
// backgroundColor: dataset.metaDataset.custom && dataset.metaDataset.custom.backgroundColor ? dataset.metaDataset.custom.backgroundColor : (dataset.backgroundColor || this.chart.options.elements.line.backgroundColor),
// borderWidth: dataset.metaDataset.custom && dataset.metaDataset.custom.borderWidth ? dataset.metaDataset.custom.borderWidth : (dataset.borderWidth || this.chart.options.elements.line.borderWidth),
// borderColor: dataset.metaDataset.custom && dataset.metaDataset.custom.borderColor ? dataset.metaDataset.custom.borderColor : (dataset.borderColor || this.chart.options.elements.line.borderColor),
// fill: dataset.metaDataset.custom && dataset.metaDataset.custom.fill ? dataset.metaDataset.custom.fill : (dataset.fill !== undefined ? dataset.fill : this.chart.options.elements.line.fill),
// skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.chart.options.elements.line.skipNull,
// drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.chart.options.elements.line.drawNull,
// // Scale
// scaleTop: yScale.top,
// scaleBottom: yScale.bottom,
// scaleZero: scaleBase,
// },
// });
// dataset.metaDataset.pivot();
// });
// },
// updatePoints: function updatePoints(dataset, datasetIndex) {
// helpers.each(dataset.metaData, function(point, index) {
// var xScale = this.getScaleForId(this.chart.data.datasets[datasetIndex].xAxisID);
// var yScale = this.getScaleForId(this.chart.data.datasets[datasetIndex].yAxisID);
// helpers.extend(point, {
// // Utility
// _chart: this.chart.chart,
// _xScale: xScale,
// _yScale: yScale,
// _datasetIndex: datasetIndex,
// _index: index,
// // Desired view properties
// _model: {
// x: xScale.getPointPixelForValue(this.chart.data.datasets[datasetIndex].data[index], index, datasetIndex),
// y: yScale.getPointPixelForValue(this.chart.data.datasets[datasetIndex].data[index], index, datasetIndex),
// },
// });
// this.updatePointElementAppearance(point, datasetIndex, index);
// }, this);
// this.updateBezierControlPoints(dataset);
// },
// updatePointElementAppearance: function updatePointElementAppearance(point, datasetIndex, index) {
// helpers.extend(point._model, {
// // Appearance
// tension: point.custom && point.custom.tension ? point.custom.tension : this.chart.options.elements.line.tension,
// radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.chart.data.datasets[datasetIndex].radius, index, this.chart.options.elements.point.radius),
// backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.chart.data.datasets[datasetIndex].pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
// borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.chart.data.datasets[datasetIndex].pointBorderColor, index, this.chart.options.elements.point.borderColor),
// borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.chart.data.datasets[datasetIndex].pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
// skip: this.chart.data.datasets[datasetIndex].data[index] === null,
// // Tooltip
// hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.chart.data.datasets[datasetIndex].hitRadius, index, this.chart.options.elements.point.hitRadius),
// });
// },
// setElementHoverStyle: function setElementHoverStyle(element) {
// if (element instanceof Chart.Point) {
// this.setPointHoverStyle(element);
// } else if (element instanceof Chart.Rectangle) {
// this.setRectangleHoverStyle(element);
// }
// },
// setPointHoverStyle: function setPointHoverStyle(point) {
// var dataset = this.chart.data.datasets[point._datasetIndex];
// var index = point._index;
// point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
// point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
// point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
// point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
// },
// setRectangleHoverStyle: 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);
// },
// updateBezierControlPoints: function updateBezierControlPoints(dataset) {
// // Update control points for the bezier curve
// helpers.each(dataset.metaData, function(point, index) {
// var controlPoints = helpers.splineCurve(
// this.previousPoint(dataset.metaData, index)._model,
// point._model,
// this.nextPoint(dataset.metaData, index)._model,
// point._model.tension
// );
// point._model.controlPointPreviousX = controlPoints.previous.x;
// point._model.controlPointNextX = controlPoints.next.x;
// // Prevent the bezier going outside of the bounds of the graph
// // Cap puter bezier handles to the upper/lower scale bounds
// if (controlPoints.next.y > this.chart.chartArea.bottom) {
// point._model.controlPointNextY = this.chart.chartArea.bottom;
// } else if (controlPoints.next.y < this.chart.chartArea.top) {
// point._model.controlPointNextY = this.chart.chartArea.top;
// } else {
// point._model.controlPointNextY = controlPoints.next.y;
// }
// // Cap inner bezier handles to the upper/lower scale bounds
// if (controlPoints.previous.y > this.chart.chartArea.bottom) {
// point._model.controlPointPreviousY = this.chart.chartArea.bottom;
// } else if (controlPoints.previous.y < this.chart.chartArea.top) {
// point._model.controlPointPreviousY = this.chart.chartArea.top;
// } else {
// point._model.controlPointPreviousY = controlPoints.previous.y;
// }
// // Now pivot the point for animation
// point.pivot();
// }, 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.chart.data.datasets.length; datasetIndex++) {
// for (elementIndex = 0; elementIndex < this.chart.data.datasets[datasetIndex].metaData.length; elementIndex++) {
// if (this.chart.data.datasets[datasetIndex].metaData[elementIndex].inGroupRange(eventPosition.x, eventPosition.y)) {
// helpers.each(this.chart.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.chart.data.datasets.length; ++datasetIndex) {
// for (var elementIndex = 0; elementIndex < this.chart.data.datasets[datasetIndex].metaData.length; ++elementIndex) {
// if (this.chart.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x, eventPosition.y)) {
// element.push(this.chart.data.datasets[datasetIndex].metaData[elementIndex]);
// return element;
// }
// }
// }
// return [];
// },
});
}).call(this);

View File

@ -1,9 +0,0 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
}).call(this);

View File

@ -1,427 +0,0 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.RectangularElementController = function(chart) {
this.chartInstance = chart;
};
Chart.RectangularElementController.prototype.getScaleForId = function getScaleForId(scaleID) {
return this.chartInstance.scales[scaleID];
};
// 2 helper functions to get points in collections
Chart.RectangularElementController.prototype.nextPoint = function nextPoint(collection, index) {
return collection[index + 1] || collection[index];
};
Chart.RectangularElementController.prototype.previousPoint = function previousPoint(collection, index) {
return collection[index - 1] || collection[index];
};
Chart.RectangularElementController.prototype.eachLine = function eachLine(callback) {
helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
if (dataset.metaDataset && dataset.metaDataset instanceof Chart.Line) {
callback.call(this, dataset, datasetIndex)
}
}, this);
};
Chart.RectangularElementController.prototype.eachRectangle = function(callback) {
helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
helpers.each(dataset.metaData, function(element, index) {
if (element instanceof Chart.Rectangle) {
callback(element, index, dataset, datasetIndex);
}
}, this);
}, this);
};
Chart.RectangularElementController.prototype.addLine = function addLine(dataset, datasetIndex) {
if (dataset) {
dataset.metaDataset = new Chart.Line({
_chart: this.chartInstance.chart,
_datasetIndex: datasetIndex,
_points: dataset.metaData,
});
}
}
Chart.RectangularElementController.prototype.addPoint = function addPoint(dataset, datasetIndex, index) {
if (dataset) {
dataset.metaData = dataset.metaData || new Array(this.chartInstance.data.datasets[datasetIndex].data.length);
if (index < dataset.metaData.length) {
dataset.metaData[index] = new Chart.Point({
_datasetIndex: datasetIndex,
_index: index,
_chart: this.chartInstance.chart,
_model: {
x: 0,
y: 0,
},
})
}
}
};
Chart.RectangularElementController.prototype.addRectangle = function(dataset, datasetIndex, index) {
if (dataset) {
dataset.metaData = dataset.metaData || new Array(this.chartInstance.data.datasets[datasetIndex].data.length);
if (index < dataset.metaData.length) {
dataset.metaData[index] = new Chart.Rectangle({
_chart: this.chartInstance.chart,
_datasetIndex: datasetIndex,
_index: index,
});
}
}
};
Chart.RectangularElementController.prototype.resetElements = function resetElements() {
helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
// All elements must be the same type for the given dataset so we are fine to check just the first one
if (dataset.metaData[0] instanceof Chart.Point) {
// Have points. Update all of them
this.resetDatasetPoints(dataset, datasetIndex);
} else if (dataset.metaData[0] instanceof Chart.Rectangle) {
// Have rectangles (bars)
this.resetDatasetRectangles(dataset, datasetIndex);
}
}, this);
};
Chart.RectangularElementController.prototype.resetDatasetPoints = function resetDatasetPoints(dataset, datasetIndex) {
helpers.each(dataset.metaData, function(point, index){
var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// all less than 0. use the top
yScalePoint = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
yScalePoint = yScale.getPixelForValue(yScale.min);
} else {
yScalePoint = yScale.getPixelForValue(0);
}
helpers.extend(point, {
// Utility
_chart: this.chartInstance.chart, //WTF
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.getPointPixelForValue(this.chartInstance.data.datasets[datasetIndex].data[index], index, datasetIndex),
y: yScalePoint,
},
});
this.updatePointElementAppearance(point, datasetIndex, index);
}, this);
this.updateBezierControlPoints(dataset);
};
Chart.RectangularElementController.prototype.resetDatasetRectangles = function resetDatasetRectangles(dataset, datasetIndex) {
helpers.each(dataset.metaData, function(rectangle, index) {
var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// all less than 0. use the top
yScalePoint = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
yScalePoint = yScale.getPixelForValue(yScale.min);
} else {
yScalePoint = yScale.getPixelForValue(0);
}
helpers.extend(rectangle, {
// Utility
_chart: this.chartInstance.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.chartInstance.data.datasets.length, datasetIndex, index),
y: yScalePoint,
// Tooltip
label: this.chartInstance.data.labels[index],
datasetLabel: this.chartInstance.data.datasets[datasetIndex].label,
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.chartInstance.data.datasets.length),
},
});
this.updateRectangleElementAppearance(rectangle, datasetIndex, index);
rectangle.pivot();
}, this);
};
Chart.RectangularElementController.prototype.resetElementAppearance = function(element, datasetIndex, index) {
if (element instanceof Chart.Point) {
this.updatePointElementAppearance(element, datasetIndex, index);
} else if (element instanceof Chart.Rectangle) {
this.updateRectangleElementAppearance(element, datasetIndex, index);
}
};
Chart.RectangularElementController.prototype.updateElements = function updateElements() {
// Update the lines
this.updateLines();
helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
// All elements must be the same type for the given dataset so we are fine to check just the first one
if (dataset.metaData[0] instanceof Chart.Point) {
// Have points. Update all of them
this.updatePoints(dataset, datasetIndex);
} else if (dataset.metaData[0] instanceof Chart.Rectangle) {
// Have rectangles (bars)
this.updateRectangles(dataset, datasetIndex);
}
}, this);
};
Chart.RectangularElementController.prototype.updateLines = function updateLines() {
this.eachLine(function(dataset, datasetIndex) {
var yScale = this.getScaleForId(dataset.yAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
helpers.extend(dataset.metaDataset, {
// Utility
_scale: yScale,
_datasetIndex: datasetIndex,
// Data
_children: dataset.metaData,
// Model
_model: {
// Appearance
tension: dataset.metaDataset.custom && dataset.metaDataset.custom.tension ? dataset.metaDataset.custom.tension : (dataset.tension || this.chartInstance.options.elements.line.tension),
backgroundColor: dataset.metaDataset.custom && dataset.metaDataset.custom.backgroundColor ? dataset.metaDataset.custom.backgroundColor : (dataset.backgroundColor || this.chartInstance.options.elements.line.backgroundColor),
borderWidth: dataset.metaDataset.custom && dataset.metaDataset.custom.borderWidth ? dataset.metaDataset.custom.borderWidth : (dataset.borderWidth || this.chartInstance.options.elements.line.borderWidth),
borderColor: dataset.metaDataset.custom && dataset.metaDataset.custom.borderColor ? dataset.metaDataset.custom.borderColor : (dataset.borderColor || this.chartInstance.options.elements.line.borderColor),
fill: dataset.metaDataset.custom && dataset.metaDataset.custom.fill ? dataset.metaDataset.custom.fill : (dataset.fill !== undefined ? dataset.fill : this.chartInstance.options.elements.line.fill),
skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.chartInstance.options.elements.line.skipNull,
drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.chartInstance.options.elements.line.drawNull,
// Scale
scaleTop: yScale.top,
scaleBottom: yScale.bottom,
scaleZero: scaleBase,
},
});
dataset.metaDataset.pivot();
});
};
Chart.RectangularElementController.prototype.updatePoints = function updatePoints(dataset, datasetIndex) {
helpers.each(dataset.metaData, function(point, index) {
var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
helpers.extend(point, {
// Utility
_chart: this.chartInstance.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.getPointPixelForValue(this.chartInstance.data.datasets[datasetIndex].data[index], index, datasetIndex),
y: yScale.getPointPixelForValue(this.chartInstance.data.datasets[datasetIndex].data[index], index, datasetIndex),
},
});
this.updatePointElementAppearance(point, datasetIndex, index);
}, this);
this.updateBezierControlPoints(dataset);
};
Chart.RectangularElementController.prototype.updatePointElementAppearance = function updatePointElementAppearance(point, datasetIndex, index) {
helpers.extend(point._model, {
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : this.chartInstance.options.elements.line.tension,
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].radius, index, this.chartInstance.options.elements.point.radius),
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].pointBackgroundColor, index, this.chartInstance.options.elements.point.backgroundColor),
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].pointBorderColor, index, this.chartInstance.options.elements.point.borderColor),
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].pointBorderWidth, index, this.chartInstance.options.elements.point.borderWidth),
skip: this.chartInstance.data.datasets[datasetIndex].data[index] === null,
// Tooltip
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].hitRadius, index, this.chartInstance.options.elements.point.hitRadius),
});
};
Chart.RectangularElementController.prototype.updateRectangles = function updateRectangles(dataset, datasetIndex) {
helpers.each(dataset.metaData, function(rectangle, index){
var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
helpers.extend(rectangle, {
// Utility
_chart: this.chartInstance.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.chartInstance.data.datasets.length, datasetIndex, index),
y: yScale.calculateBarY(datasetIndex, index),
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.chartInstance.data.datasets.length),
// Tooltip
label: this.chartInstance.data.labels[index],
datasetLabel: this.chartInstance.data.datasets[datasetIndex].label,
},
});
this.updateRectangleElementAppearance(rectangle, datasetIndex, index);
rectangle.pivot();
}, this);
};
Chart.RectangularElementController.prototype.updateRectangleElementAppearance = function updateRectangleElementAppearance(rectangle, datasetIndex, index) {
helpers.extend(rectangle._model, {
// Appearance
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].backgroundColor, index, this.chartInstance.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].borderColor, index, this.chartInstance.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].borderWidth, index, this.chartInstance.options.elements.rectangle.borderWidth),
});
};
Chart.RectangularElementController.prototype.setElementHoverStyle = function setElementHoverStyle(element) {
if (element instanceof Chart.Point) {
this.setPointHoverStyle(element);
} else if (element instanceof Chart.Rectangle) {
this.setRectangleHoverStyle(element);
}
};
Chart.RectangularElementController.prototype.setPointHoverStyle = function setPointHoverStyle(point) {
var dataset = this.chartInstance.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chartInstance.options.elements.point.hoverRadius);
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
};
Chart.RectangularElementController.prototype.setRectangleHoverStyle = function(rectangle) {
var dataset = this.chartInstance.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);
};
Chart.RectangularElementController.prototype.updateBezierControlPoints = function updateBezierControlPoints(dataset) {
// Update control points for the bezier curve
helpers.each(dataset.metaData, function(point, index) {
var controlPoints = helpers.splineCurve(
this.previousPoint(dataset.metaData, index)._model,
point._model,
this.nextPoint(dataset.metaData, index)._model,
point._model.tension
);
point._model.controlPointPreviousX = controlPoints.previous.x;
point._model.controlPointNextX = controlPoints.next.x;
// Prevent the bezier going outside of the bounds of the graph
// Cap puter bezier handles to the upper/lower scale bounds
if (controlPoints.next.y > this.chartInstance.chartArea.bottom) {
point._model.controlPointNextY = this.chartInstance.chartArea.bottom;
} else if (controlPoints.next.y < this.chartInstance.chartArea.top) {
point._model.controlPointNextY = this.chartInstance.chartArea.top;
} else {
point._model.controlPointNextY = controlPoints.next.y;
}
// Cap inner bezier handles to the upper/lower scale bounds
if (controlPoints.previous.y > this.chartInstance.chartArea.bottom) {
point._model.controlPointPreviousY = this.chartInstance.chartArea.bottom;
} else if (controlPoints.previous.y < this.chartInstance.chartArea.top) {
point._model.controlPointPreviousY = this.chartInstance.chartArea.top;
} else {
point._model.controlPointPreviousY = controlPoints.previous.y;
}
// Now pivot the point for animation
point.pivot();
}, this);
};
Chart.RectangularElementController.prototype.getElementsAtEvent = function(e) {
var elementsArray = [],
eventPosition = helpers.getRelativePosition(e),
datasetIterator = function(dataset) {
elementsArray.push(dataset.metaData[elementIndex]);
},
elementIndex;
for (var datasetIndex = 0; datasetIndex < this.chartInstance.data.datasets.length; datasetIndex++) {
for (elementIndex = 0; elementIndex < this.chartInstance.data.datasets[datasetIndex].metaData.length; elementIndex++) {
if (this.chartInstance.data.datasets[datasetIndex].metaData[elementIndex].inGroupRange(eventPosition.x, eventPosition.y)) {
helpers.each(this.chartInstance.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
Chart.RectangularElementController.prototype.getElementAtEvent = function(e) {
var element = [];
var eventPosition = helpers.getRelativePosition(e);
for (var datasetIndex = 0; datasetIndex < this.chartInstance.data.datasets.length; ++datasetIndex) {
for (var elementIndex = 0; elementIndex < this.chartInstance.data.datasets[datasetIndex].metaData.length; ++elementIndex) {
if (this.chartInstance.data.datasets[datasetIndex].metaData[elementIndex].inRange(eventPosition.x, eventPosition.y)) {
element.push(this.chartInstance.data.datasets[datasetIndex].metaData[elementIndex]);
return element;
}
}
}
return [];
};
}).call(this);

View File

@ -15,22 +15,21 @@
//Destroy method on the chart will remove the instance of the chart from this reference.
Chart.instances = {};
// Controllers available for dataset visualization eg. bar, line, slice, etc.
Chart.controllers = {};
// The main controller of a chart
Chart.Controller = function(instance) {
this.chart = instance;
var config = instance.config;
this.data = config.data;
this.options = config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[config.type], config.options || {});
this.config = instance.config;
this.data = this.config.data;
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
this.id = helpers.uid();
console.log(this.options);
//Add the chart instance to the global namespace
Chart.instances[this.id] = this;
// Initialize is always called when a chart type is created
// By default it is a no op, but it should be extended
if (this.options.responsive) {
this.resize();
}
@ -44,12 +43,19 @@
initialize: function initialize() {
// TODO
// If BeforeInit(this) doesn't return false, proceed
this.bindEvents();
this.buildScales();
this.buildControllers();
this.resetElements();
this.initToolTip();
this.update();
// TODO
// If AfterInit(this) doesn't return false, proceed
return this;
},
@ -111,12 +117,30 @@
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
},
resetElements: function resetElements() {
// This will loop through any data and do the appropriate element reset for the type
buildControllers: function() {
this.eachDataset(function(dataset, datasetIndex) {
var type = dataset.type || this.config.type;
if (dataset.controller) {
dataset.controller.updateIndex(datasetIndex);
return;
}
dataset.controller = new Chart.controllers[type](this, datasetIndex);
});
},
resetElements: function resetElements() {
this.eachDataset(function(dataset, datasetIndex) {
dataset.controller.reset();
});
},
update: function update(animationDuration) {
// This will loop through any data and do the appropriate element update for the type
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
this.eachDataset(function(dataset, datasetIndex) {
dataset.controller.update();
});
this.render(animationDuration);
},
@ -148,6 +172,25 @@
return this;
},
draw: function(ease) {
var easingDecimal = ease || 1;
this.clear();
// Draw all the scales
helpers.each(this.scales, function(scale) {
scale.draw(this.chartArea);
}, this);
// Draw each dataset via its respective controller
// TODO: needs support for reverse stacking (line chart)
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.controller.draw(ease);
}, this);
// Finally draw the tooltip
this.tooltip.transition(easingDecimal).draw();
},
eachValue: function eachValue(callback) {
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
helpers.each(dataset.data, callback, this, datasetIndex);
@ -164,6 +207,20 @@
helpers.each(this.data.datasets, callback, this);
},
// 2 helper functions to get next/previous elements in datasets
nextElement: function nextElement(datasets, index, loop) {
if (this.loop) {
return dataset[index + 1] || dataset[0];
}
return datasets[index + 1] || datasets[index];
},
previousElement: function previousElement(datasets, index, loop) {
if (this.loop) {
return dataset[index - 1] || dataset[dataset.length - 1];
}
return datasets[index - 1] || datasets[index];
},
// 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 draw
getElementAtEvent: function getElementAtEvent(e) {

View File

@ -7,6 +7,7 @@
previous = root.Chart,
helpers = Chart.helpers;
Chart.elements = {};
Chart.Element = function(configuration) {
helpers.extend(this, configuration);

View File

@ -58,12 +58,14 @@
//High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
Chart.helpers.retinaScale(this);
if (config) {
this.controller = new Chart.Controller(this);
return this.controller;
};
}
var defaultColor = 'rgba(0,0,0,0.1)';
return this;
};
//Globally expose the defaults to allow for user updating/changing
Chart.defaults = {
@ -77,7 +79,7 @@
animationDuration: 400,
},
onClick: null,
defaultColor: defaultColor,
defaultColor: 'rgba(0,0,0,0.1)',
// Element defaults defined in element extensions
elements: {}

View File

@ -1,14 +1,3 @@
/*!
* Chart.js
* http://chartjs.org/
* Version: {{ version }}
*
* Copyright 2015 Nick Downie
* Released under the MIT license
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
*/
(function() {
"use strict";
@ -23,7 +12,7 @@
borderColor: Chart.defaults.global.defaultColor,
};
Chart.Rectangle = Chart.Element.extend({
Chart.elements.Rectangle = Chart.Element.extend({
draw: function() {
var ctx = this._chart.ctx;
@ -92,4 +81,6 @@
},
});
}).call(this);