0👍
Well, I didn’t figure out a nice solution, but I found one.
So basically, as I described on the question I wanted to create a bar chart, but I also wanted that the chart would have different colors along the time…
Here’s how I solved this.
First I create and array of colors, and calculate the percentage of time a color would be visible. And then I put it inside an array of colors, called “activityGradientArray”
var generateFilteredActivityGradientArray = function (){
var filteredActivityColorWithSpaces = $scope.filteredActivityColorWithSpaces;
//define ratio length
var ratioActivity = (1.0) / filteredActivityColorWithSpaces.length;
var newRatio = 0.0;
var oldRatio = 0.0;
var ratioCounter = 0.0;
var oldColor = "";
var newColor = "";
var colorScheme = [];
for (var i = 0; i < filteredActivityColorWithSpaces.length; i++) {
newColor = filteredActivityColorWithSpaces[i].color;
if (oldColor != newColor) {
colorScheme.push({ratio: ratioCounter, color: newColor});
oldColor = newColor;
oldRatio = newRatio;
}
ratioCounter = ratioCounter + ratioActivity;
}
$scope.activityGradientArray = [];
for (var i = 1; i < colorScheme.length; i++) {
$scope.activityGradientArray.push({ratio: colorScheme[i-1].ratio, color: colorScheme[i-1].color});
$scope.activityGradientArray.push({ratio: colorScheme[i].ratio, color: colorScheme[i-1].color});
}
var lastPosition = colorScheme.length-1;
$scope.activityGradientArray.push({ratio: colorScheme[lastPosition].ratio, color: colorScheme[lastPosition].color});
$scope.activityGradientArray.push({ratio: 1, color: colorScheme[lastPosition].color});
};
Then I extend the Chart.controllers.line
, and use the activityGradientArray to apply a gradient to the chart.chart.ctx
, using addColorStop
var chartControllersLine = function(){
return Chart.controllers.line.extend({
update: function() {
if( ($scope.filteredChart.temperature.isNull()) && ($scope.filteredChart.activity.isNull()) ) {
return;
}
// get the min and max values
var minY = Math.min.apply(null, this.chart.data.datasets[$scope.CHART_DATASET_TEMPERATURE].data);
var maxY = Math.max.apply(null, this.chart.data.datasets[$scope.CHART_DATASET_TEMPERATURE].data);
$scope.minYValue = minY;
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xAxis = this.chart.scales['x-axis-0'];
if( (undefined == yScale) || (undefined == xAxis) ){
return;
}
var ctx = this.chart.chart.ctx;
// figure out the pixels for these and the value 0
var top = yScale.getPixelForValue(maxY)+5; //add
var bottom = yScale.getPixelForValue(minY)+5;
var left = xAxis.chart.chartArea.left;
var right = xAxis.chart.chartArea.right;
var minimumPixelPosition;
var maximumPixelPosition;
var minimumMarginPixelPosition;
var maximumMarginPixelPosition;
if( ($scope.homeConfig.rangeChartMin > minY) ){
minimumPixelPosition = yScale.getPixelForValue($scope.homeConfig.rangeChartMin);
} else {
minimumPixelPosition = yScale.getPixelForValue(minY);
}
if( ($scope.homeConfig.rangeChartMax < maxY) ) {
maximumPixelPosition = yScale.getPixelForValue($scope.homeConfig.rangeChartMax);
} else {
maximumPixelPosition = yScale.getPixelForValue(maxY);
}
var isAllOutside = false;
if( ( ($scope.homeConfig.rangeChartMax > maxY) && ($scope.homeConfig.rangeChartMin > maxY) ) ||
( ($scope.homeConfig.rangeChartMax < minY) && ($scope.homeConfig.rangeChartMin < minY) ) ) {
isAllOutside = true;
}
var ratioMinimumTemperature = Math.abs( Math.min((minimumPixelPosition - top) / (bottom - top), 1) );
var ratioMaximumTemperature = Math.abs( Math.min((maximumPixelPosition - top) / (bottom - top), 1) );
var ratioMinimumMarginTemperature = Math.abs( Math.min(((minimumPixelPosition+5) - top) / (bottom - top), 1) );
var ratioMaximumMarginTemperature = Math.abs( Math.min(((maximumPixelPosition+5) - top) / (bottom - top), 1) );
if(ratioMinimumTemperature < 0)
ratioMinimumTemperature = 0;
if(ratioMinimumTemperature > 1)
ratioMinimumTemperature = 1;
if(ratioMaximumTemperature < 0)
ratioMaximumTemperature = 0;
if(ratioMaximumTemperature > 1)
ratioMaximumTemperature = 1;
if(!$scope.filteredChart.activity.isNull()) {
var gradientActivity = ctx.createLinearGradient(left, 0, right, 0);
for(var i = 0; i<$scope.activityGradientArray.length; i++){
gradientActivity.addColorStop($scope.activityGradientArray[i].ratio, $scope.activityGradientArray[i].color);
}
this.chart.data.datasets[$scope.CHART_DATASET_ACTIVITY].borderColor = gradientActivity;
}
var auxApply = Chart.controllers.line.prototype.update.apply(this, arguments);
return auxApply;
}
});
};
Here’s is where I define the dataset:
var lineChartData = function () {
return {
xAxisID: "x-axis-0",
labels: angular.isDefined($scope.filteredChart.labels) ? $scope.filteredChart.labels : [],
datasets: [
{
yAxisID: "y-axis-activity",
label: "Activity",
fill: false,
data: angular.isDefined($scope.filteredChart.activity) ? $scope.filteredChart.activity : [],
borderWidth: 20,
pointRadius: 0,
borderColor: $scope.lineActivityColors,
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderWidth: 0,
pointHoverRadius: 5,
pointHitRadius: 10,
}]
}
};
And finally where I initialize the the chart with all of the above methods:
$(document).ready(function() {
var ctx = resetHomeChart();
if(null == ctx)
return;
$scope.pointTemperatureColors = [];
$scope.lineActivityColors = [];
Chart.defaults.NegativeTransparentLine = Chart.helpers.clone(Chart.defaults.line);
Chart.controllers.NegativeTransparentLine = chartControllersLine();
$scope.lineChart = new Chart(ctx, {
data: lineChartData(),
type: 'NegativeTransparentLine',
pointDotRadius: 10,
bezierCurve: false,
scaleShowVerticalLines: false,
scaleGridLineColor: "black",
options: options()
});
var maxTemperature = Math.max.apply(Math, $scope.lineChart.data.datasets[$scope.CHART_DATASET_TEMPERATURE].data.map(function(o) {
return o == null ? -Infinity : o;
}));
var minTemperature = Math.min.apply(Math, $scope.lineChart.data.datasets[$scope.CHART_DATASET_TEMPERATURE].data.map(function(o) {
return o == null ? Infinity : o;
}));
if( (Infinity == maxTemperature || -Infinity == maxTemperature) && (Infinity == minTemperature || -Infinity == minTemperature) ){
maxTemperature = 0;
minTemperature = 0;
}
var yScaleMaxValue = maxTemperature + 2;
var yScaleMinValue = minTemperature - 4;
$scope.lineChart.options.scales.yAxes[0].ticks.max = Math.floor(yScaleMaxValue);
$scope.lineChart.options.scales.yAxes[1].ticks.max = Math.floor(yScaleMaxValue);
$scope.lineChart.options.scales.yAxes[0].ticks.min = Math.round(yScaleMinValue);
$scope.lineChart.options.scales.yAxes[1].ticks.min = Math.round(yScaleMinValue);
//adjust bar position on the chart
for (var i = 0; i < $scope.lineChart.data.datasets[$scope.CHART_DATASET_ACTIVITY].data.length; i++) {
if(null != $scope.lineChart.data.datasets[$scope.CHART_DATASET_ACTIVITY].data[i]) {
$scope.lineChart.data.datasets[$scope.CHART_DATASET_ACTIVITY].data[i] = yScaleMinValue + 2;
}
}
$scope.lineChart.update();
});
};
Again, this is not an ideal solution. It takes a bit more of processing than I would expect, but it works, at least it did for me…
If anyone would have a better solution, please share it