From: unknown Date: Sun, 3 Jul 2016 19:55:43 +0000 (-0600) Subject: Issue #2252, added usePointStyle option to allow label boxes to match the shape(point... X-Git-Tag: v2.2.0-rc.2~3^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d38111723676d51d6a2b49bd3eec71b675d508ca;p=thirdparty%2FChart.js.git Issue #2252, added usePointStyle option to allow label boxes to match the shape(pointStyle) of the corresponding data. --- diff --git a/docs/01-Chart-Configuration.md b/docs/01-Chart-Configuration.md index f2be7e3c2..a573c51ff 100644 --- a/docs/01-Chart-Configuration.md +++ b/docs/01-Chart-Configuration.md @@ -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 } ``` diff --git a/src/chart.js b/src/chart.js index 75bd47538..a12890aa0 100644 --- a/src/chart.js +++ b/src/chart.js @@ -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); diff --git a/src/core/core.canvasHelpers.js b/src/core/core.canvasHelpers.js new file mode 100644 index 000000000..f8c35c96e --- /dev/null +++ b/src/core/core.canvasHelpers.js @@ -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(); + }; +}; \ No newline at end of file diff --git a/src/core/core.legend.js b/src/core/core.legend.js index 78c89c559..26e17f991 100644 --- a/src/core/core.legend.js +++ b/src/core/core.legend.js @@ -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) { @@ -303,6 +312,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(); @@ -318,9 +331,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(); }; @@ -348,7 +374,7 @@ module.exports = function(Chart) { } else { cursor = { x: me.left + labelOpts.padding, - y: me.top, + y: me.top + labelOpts.padding, line: 0 }; } @@ -356,13 +382,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); } @@ -374,7 +402,6 @@ module.exports = function(Chart) { } } - drawLegendBox(x, y, legendItem); hitboxes[i].left = x; diff --git a/src/elements/element.point.js b/src/elements/element.point.js index 3290041b2..4131f8ced 100644 --- a/src/elements/element.point.js +++ b/src/elements/element.point.js @@ -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); } }); }; diff --git a/test/core.legend.tests.js b/test/core.legend.tests.js index fa6e8e76f..2db970f3f 100644 --- a/test/core.legend.tests.js +++ b/test/core.legend.tests.js @@ -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 }]); }); diff --git a/test/element.point.tests.js b/test/element.point.tests.js index 674682660..c257f7375 100644 --- a/test/element.point.tests.js +++ b/test/element.point.tests.js @@ -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]