]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Added usePointStyle option to label boxes
authorShayne Linhart <shayne.linhart@gmail.com>
Wed, 6 Jul 2016 02:08:29 +0000 (20:08 -0600)
committerTanner Linsley <tannerlinsley@gmail.com>
Wed, 6 Jul 2016 02:08:29 +0000 (20:08 -0600)
- Closes #2252
- Allows label boxes to match the shape(pointStyle) of the corresponding data.
* Removed unused varaible from legend's draw()

docs/01-Chart-Configuration.md
src/chart.js
src/core/core.canvasHelpers.js [new file with mode: 0644]
src/core/core.legend.js
src/elements/element.point.js
test/core.legend.tests.js
test/element.point.tests.js

index 75950eadad4ec23cdc29facdc8e06b0e9aad4de2..9c9f3659d0658a375bd730b70cd90806cd15b75d 100644 (file)
@@ -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
 }
 ```
 
index 75bd47538416d3e9336a1b4ec48de02ac0eea19d..a12890aa06b96f1a79d7df094359f8a6694375af 100644 (file)
@@ -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 (file)
index 0000000..f8c35c9
--- /dev/null
@@ -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
index 4747bbad5efad3476b4ce71fc6c0e048756b98ec..a13151bd332dc35a01dfa0ab7d52e6a327bc6f92 100644 (file)
@@ -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);
                                                }
index 3290041b2b9abe390b1b2789b7cb24323ecfd52e..4131f8ceded7c2bc12dfd76ae116aaf92d9d42b4 100644 (file)
@@ -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);
                }
        });
 };
index fa6e8e76f1dda10343a9f61b719bb208ddd3ea28..2db970f3f0f9658172c64a41f625fe1846425c4c 100644 (file)
@@ -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
                }]);
        });
index 674682660caefcf0a4e6eae4b97068f52a59cb2b..c257f7375ca5d7fd05a638ec151f3c6defcacf25 100644 (file)
@@ -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]