mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Reduce size of on-canvas legend
This commit is contained in:
parent
eb126fdd27
commit
eeae8a6a16
@ -3,6 +3,7 @@
|
||||
module.exports = function(Chart) {
|
||||
|
||||
var helpers = Chart.helpers;
|
||||
var noop = helpers.noop;
|
||||
|
||||
Chart.defaults.global.legend = {
|
||||
|
||||
@ -14,13 +15,14 @@ module.exports = function(Chart) {
|
||||
// a callback that will handle
|
||||
onClick: function(e, legendItem) {
|
||||
var index = legendItem.datasetIndex;
|
||||
var meta = this.chart.getDatasetMeta(index);
|
||||
var ci = this.chart;
|
||||
var meta = ci.getDatasetMeta(index);
|
||||
|
||||
// See controller.isDatasetVisible comment
|
||||
meta.hidden = meta.hidden === null? !this.chart.data.datasets[index].hidden : null;
|
||||
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
|
||||
|
||||
// We hid a dataset ... rerender the chart
|
||||
this.chart.update();
|
||||
ci.update();
|
||||
},
|
||||
|
||||
labels: {
|
||||
@ -75,7 +77,7 @@ module.exports = function(Chart) {
|
||||
// Any function defined here is inherited by all legend types.
|
||||
// Any function can be extended by the legend type
|
||||
|
||||
beforeUpdate: helpers.noop,
|
||||
beforeUpdate: noop,
|
||||
update: function(maxWidth, maxHeight, margins) {
|
||||
|
||||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||||
@ -104,11 +106,11 @@ module.exports = function(Chart) {
|
||||
|
||||
return this.minSize;
|
||||
},
|
||||
afterUpdate: helpers.noop,
|
||||
afterUpdate: noop,
|
||||
|
||||
//
|
||||
|
||||
beforeSetDimensions: helpers.noop,
|
||||
beforeSetDimensions: noop,
|
||||
setDimensions: function() {
|
||||
// Set the unconstrained dimension before label rotation
|
||||
if (this.isHorizontal()) {
|
||||
@ -136,90 +138,92 @@ module.exports = function(Chart) {
|
||||
height: 0
|
||||
};
|
||||
},
|
||||
afterSetDimensions: helpers.noop,
|
||||
afterSetDimensions: noop,
|
||||
|
||||
//
|
||||
|
||||
beforeBuildLabels: helpers.noop,
|
||||
beforeBuildLabels: noop,
|
||||
buildLabels: function() {
|
||||
this.legendItems = this.options.labels.generateLabels.call(this, this.chart);
|
||||
if(this.options.reverse){
|
||||
this.legendItems.reverse();
|
||||
}
|
||||
},
|
||||
afterBuildLabels: helpers.noop,
|
||||
afterBuildLabels: noop,
|
||||
|
||||
//
|
||||
|
||||
beforeFit: helpers.noop,
|
||||
beforeFit: noop,
|
||||
fit: function() {
|
||||
var opts = this.options;
|
||||
var labelOpts = opts.labels;
|
||||
var display = opts.display;
|
||||
|
||||
var ctx = this.ctx;
|
||||
var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize);
|
||||
var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle);
|
||||
var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily);
|
||||
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
|
||||
var globalDefault = Chart.defaults.global,
|
||||
itemOrDefault = helpers.getValueOrDefault,
|
||||
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
|
||||
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
|
||||
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
|
||||
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
|
||||
// Reset hit boxes
|
||||
this.legendHitBoxes = [];
|
||||
var hitboxes = this.legendHitBoxes = [];
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.width = this.maxWidth; // fill all the width
|
||||
} else {
|
||||
this.minSize.width = this.options.display ? 10 : 0;
|
||||
}
|
||||
var minSize = this.minSize;
|
||||
var isHorizontal = this.isHorizontal();
|
||||
|
||||
// height
|
||||
if (this.isHorizontal()) {
|
||||
this.minSize.height = this.options.display ? 10 : 0;
|
||||
if (isHorizontal) {
|
||||
minSize.width = this.maxWidth; // fill all the width
|
||||
minSize.height = display ? 10 : 0;
|
||||
} else {
|
||||
this.minSize.height = this.maxHeight; // fill all the height
|
||||
minSize.width = display ? 10 : 0;
|
||||
minSize.height = this.maxHeight; // fill all the height
|
||||
}
|
||||
|
||||
// Increase sizes here
|
||||
if (this.options.display) {
|
||||
if (this.isHorizontal()) {
|
||||
if (display) {
|
||||
if (isHorizontal) {
|
||||
// Labels
|
||||
|
||||
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
|
||||
this.lineWidths = [0];
|
||||
var totalHeight = this.legendItems.length ? fontSize + (this.options.labels.padding) : 0;
|
||||
var lineWidths = this.lineWidths = [0];
|
||||
var totalHeight = this.legendItems.length ? fontSize + (labelOpts.padding) : 0;
|
||||
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.font = labelFont;
|
||||
|
||||
helpers.each(this.legendItems, function(legendItem, i) {
|
||||
var width = this.options.labels.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
if (this.lineWidths[this.lineWidths.length - 1] + width + this.options.labels.padding >= this.width) {
|
||||
totalHeight += fontSize + (this.options.labels.padding);
|
||||
this.lineWidths[this.lineWidths.length] = this.left;
|
||||
var width = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= this.width) {
|
||||
totalHeight += fontSize + (labelOpts.padding);
|
||||
lineWidths[lineWidths.length] = this.left;
|
||||
}
|
||||
|
||||
// Store the hitbox width and height here. Final position will be updated in `draw`
|
||||
this.legendHitBoxes[i] = {
|
||||
hitboxes[i] = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: width,
|
||||
height: fontSize
|
||||
};
|
||||
|
||||
this.lineWidths[this.lineWidths.length - 1] += width + this.options.labels.padding;
|
||||
lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
|
||||
}, this);
|
||||
|
||||
this.minSize.height += totalHeight;
|
||||
minSize.height += totalHeight;
|
||||
|
||||
} else {
|
||||
// TODO vertical
|
||||
}
|
||||
}
|
||||
|
||||
this.width = this.minSize.width;
|
||||
this.height = this.minSize.height;
|
||||
|
||||
this.width = minSize.width;
|
||||
this.height = minSize.height;
|
||||
},
|
||||
afterFit: helpers.noop,
|
||||
afterFit: noop,
|
||||
|
||||
// Shared Methods
|
||||
isHorizontal: function() {
|
||||
@ -228,19 +232,26 @@ module.exports = function(Chart) {
|
||||
|
||||
// Actualy draw the legend on the canvas
|
||||
draw: function() {
|
||||
if (this.options.display) {
|
||||
var ctx = this.ctx;
|
||||
var cursor = {
|
||||
x: this.left + ((this.width - this.lineWidths[0]) / 2),
|
||||
y: this.top + this.options.labels.padding,
|
||||
line: 0
|
||||
};
|
||||
var opts = this.options;
|
||||
var labelOpts = opts.labels;
|
||||
var globalDefault = Chart.defaults.global,
|
||||
lineDefault = globalDefault.elements.line,
|
||||
legendWidth = this.width,
|
||||
lineWidths = this.lineWidths;
|
||||
|
||||
var fontColor = helpers.getValueOrDefault(this.options.labels.fontColor, Chart.defaults.global.defaultFontColor);
|
||||
var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize);
|
||||
var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle);
|
||||
var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily);
|
||||
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
if (opts.display) {
|
||||
var ctx = this.ctx,
|
||||
cursor = {
|
||||
x: this.left + ((legendWidth - lineWidths[0]) / 2),
|
||||
y: this.top + labelOpts.padding,
|
||||
line: 0
|
||||
},
|
||||
itemOrDefault = helpers.getValueOrDefault,
|
||||
fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor),
|
||||
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
|
||||
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
|
||||
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
|
||||
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
|
||||
|
||||
// Horizontal
|
||||
if (this.isHorizontal()) {
|
||||
@ -252,57 +263,58 @@ module.exports = function(Chart) {
|
||||
ctx.fillStyle = fontColor; // render in correct colour
|
||||
ctx.font = labelFont;
|
||||
|
||||
helpers.each(this.legendItems, function(legendItem, i) {
|
||||
var textWidth = ctx.measureText(legendItem.text).width;
|
||||
var width = this.options.labels.boxWidth + (fontSize / 2) + textWidth;
|
||||
var boxWidth = labelOpts.boxWidth,
|
||||
hitboxes = this.legendHitBoxes;
|
||||
|
||||
if (cursor.x + width >= this.width) {
|
||||
cursor.y += fontSize + (this.options.labels.padding);
|
||||
helpers.each(this.legendItems, function(legendItem, i) {
|
||||
var textWidth = ctx.measureText(legendItem.text).width,
|
||||
width = boxWidth + (fontSize / 2) + textWidth,
|
||||
x = cursor.x,
|
||||
y = cursor.y;
|
||||
|
||||
if (x + width >= legendWidth) {
|
||||
cursor.y += fontSize + (labelOpts.padding);
|
||||
cursor.line++;
|
||||
cursor.x = this.left + ((this.width - this.lineWidths[cursor.line]) / 2);
|
||||
cursor.x = this.left + ((legendWidth - lineWidths[cursor.line]) / 2);
|
||||
}
|
||||
|
||||
// Set the ctx for the box
|
||||
ctx.save();
|
||||
|
||||
var itemOrDefault = function(item, defaulVal) {
|
||||
return item !== undefined ? item : defaulVal;
|
||||
};
|
||||
|
||||
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, Chart.defaults.global.defaultColor);
|
||||
ctx.lineCap = itemOrDefault(legendItem.lineCap, Chart.defaults.global.elements.line.borderCapStyle);
|
||||
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, Chart.defaults.global.elements.line.borderDashOffset);
|
||||
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, Chart.defaults.global.elements.line.borderJoinStyle);
|
||||
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, Chart.defaults.global.elements.line.borderWidth);
|
||||
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, Chart.defaults.global.defaultColor);
|
||||
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
|
||||
ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
|
||||
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
|
||||
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
|
||||
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
|
||||
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
|
||||
|
||||
if (ctx.setLineDash) {
|
||||
// IE 9 and 10 do not support line dash
|
||||
ctx.setLineDash(itemOrDefault(legendItem.lineDash, Chart.defaults.global.elements.line.borderDash));
|
||||
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
|
||||
}
|
||||
|
||||
// Draw the box
|
||||
ctx.strokeRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize);
|
||||
ctx.fillRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize);
|
||||
ctx.strokeRect(x, y, boxWidth, fontSize);
|
||||
ctx.fillRect(x, y, boxWidth, fontSize);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
this.legendHitBoxes[i].left = cursor.x;
|
||||
this.legendHitBoxes[i].top = cursor.y;
|
||||
hitboxes[i].left = x;
|
||||
hitboxes[i].top = y;
|
||||
|
||||
// Fill the actual label
|
||||
ctx.fillText(legendItem.text, this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y);
|
||||
ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y);
|
||||
|
||||
if (legendItem.hidden) {
|
||||
// Strikethrough the text if hidden
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.moveTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y + (fontSize / 2));
|
||||
ctx.lineTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x + textWidth, cursor.y + (fontSize / 2));
|
||||
ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2));
|
||||
ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2));
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
cursor.x += width + (this.options.labels.padding);
|
||||
cursor.x += width + (labelOpts.padding);
|
||||
}, this);
|
||||
} else {
|
||||
|
||||
@ -312,17 +324,21 @@ module.exports = function(Chart) {
|
||||
|
||||
// Handle an event
|
||||
handleEvent: function(e) {
|
||||
var position = helpers.getRelativePosition(e, this.chart.chart);
|
||||
var position = helpers.getRelativePosition(e, this.chart.chart),
|
||||
x = position.x,
|
||||
y = position.y,
|
||||
opts = this.options;
|
||||
|
||||
if (position.x >= this.left && position.x <= this.right && position.y >= this.top && position.y <= this.bottom) {
|
||||
if (x >= this.left && x <= this.right && y >= this.top && y <= this.bottom) {
|
||||
// See if we are touching one of the dataset boxes
|
||||
for (var i = 0; i < this.legendHitBoxes.length; ++i) {
|
||||
var hitBox = this.legendHitBoxes[i];
|
||||
var lh = this.legendHitBoxes;
|
||||
for (var i = 0; i < lh.length; ++i) {
|
||||
var hitBox = lh[i];
|
||||
|
||||
if (position.x >= hitBox.left && position.x <= hitBox.left + hitBox.width && position.y >= hitBox.top && position.y <= hitBox.top + hitBox.height) {
|
||||
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
|
||||
// Touching an element
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.legendItems[i]);
|
||||
if (opts.onClick) {
|
||||
opts.onClick.call(this, e, this.legendItems[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user