--- /dev/null
+<!doctype html>
+<html>
+
+<head>
+ <title>Legend Positions</title>
+ <script src="../../dist/Chart.min.js"></script>
+ <script src="../utils.js"></script>
+ <style>
+ canvas {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+ .chart-container {
+ width: 500px;
+ margin-left: 40px;
+ margin-right: 40px;
+ }
+ .container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ </style>
+</head>
+
+<body>
+ <div class="container">
+ <div class="chart-container">
+ <canvas id="chart-legend-top-start"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-legend-top-center"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-legend-top-end"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-legend-left-start"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-legend-left-center"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="chart-legend-left-end"></canvas>
+ </div>
+ </div>
+ <script>
+ var color = Chart.helpers.color;
+ function createConfig(legendPosition, titlePosition, align, colorName) {
+ return {
+ type: 'line',
+ data: {
+ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+ datasets: [{
+ label: 'My First dataset',
+ data: [
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor()
+ ],
+ backgroundColor: color(window.chartColors[colorName]).alpha(0.5).rgbString(),
+ borderColor: window.chartColors[colorName],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ responsive: true,
+ legend: {
+ align: align,
+ position: legendPosition,
+ title: {
+ display: true,
+ text: 'Legend Title',
+ position: titlePosition,
+ }
+ },
+ scales: {
+ x: {
+ display: true,
+ scaleLabel: {
+ display: true,
+ labelString: 'Month'
+ }
+ },
+ y: {
+ display: true,
+ scaleLabel: {
+ display: true,
+ labelString: 'Value'
+ }
+ }
+ },
+ title: {
+ display: true,
+ text: 'Legend Title Position: ' + titlePosition
+ }
+ }
+ };
+ }
+
+ window.onload = function() {
+ [{
+ id: 'chart-legend-top-start',
+ align: 'start',
+ legendPosition: 'top',
+ titlePosition: 'start',
+ color: 'red'
+ }, {
+ id: 'chart-legend-top-center',
+ align: 'center',
+ legendPosition: 'top',
+ titlePosition: 'center',
+ color: 'orange'
+ }, {
+ id: 'chart-legend-top-end',
+ align: 'end',
+ legendPosition: 'top',
+ titlePosition: 'end',
+ color: 'yellow'
+ }, {
+ id: 'chart-legend-left-start',
+ align: 'start',
+ legendPosition: 'left',
+ titlePosition: 'start',
+ color: 'green'
+ }, {
+ id: 'chart-legend-left-center',
+ align: 'center',
+ legendPosition: 'left',
+ titlePosition: 'center',
+ color: 'blue'
+ }, {
+ id: 'chart-legend-left-end',
+ align: 'end',
+ legendPosition: 'left',
+ titlePosition: 'end',
+ color: 'purple'
+ }].forEach(function(details) {
+ var ctx = document.getElementById(details.id).getContext('2d');
+ var config = createConfig(details.legendPosition, details.titlePosition, details.align, details.color);
+ new Chart(ctx, config);
+ });
+ };
+ </script>
+</body>
+
+</html>
};
}, this);
}
+ },
+
+ title: {
+ display: false,
+ position: 'center',
+ text: '',
}
});
beforeFit() {}
fit() {
- var me = this;
- var opts = me.options;
- var labelOpts = opts.labels;
- var display = opts.display;
-
- var ctx = me.ctx;
+ const me = this;
+ const opts = me.options;
+ const labelOpts = opts.labels;
+ const display = opts.display;
- var labelFont = helpers.options._parseFont(labelOpts);
- var fontSize = labelFont.size;
+ const ctx = me.ctx;
+ const labelFont = helpers.options._parseFont(labelOpts);
+ const fontSize = labelFont.size;
// Reset hit boxes
- var hitboxes = me.legendHitBoxes = [];
+ const hitboxes = me.legendHitBoxes = [];
- var minSize = me._minSize;
- var isHorizontal = me.isHorizontal();
+ const minSize = me._minSize;
+ const isHorizontal = me.isHorizontal();
+ const titleHeight = me._computeTitleHeight();
if (isHorizontal) {
minSize.width = me.maxWidth; // fill all the width
ctx.font = labelFont.string;
if (isHorizontal) {
- // Labels
-
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
- var lineWidths = me.lineWidths = [0];
- var totalHeight = 0;
+ const lineWidths = me.lineWidths = [0];
+ let totalHeight = titleHeight;
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
me.legendItems.forEach(function(legendItem, i) {
- var boxWidth = getBoxWidth(labelOpts, fontSize);
- var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
+ const boxWidth = getBoxWidth(labelOpts, fontSize);
+ const width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
totalHeight += fontSize + labelOpts.padding;
minSize.height += totalHeight;
} else {
- var vPadding = labelOpts.padding;
- var columnWidths = me.columnWidths = [];
- var columnHeights = me.columnHeights = [];
- var totalWidth = labelOpts.padding;
- var currentColWidth = 0;
- var currentColHeight = 0;
-
+ const vPadding = labelOpts.padding;
+ const columnWidths = me.columnWidths = [];
+ const columnHeights = me.columnHeights = [];
+ let totalWidth = labelOpts.padding;
+ let currentColWidth = 0;
+ let currentColHeight = 0;
+
+ let heightLimit = minSize.height - titleHeight;
me.legendItems.forEach(function(legendItem, i) {
- var boxWidth = getBoxWidth(labelOpts, fontSize);
- var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
+ const boxWidth = getBoxWidth(labelOpts, fontSize);
+ const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
// If too tall, go to new column
- if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) {
+ if (i > 0 && currentColHeight + fontSize + 2 * vPadding > heightLimit) {
totalWidth += currentColWidth + labelOpts.padding;
columnWidths.push(currentColWidth); // previous column width
columnHeights.push(currentColHeight);
// Actually draw the legend on the canvas
draw() {
- var me = this;
- var opts = me.options;
- var labelOpts = opts.labels;
- var defaultColor = defaults.color;
- var lineDefault = defaults.elements.line;
- var legendHeight = me.height;
- var columnHeights = me.columnHeights;
- var legendWidth = me.width;
- var lineWidths = me.lineWidths;
+ const me = this;
+ const opts = me.options;
+ const labelOpts = opts.labels;
+ const defaultColor = defaults.color;
+ const lineDefault = defaults.elements.line;
+ const legendHeight = me.height;
+ const columnHeights = me.columnHeights;
+ const legendWidth = me.width;
+ const lineWidths = me.lineWidths;
if (!opts.display) {
return;
}
- var rtlHelper = getRtlHelper(opts.rtl, me.left, me._minSize.width);
- var ctx = me.ctx;
- var fontColor = valueOrDefault(labelOpts.fontColor, defaults.fontColor);
- var labelFont = helpers.options._parseFont(labelOpts);
- var fontSize = labelFont.size;
- var cursor;
+ me._drawTitle();
+ const rtlHelper = getRtlHelper(opts.rtl, me.left, me._minSize.width);
+ const ctx = me.ctx;
+ const fontColor = valueOrDefault(labelOpts.fontColor, defaults.fontColor);
+ const labelFont = helpers.options._parseFont(labelOpts);
+ const fontSize = labelFont.size;
+ let cursor;
// Canvas setup
ctx.textAlign = rtlHelper.textAlign('left');
};
// Horizontal
- var isHorizontal = me.isHorizontal();
+ const isHorizontal = me.isHorizontal();
+ const titleHeight = this._computeTitleHeight();
if (isHorizontal) {
cursor = {
x: me.left + alignmentOffset(legendWidth, lineWidths[0]),
- y: me.top + labelOpts.padding,
+ y: me.top + labelOpts.padding + titleHeight,
line: 0
};
} else {
cursor = {
x: me.left + labelOpts.padding,
- y: me.top + alignmentOffset(legendHeight, columnHeights[0]),
+ y: me.top + alignmentOffset(legendHeight, columnHeights[0]) + titleHeight,
line: 0
};
}
helpers.rtl.restoreTextDirection(me.ctx, opts.textDirection);
}
+ _drawTitle() {
+ const me = this;
+ const opts = me.options;
+ const titleOpts = opts.title;
+ const titleFont = helpers.options._parseFont(titleOpts);
+ const titlePadding = helpers.options.toPadding(titleOpts.padding);
+
+ if (!titleOpts.display) {
+ return;
+ }
+
+ const rtlHelper = getRtlHelper(opts.rtl, me.left, me.minSize.width);
+ const ctx = me.ctx;
+ const fontColor = valueOrDefault(titleOpts.fontColor, defaults.fontColor);
+ const position = titleOpts.position;
+ let x, textAlign;
+
+ const halfFontSize = titleFont.size / 2;
+ let y = me.top + titlePadding.top + halfFontSize;
+
+ // These defaults are used when the legend is vertical.
+ // When horizontal, they are computed below.
+ let left = me.left;
+ let maxWidth = me.width;
+
+ if (this.isHorizontal()) {
+ // Move left / right so that the title is above the legend lines
+ maxWidth = Math.max(...me.lineWidths);
+ switch (opts.align) {
+ case 'start':
+ // left is already correct in this case
+ break;
+ case 'end':
+ left = me.right - maxWidth;
+ break;
+ default:
+ left = ((me.left + me.right) / 2) - (maxWidth / 2);
+ break;
+ }
+ } else {
+ // Move down so that the title is above the legend stack in every alignment
+ const maxHeight = Math.max(...me.columnHeights);
+ switch (opts.align) {
+ case 'start':
+ // y is already correct in this case
+ break;
+ case 'end':
+ y += me.height - maxHeight;
+ break;
+ default: // center
+ y += (me.height - maxHeight) / 2;
+ break;
+ }
+ }
+
+ // Now that we know the left edge of the inner legend box, compute the correct
+ // X coordinate from the title alignment
+ switch (position) {
+ case 'start':
+ x = left;
+ textAlign = 'left';
+ break;
+ case 'end':
+ x = left + maxWidth;
+ textAlign = 'right';
+ break;
+ default:
+ x = left + (maxWidth / 2);
+ textAlign = 'center';
+ break;
+ }
+
+ // Canvas setup
+ ctx.textAlign = rtlHelper.textAlign(textAlign);
+ ctx.textBaseline = 'middle';
+ ctx.strokeStyle = fontColor;
+ ctx.fillStyle = fontColor;
+ ctx.font = titleFont.string;
+
+ ctx.fillText(titleOpts.text, x, y);
+ }
+
+ _computeTitleHeight() {
+ const titleOpts = this.options.title;
+ const titleFont = helpers.options._parseFont(titleOpts);
+ const titlePadding = helpers.options.toPadding(titleOpts.padding);
+ return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;
+ }
+
/**
* @private
*/