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
// Stroke style of the legend box
strokeStyle: Color
+
+ // Point style of the legend box (only used if usePointStyle is true)
+ pointStyle: String
}
```
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);
--- /dev/null
+"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();
+ };
+};
\ No newline at end of file
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
+ pointStyle: dataset.pointStyle,
// Below is extra data used for toggling the datasets
datasetIndex: i
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;
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) {
// current position
var drawLegendBox = function(x, y, legendItem) {
+ if (isNaN(boxWidth) || boxWidth <= 0) {
+ return;
+ }
+
// Set the ctx for the box
ctx.save();
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();
};
} else {
cursor = {
x: me.left + labelOpts.padding,
- y: me.top,
+ y: me.top + labelOpts.padding,
line: 0
};
}
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);
}
}
}
-
drawLegendBox(x, y, legendItem);
hitboxes[i].left = x;
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);
}
});
};
label: 'dataset3',
borderWidth: 10,
borderColor: 'green',
+ pointStyle: 'crossRot',
data: []
}],
labels: []
lineJoin: undefined,
lineWidth: undefined,
strokeStyle: undefined,
+ pointStyle: undefined,
datasetIndex: 0
}, {
text: 'dataset2',
lineJoin: 'miter',
lineWidth: undefined,
strokeStyle: undefined,
+ pointStyle: undefined,
datasetIndex: 1
}, {
text: 'dataset3',
lineJoin: undefined,
lineWidth: 10,
strokeStyle: 'green',
+ pointStyle: 'crossRot',
datasetIndex: 2
}]);
});
}, {
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]