mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Added usePointStyle option to label boxes
- Closes #2252 - Allows label boxes to match the shape(pointStyle) of the corresponding data. * Removed unused varaible from legend's draw()
This commit is contained in:
parent
3cdd66ca58
commit
0dccc85e3a
@ -132,6 +132,7 @@ fontColor | Color | "#666" | Font color inherited from global configuration
|
||||
fontFamily | String | "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" | Font family inherited from global configuration
|
||||
padding | Number | 10 | Padding between rows of colored boxes
|
||||
generateLabels: | Function | `function(chart) { }` | Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See [Legend Item](#chart-configuration-legend-item-interface) for details.
|
||||
usePointStyle | Boolean | false | Label style will match corresponding point style (size is based on fontSize, boxWidth is not used in this case).
|
||||
|
||||
#### Legend Item Interface
|
||||
|
||||
@ -165,6 +166,9 @@ Items passed to the legend `onClick` function are the ones returned from `labels
|
||||
|
||||
// Stroke style of the legend box
|
||||
strokeStyle: Color
|
||||
|
||||
// Point style of the legend box (only used if usePointStyle is true)
|
||||
pointStyle: String
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
var Chart = require('./core/core.js')();
|
||||
|
||||
require('./core/core.helpers')(Chart);
|
||||
require('./core/core.canvasHelpers')(Chart);
|
||||
require('./core/core.element')(Chart);
|
||||
require('./core/core.animation')(Chart);
|
||||
require('./core/core.controller')(Chart);
|
||||
|
||||
104
src/core/core.canvasHelpers.js
Normal file
104
src/core/core.canvasHelpers.js
Normal file
@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function(Chart) {
|
||||
// Global Chart canvas helpers object for drawing items to canvas
|
||||
var helpers = Chart.canvasHelpers = {};
|
||||
|
||||
helpers.drawPoint = function(ctx, pointStyle, radius, x, y) {
|
||||
var type, edgeLength, xOffset, yOffset, height, size;
|
||||
|
||||
if (typeof pointStyle === 'object') {
|
||||
type = pointStyle.toString();
|
||||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||||
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNaN(radius) || radius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pointStyle) {
|
||||
// Default includes circle
|
||||
default:
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'triangle':
|
||||
ctx.beginPath();
|
||||
edgeLength = 3 * radius / Math.sqrt(3);
|
||||
height = edgeLength * Math.sqrt(3) / 2;
|
||||
ctx.moveTo(x - edgeLength / 2, y + height / 3);
|
||||
ctx.lineTo(x + edgeLength / 2, y + height / 3);
|
||||
ctx.lineTo(x, y - 2 * height / 3);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'rect':
|
||||
size = 1 / Math.SQRT2 * radius;
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
|
||||
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
|
||||
break;
|
||||
case 'rectRot':
|
||||
size = 1 / Math.SQRT2 * radius;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - size, y);
|
||||
ctx.lineTo(x, y + size);
|
||||
ctx.lineTo(x + size, y);
|
||||
ctx.lineTo(x, y - size);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'cross':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y - radius);
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'crossRot':
|
||||
ctx.beginPath();
|
||||
xOffset = Math.cos(Math.PI / 4) * radius;
|
||||
yOffset = Math.sin(Math.PI / 4) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x - xOffset, y + yOffset);
|
||||
ctx.lineTo(x + xOffset, y - yOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'star':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y - radius);
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
xOffset = Math.cos(Math.PI / 4) * radius;
|
||||
yOffset = Math.sin(Math.PI / 4) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x - xOffset, y + yOffset);
|
||||
ctx.lineTo(x + xOffset, y - yOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'line':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'dash':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
};
|
||||
};
|
||||
@ -52,6 +52,7 @@ module.exports = function(Chart) {
|
||||
lineJoin: dataset.borderJoinStyle,
|
||||
lineWidth: dataset.borderWidth,
|
||||
strokeStyle: dataset.borderColor,
|
||||
pointStyle: dataset.pointStyle,
|
||||
|
||||
// Below is extra data used for toggling the datasets
|
||||
datasetIndex: i
|
||||
@ -201,7 +202,11 @@ module.exports = function(Chart) {
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
helpers.each(me.legendItems, function(legendItem, i) {
|
||||
var width = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
var boxWidth = labelOpts.usePointStyle ?
|
||||
fontSize * Math.sqrt(2) :
|
||||
labelOpts.boxWidth;
|
||||
|
||||
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
|
||||
totalHeight += fontSize + (labelOpts.padding);
|
||||
lineWidths[lineWidths.length] = me.left;
|
||||
@ -229,7 +234,11 @@ module.exports = function(Chart) {
|
||||
var itemHeight = fontSize + vPadding;
|
||||
|
||||
helpers.each(me.legendItems, function(legendItem, i) {
|
||||
var itemWidth = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
// If usePointStyle is set, multiple boxWidth by 2 since it represents
|
||||
// the radius and not truly the width
|
||||
var boxWidth = labelOpts.usePointStyle ? 2 * labelOpts.boxWidth : labelOpts.boxWidth;
|
||||
|
||||
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
|
||||
// If too tall, go to new column
|
||||
if (currentColHeight + itemHeight > minSize.height) {
|
||||
@ -274,10 +283,10 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
var opts = me.options;
|
||||
var labelOpts = opts.labels;
|
||||
var globalDefault = Chart.defaults.global;
|
||||
var lineDefault = globalDefault.elements.line;
|
||||
var legendWidth = me.width;
|
||||
var lineWidths = me.lineWidths;
|
||||
var globalDefault = Chart.defaults.global,
|
||||
lineDefault = globalDefault.elements.line,
|
||||
legendWidth = me.width,
|
||||
lineWidths = me.lineWidths;
|
||||
|
||||
if (opts.display) {
|
||||
var ctx = me.ctx,
|
||||
@ -302,6 +311,10 @@ module.exports = function(Chart) {
|
||||
|
||||
// current position
|
||||
var drawLegendBox = function(x, y, legendItem) {
|
||||
if (isNaN(boxWidth) || boxWidth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the ctx for the box
|
||||
ctx.save();
|
||||
|
||||
@ -317,9 +330,22 @@ module.exports = function(Chart) {
|
||||
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
|
||||
}
|
||||
|
||||
// Draw the box
|
||||
ctx.strokeRect(x, y, boxWidth, fontSize);
|
||||
ctx.fillRect(x, y, boxWidth, fontSize);
|
||||
if (opts.labels && opts.labels.usePointStyle) {
|
||||
// Recalulate x and y for drawPoint() because its expecting
|
||||
// x and y to be center of figure (instead of top left)
|
||||
var radius = fontSize * Math.SQRT2 / 2;
|
||||
var offSet = radius / Math.SQRT2;
|
||||
var centerX = x + offSet;
|
||||
var centerY = y + offSet;
|
||||
|
||||
// Draw pointStyle as legend symbol
|
||||
Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
|
||||
}
|
||||
else {
|
||||
// Draw box as legend symbol
|
||||
ctx.strokeRect(x, y, boxWidth, fontSize);
|
||||
ctx.fillRect(x, y, boxWidth, fontSize);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
@ -347,7 +373,7 @@ module.exports = function(Chart) {
|
||||
} else {
|
||||
cursor = {
|
||||
x: me.left + labelOpts.padding,
|
||||
y: me.top,
|
||||
y: me.top + labelOpts.padding,
|
||||
line: 0
|
||||
};
|
||||
}
|
||||
@ -355,13 +381,15 @@ module.exports = function(Chart) {
|
||||
var itemHeight = fontSize + labelOpts.padding;
|
||||
helpers.each(me.legendItems, function(legendItem, i) {
|
||||
var textWidth = ctx.measureText(legendItem.text).width,
|
||||
width = boxWidth + (fontSize / 2) + textWidth,
|
||||
width = labelOpts.usePointStyle ?
|
||||
fontSize + (fontSize / 2) + textWidth :
|
||||
boxWidth + (fontSize / 2) + textWidth,
|
||||
x = cursor.x,
|
||||
y = cursor.y;
|
||||
|
||||
if (isHorizontal) {
|
||||
if (x + width >= legendWidth) {
|
||||
y = cursor.y += fontSize + (labelOpts.padding);
|
||||
y = cursor.y += itemHeight;
|
||||
cursor.line++;
|
||||
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
|
||||
}
|
||||
|
||||
@ -42,108 +42,16 @@ module.exports = function(Chart) {
|
||||
var radius = vm.radius;
|
||||
var x = vm.x;
|
||||
var y = vm.y;
|
||||
var type, edgeLength, xOffset, yOffset, height, size;
|
||||
|
||||
if (vm.skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof pointStyle === 'object') {
|
||||
type = pointStyle.toString();
|
||||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||||
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNaN(radius) || radius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || defaultColor;
|
||||
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
|
||||
ctx.fillStyle = vm.backgroundColor || defaultColor;
|
||||
|
||||
switch (pointStyle) {
|
||||
// Default includes circle
|
||||
default:
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'triangle':
|
||||
ctx.beginPath();
|
||||
edgeLength = 3 * radius / Math.sqrt(3);
|
||||
height = edgeLength * Math.sqrt(3) / 2;
|
||||
ctx.moveTo(x - edgeLength / 2, y + height / 3);
|
||||
ctx.lineTo(x + edgeLength / 2, y + height / 3);
|
||||
ctx.lineTo(x, y - 2 * height / 3);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'rect':
|
||||
size = 1 / Math.SQRT2 * radius;
|
||||
ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
|
||||
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
|
||||
break;
|
||||
case 'rectRot':
|
||||
size = 1 / Math.SQRT2 * radius;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - size, y);
|
||||
ctx.lineTo(x, y + size);
|
||||
ctx.lineTo(x + size, y);
|
||||
ctx.lineTo(x, y - size);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
break;
|
||||
case 'cross':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y - radius);
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'crossRot':
|
||||
ctx.beginPath();
|
||||
xOffset = Math.cos(Math.PI / 4) * radius;
|
||||
yOffset = Math.sin(Math.PI / 4) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x - xOffset, y + yOffset);
|
||||
ctx.lineTo(x + xOffset, y - yOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'star':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y - radius);
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
xOffset = Math.cos(Math.PI / 4) * radius;
|
||||
yOffset = Math.sin(Math.PI / 4) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x - xOffset, y + yOffset);
|
||||
ctx.lineTo(x + xOffset, y - yOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'line':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'dash':
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.closePath();
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -52,6 +52,7 @@ describe('Legend block tests', function() {
|
||||
label: 'dataset3',
|
||||
borderWidth: 10,
|
||||
borderColor: 'green',
|
||||
pointStyle: 'crossRot',
|
||||
data: []
|
||||
}],
|
||||
labels: []
|
||||
@ -68,6 +69,7 @@ describe('Legend block tests', function() {
|
||||
lineJoin: undefined,
|
||||
lineWidth: undefined,
|
||||
strokeStyle: undefined,
|
||||
pointStyle: undefined,
|
||||
datasetIndex: 0
|
||||
}, {
|
||||
text: 'dataset2',
|
||||
@ -79,6 +81,7 @@ describe('Legend block tests', function() {
|
||||
lineJoin: 'miter',
|
||||
lineWidth: undefined,
|
||||
strokeStyle: undefined,
|
||||
pointStyle: undefined,
|
||||
datasetIndex: 1
|
||||
}, {
|
||||
text: 'dataset3',
|
||||
@ -90,6 +93,7 @@ describe('Legend block tests', function() {
|
||||
lineJoin: undefined,
|
||||
lineWidth: 10,
|
||||
strokeStyle: 'green',
|
||||
pointStyle: 'crossRot',
|
||||
datasetIndex: 2
|
||||
}]);
|
||||
});
|
||||
|
||||
@ -164,6 +164,9 @@ describe('Point element tests', function() {
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgba(0, 255, 0)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'fillRect',
|
||||
args: [10 - 1 / Math.SQRT2 * 2, 15 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user