mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
This PR allows for multi-line labels, as per Slack discussion..
Usage: If a label is an `array` as opposed to a `string` i.e. `[["June","2015"], "July"]` then each element is treated as a seperate line. The appropriate calculations are made to determine the correct height and width, and rotation is still supported. view samples/line-multiline-labels.html to see it working. On branch multiline_labels Changes to be committed: modified: docs/03-Line-Chart.md new file: samples/line-multiline-labels.html modified: src/core/core.helpers.js modified: src/core/core.scale.js
This commit is contained in:
parent
829c2be2bc
commit
2bc8e3c7f6
@ -96,7 +96,7 @@ The label key on each dataset is optional, and can be used when generating a sca
|
|||||||
|
|
||||||
### Data Points
|
### Data Points
|
||||||
|
|
||||||
The data passed to the chart can be passed in two formats. The most common method is to pass the data array as an array of numbers. In this case, the `data.labels` array must be specified and must contain a label for each point.
|
The data passed to the chart can be passed in two formats. The most common method is to pass the data array as an array of numbers. In this case, the `data.labels` array must be specified and must contain a label for each point or, in the case of labels to be displayed over multiple lines an array of labels (one for each line) i.e `[["June","2015"], "July"]`.
|
||||||
|
|
||||||
The alternate is used for sparse datasets. Data is specified using an object containing `x` and `y` properties. This is used for scatter charts as documented below.
|
The alternate is used for sparse datasets. Data is specified using an object containing `x` and `y` properties. This is used for scatter charts as documented below.
|
||||||
|
|
||||||
@ -177,4 +177,4 @@ var stackedLine = new Chart(ctx, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
218
samples/line-multiline-labels.html
Normal file
218
samples/line-multiline-labels.html
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Line Chart</title>
|
||||||
|
<script src="../dist/Chart.bundle.js"></script>
|
||||||
|
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<style>
|
||||||
|
canvas{
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div style="width:90%;">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<button id="randomizeData">Randomize Data</button>
|
||||||
|
<button id="changeDataObject">Change Data Object</button>
|
||||||
|
<button id="addDataset">Add Dataset</button>
|
||||||
|
<button id="removeDataset">Remove Dataset</button>
|
||||||
|
<button id="addData">Add Data</button>
|
||||||
|
<button id="removeData">Remove Data</button>
|
||||||
|
<script>
|
||||||
|
var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||||
|
|
||||||
|
var randomScalingFactor = function() {
|
||||||
|
return Math.round(Math.random() * 100);
|
||||||
|
//return 0;
|
||||||
|
};
|
||||||
|
var randomColorFactor = function() {
|
||||||
|
return Math.round(Math.random() * 255);
|
||||||
|
};
|
||||||
|
var randomColor = function(opacity) {
|
||||||
|
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||||
|
};
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [["June","2015"], "July", "August", "September", "October", "November", "December", ["January","2016"],"February", "March", "April", "May"],
|
||||||
|
datasets: [{
|
||||||
|
label: "My First dataset",
|
||||||
|
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||||
|
fill: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
}, {
|
||||||
|
hidden: true,
|
||||||
|
label: 'hidden dataset',
|
||||||
|
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||||
|
}, {
|
||||||
|
label: "My Second dataset",
|
||||||
|
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
title:{
|
||||||
|
display:true,
|
||||||
|
text:'Chart.js Line Chart'
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
mode: 'label',
|
||||||
|
callbacks: {
|
||||||
|
// beforeTitle: function() {
|
||||||
|
// return '...beforeTitle';
|
||||||
|
// },
|
||||||
|
// afterTitle: function() {
|
||||||
|
// return '...afterTitle';
|
||||||
|
// },
|
||||||
|
// beforeBody: function() {
|
||||||
|
// return '...beforeBody';
|
||||||
|
// },
|
||||||
|
// afterBody: function() {
|
||||||
|
// return '...afterBody';
|
||||||
|
// },
|
||||||
|
// beforeFooter: function() {
|
||||||
|
// return '...beforeFooter';
|
||||||
|
// },
|
||||||
|
// footer: function() {
|
||||||
|
// return 'Footer';
|
||||||
|
// },
|
||||||
|
// afterFooter: function() {
|
||||||
|
// return '...afterFooter';
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
mode: 'dataset'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
show: true,
|
||||||
|
labelString: 'Month'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
show: true,
|
||||||
|
labelString: 'Value'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
suggestedMin: -10,
|
||||||
|
suggestedMax: 250,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.each(config.data.datasets, function(i, dataset) {
|
||||||
|
dataset.borderColor = randomColor(0.4);
|
||||||
|
dataset.backgroundColor = randomColor(0.5);
|
||||||
|
dataset.pointBorderColor = randomColor(0.7);
|
||||||
|
dataset.pointBackgroundColor = randomColor(0.5);
|
||||||
|
dataset.pointBorderWidth = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
var ctx = document.getElementById("canvas").getContext("2d");
|
||||||
|
window.myLine = new Chart(ctx, config);
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#randomizeData').click(function() {
|
||||||
|
$.each(config.data.datasets, function(i, dataset) {
|
||||||
|
dataset.data = dataset.data.map(function() {
|
||||||
|
return randomScalingFactor();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
window.myLine.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#changeDataObject').click(function() {
|
||||||
|
config.data = {
|
||||||
|
labels: ["July", "August", "September", "October", "November", "December"],
|
||||||
|
datasets: [{
|
||||||
|
label: "My First dataset",
|
||||||
|
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||||
|
fill: false,
|
||||||
|
}, {
|
||||||
|
label: "My Second dataset",
|
||||||
|
fill: false,
|
||||||
|
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
$.each(config.data.datasets, function(i, dataset) {
|
||||||
|
dataset.borderColor = randomColor(0.4);
|
||||||
|
dataset.backgroundColor = randomColor(0.5);
|
||||||
|
dataset.pointBorderColor = randomColor(0.7);
|
||||||
|
dataset.pointBackgroundColor = randomColor(0.5);
|
||||||
|
dataset.pointBorderWidth = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the chart
|
||||||
|
window.myLine.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#addDataset').click(function() {
|
||||||
|
var newDataset = {
|
||||||
|
label: 'Dataset ' + config.data.datasets.length,
|
||||||
|
borderColor: randomColor(0.4),
|
||||||
|
backgroundColor: randomColor(0.5),
|
||||||
|
pointBorderColor: randomColor(0.7),
|
||||||
|
pointBackgroundColor: randomColor(0.5),
|
||||||
|
pointBorderWidth: 1,
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||||
|
newDataset.data.push(randomScalingFactor());
|
||||||
|
}
|
||||||
|
|
||||||
|
config.data.datasets.push(newDataset);
|
||||||
|
window.myLine.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#addData').click(function() {
|
||||||
|
if (config.data.datasets.length > 0) {
|
||||||
|
var month = MONTHS[config.data.labels.length % MONTHS.length];
|
||||||
|
config.data.labels.push(month);
|
||||||
|
|
||||||
|
$.each(config.data.datasets, function(i, dataset) {
|
||||||
|
dataset.data.push(randomScalingFactor());
|
||||||
|
});
|
||||||
|
|
||||||
|
window.myLine.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#removeDataset').click(function() {
|
||||||
|
config.data.datasets.splice(0, 1);
|
||||||
|
window.myLine.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#removeData').click(function() {
|
||||||
|
config.data.labels.splice(-1, 1); // remove the label first
|
||||||
|
|
||||||
|
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||||
|
dataset.data.pop();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.myLine.update();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -817,7 +817,7 @@ module.exports = function(Chart) {
|
|||||||
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
|
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
|
||||||
return fontStyle + " " + pixelSize + "px " + fontFamily;
|
return fontStyle + " " + pixelSize + "px " + fontFamily;
|
||||||
};
|
};
|
||||||
helpers.longestText = function(ctx, font, arrayOfStrings, cache) {
|
helpers.longestText = function(ctx, font, arrayOfThings, cache) {
|
||||||
cache = cache || {};
|
cache = cache || {};
|
||||||
var data = cache.data = cache.data || {};
|
var data = cache.data = cache.data || {};
|
||||||
var gc = cache.garbageCollect = cache.garbageCollect || [];
|
var gc = cache.garbageCollect = cache.garbageCollect || [];
|
||||||
@ -830,31 +830,53 @@ module.exports = function(Chart) {
|
|||||||
|
|
||||||
ctx.font = font;
|
ctx.font = font;
|
||||||
var longest = 0;
|
var longest = 0;
|
||||||
helpers.each(arrayOfStrings, function(string) {
|
helpers.each(arrayOfThings, function(thing) {
|
||||||
// Undefined strings should not be measured
|
// Undefined strings and arrays should not be measured
|
||||||
if (string !== undefined && string !== null) {
|
if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
|
||||||
var textWidth = data[string];
|
longest = helpers.measureText(ctx, data, gc, longest, thing);
|
||||||
if (!textWidth) {
|
} else if (helpers.isArray(thing)) {
|
||||||
textWidth = data[string] = ctx.measureText(string).width;
|
// if it is an array lets measure each element
|
||||||
gc.push(string);
|
// to do maybe simplify this function a bit so we can do this more recursively?
|
||||||
}
|
helpers.each(thing, function(nestedThing) {
|
||||||
|
// Undefined strings and arrays should not be measured
|
||||||
if (textWidth > longest) {
|
if (nestedThing !== undefined && nestedThing !== null && helpers.isArray(nestedThing)) {
|
||||||
longest = textWidth;
|
longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var gcLen = gc.length / 2;
|
var gcLen = gc.length / 2;
|
||||||
if (gcLen > arrayOfStrings.length) {
|
if (gcLen > arrayOfThings.length) {
|
||||||
for (var i = 0; i < gcLen; i++) {
|
for (var i = 0; i < gcLen; i++) {
|
||||||
delete data[gc[i]];
|
delete data[gc[i]];
|
||||||
}
|
}
|
||||||
gc.splice(0, gcLen);
|
gc.splice(0, gcLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
return longest;
|
return longest;
|
||||||
};
|
};
|
||||||
|
helpers.measureText = function (ctx, data, gc, longest, string) {
|
||||||
|
var textWidth = data[string];
|
||||||
|
if (!textWidth) {
|
||||||
|
textWidth = data[string] = ctx.measureText(string).width;
|
||||||
|
gc.push(string);
|
||||||
|
}
|
||||||
|
if (textWidth > longest) {
|
||||||
|
longest = textWidth;
|
||||||
|
}
|
||||||
|
return longest;
|
||||||
|
};
|
||||||
|
helpers.numberOfLabelLines = function(arrayOfThings) {
|
||||||
|
var numberOfLines = 1;
|
||||||
|
helpers.each(arrayOfThings, function(thing) {
|
||||||
|
if (helpers.isArray(thing)) {
|
||||||
|
if (thing.length > numberOfLines) {
|
||||||
|
numberOfLines = thing.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return numberOfLines;
|
||||||
|
};
|
||||||
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
|
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x + radius, y);
|
ctx.moveTo(x + radius, y);
|
||||||
|
|||||||
@ -44,7 +44,7 @@ module.exports = function(Chart) {
|
|||||||
autoSkipPadding: 0,
|
autoSkipPadding: 0,
|
||||||
labelOffset: 0,
|
labelOffset: 0,
|
||||||
callback: function(value) {
|
callback: function(value) {
|
||||||
return '' + value;
|
return helpers.isArray(value) ? value : '' + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -315,13 +315,15 @@ module.exports = function(Chart) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var largestTextWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache);
|
var largestTextWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache);
|
||||||
|
var tallestLabelHeightInLines = helpers.numberOfLabelLines(this.ticks);
|
||||||
|
var lineSpace = tickFontSize * 0.5;
|
||||||
|
|
||||||
if (isHorizontal) {
|
if (isHorizontal) {
|
||||||
// A horizontal axis is more constrained by the height.
|
// A horizontal axis is more constrained by the height.
|
||||||
this.longestLabelWidth = largestTextWidth;
|
this.longestLabelWidth = largestTextWidth;
|
||||||
|
|
||||||
// TODO - improve this calculation
|
// TODO - improve this calculation
|
||||||
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + 1.5 * tickFontSize;
|
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines);
|
||||||
|
|
||||||
minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight);
|
minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight);
|
||||||
this.ctx.font = tickLabelFont;
|
this.ctx.font = tickLabelFont;
|
||||||
@ -546,6 +548,7 @@ module.exports = function(Chart) {
|
|||||||
helpers.each(this.ticks, function (label, index) {
|
helpers.each(this.ticks, function (label, index) {
|
||||||
// Blank optionTicks
|
// Blank optionTicks
|
||||||
var isLastTick = this.ticks.length === index + 1;
|
var isLastTick = this.ticks.length === index + 1;
|
||||||
|
var lineHeight;
|
||||||
|
|
||||||
// Since we always show the last tick,we need may need to hide the last shown one before
|
// Since we always show the last tick,we need may need to hide the last shown one before
|
||||||
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= this.ticks.length);
|
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= this.ticks.length);
|
||||||
@ -594,7 +597,18 @@ module.exports = function(Chart) {
|
|||||||
context.font = tickLabelFont;
|
context.font = tickLabelFont;
|
||||||
context.textAlign = (isRotated) ? "right" : "center";
|
context.textAlign = (isRotated) ? "right" : "center";
|
||||||
context.textBaseline = (isRotated) ? "middle" : options.position === "top" ? "bottom" : "top";
|
context.textBaseline = (isRotated) ? "middle" : options.position === "top" ? "bottom" : "top";
|
||||||
context.fillText(label, 0, 0);
|
|
||||||
|
lineHeight = context.measureText("M").width * 1.2;
|
||||||
|
|
||||||
|
if (helpers.isArray(label)) {
|
||||||
|
for (var i = 0, y = 0; i < label.length; ++i) {
|
||||||
|
context.fillText(label[i], 0, y);
|
||||||
|
y += lineHeight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.fillText(label, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user