*line*.fill | Boolean | true |
point | - | - | -
*point*.radius | Number | 3 | Default point radius
+*point*.pointStyle | String | 'circle' | Default point style
*point*.backgroundColor | Color | `Chart.defaults.global.defaultColor` | Default point fill color
*point*.borderWidth | Number | 1 | Default point stroke width
*point*.borderColor | Color | `Chart.defaults.global.defaultColor` | Default point stroke color
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension),
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
+ pointStyle: point.custom && point.custom.pointStyle ? point.custom.pointStyle : helpers.getValueAtIndexOrDefault(this.getDataset().pointStyle, index, this.chart.options.elements.point.pointStyle),
backgroundColor: this.getPointBackgroundColor(point, index),
borderColor: this.getPointBorderColor(point, index),
borderWidth: this.getPointBorderWidth(point, index),
Chart.defaults.global.elements.point = {
radius: 3,
+ pointStyle: 'circle',
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
if (vm.radius > 0 || vm.borderWidth > 0) {
- ctx.beginPath();
-
- ctx.arc(vm.x, vm.y, vm.radius || Chart.defaults.global.elements.point.radius, 0, Math.PI * 2);
- ctx.closePath();
-
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.point.borderWidth;
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
- ctx.fill();
+ var radius = vm.radius || Chart.defaults.global.elements.point.radius;
+
+ switch (vm.pointStyle) {
+ case 'circle':
+ default:
+ ctx.beginPath();
+ ctx.arc(vm.x, vm.y, radius, 0, Math.PI * 2);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case 'triangle':
+ ctx.beginPath();
+ var edgeLength = 3 * radius / Math.sqrt(3);
+ var height = edgeLength * Math.sqrt(3) / 2;
+ ctx.moveTo(vm.x - edgeLength / 2, vm.y + height / 3);
+ ctx.lineTo(vm.x + edgeLength / 2, vm.y + height / 3);
+ ctx.lineTo(vm.x, vm.y - 2 * height / 3);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case 'rect':
+ ctx.fillRect(vm.x - radius, vm.y - radius, 2 * radius, 2 * radius);
+ ctx.strokeRect(vm.x - radius, vm.y - radius, 2 * radius, 2 * radius);
+ break;
+ case 'rectRot':
+ ctx.translate(vm.x, vm.y);
+ ctx.rotate(Math.PI / 4);
+ ctx.fillRect(-radius, -radius, 2 * radius, 2 * radius);
+ ctx.strokeRect(-radius, -radius, 2 * radius, 2 * radius);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ break;
+ case 'cross':
+ ctx.beginPath();
+ ctx.moveTo(vm.x, vm.y + radius);
+ ctx.lineTo(vm.x, vm.y - radius);
+ ctx.moveTo(vm.x - radius, vm.y);
+ ctx.lineTo(vm.x + radius, vm.y);
+ ctx.closePath();
+ break;
+ case 'crossRot':
+ ctx.beginPath();
+ var xOffset = Math.cos(Math.PI / 4) * radius;
+ var yOffset = Math.sin(Math.PI / 4) * radius;
+ ctx.moveTo(vm.x - xOffset, vm.y - yOffset);
+ ctx.lineTo(vm.x + xOffset, vm.y + yOffset);
+ ctx.moveTo(vm.x - xOffset, vm.y + yOffset);
+ ctx.lineTo(vm.x + xOffset, vm.y - yOffset);
+ ctx.closePath();
+ break;
+ case 'star':
+ ctx.beginPath();
+ ctx.moveTo(vm.x, vm.y + radius);
+ ctx.lineTo(vm.x, vm.y - radius);
+ ctx.moveTo(vm.x - radius, vm.y);
+ ctx.lineTo(vm.x + radius, vm.y);
+ var xOffset = Math.cos(Math.PI / 4) * radius;
+ var yOffset = Math.sin(Math.PI / 4) * radius;
+ ctx.moveTo(vm.x - xOffset, vm.y - yOffset);
+ ctx.lineTo(vm.x + xOffset, vm.y + yOffset);
+ ctx.moveTo(vm.x - xOffset, vm.y + yOffset);
+ ctx.lineTo(vm.x + xOffset, vm.y - yOffset);
+ ctx.closePath();
+ break;
+ case 'line':
+ ctx.beginPath();
+ ctx.moveTo(vm.x - radius, vm.y);
+ ctx.lineTo(vm.x + radius, vm.y);
+ ctx.closePath();
+ break;
+ case 'dash':
+ ctx.beginPath();
+ ctx.moveTo(vm.x, vm.y);
+ ctx.lineTo(vm.x + radius, vm.y);
+ ctx.closePath();
+ break;
+ }
+
ctx.stroke();
}
}
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
+ pointStyle: 'circle'
}
},
scales: {
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
+ pointStyle: 'circle',
skip: false,
tension: 0.1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
+ pointStyle: 'circle',
skip: false,
tension: 0.1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
+ pointStyle: 'circle',
skip: false,
tension: 0.1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
+ pointStyle: 'circle',
skip: false,
tension: 0.1,
borderColor: 'rgb(56, 57, 58)',
hitRadius: 3.3,
radius: 22,
+ pointStyle: 'circle',
skip: false,
tension: 0,
borderColor: 'rgb(56, 57, 58)',
hitRadius: 3.3,
radius: 22,
+ pointStyle: 'circle',
skip: false,
tension: 0,
borderColor: 'rgb(56, 57, 58)',
hitRadius: 3.3,
radius: 22,
+ pointStyle: 'circle',
skip: false,
tension: 0,
borderColor: 'rgb(56, 57, 58)',
hitRadius: 3.3,
radius: 22,
+ pointStyle: 'circle',
skip: false,
tension: 0,
borderColor: 'rgb(4, 6, 8)',
hitRadius: 5,
radius: 2.2,
+ pointStyle: 'circle',
skip: true,
tension: 0.15,
// Attach a view object as if we were the controller
point._view = {
radius: 2,
+ pointStyle: 'circle',
hitRadius: 3,
borderColor: 'rgba(1, 2, 3, 1)',
borderWidth: 6,
point.draw();
expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
name: 'beginPath',
args: []
}, {
name: 'closePath',
args: [],
}, {
+ name: 'fill',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'triangle';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [10 - 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3]
+ }, {
+ name: 'lineTo',
+ args: [10 + 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
+ }, {
+ name: 'lineTo',
+ args: [10, 15 - 2 * 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
+ }, {
+ name: 'closePath',
+ args: [],
}, {
name: 'fill',
args: [],
name: 'stroke',
args: []
}]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'rect';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'fillRect',
+ args: [8, 13, 4, 4]
+ }, {
+ name: 'strokeRect',
+ args: [8, 13, 4, 4]
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'rectRot';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'translate',
+ args: [10, 15]
+ }, {
+ name: 'rotate',
+ args: [Math.PI / 4]
+ }, {
+ name: 'fillRect',
+ args: [-2, -2, 4, 4],
+ }, {
+ name: 'strokeRect',
+ args: [-2, -2, 4, 4],
+ }, {
+ name: 'setTransform',
+ args: [1, 0, 0, 1, 0, 0],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'cross';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [10, 17]
+ }, {
+ name: 'lineTo',
+ args: [10, 13],
+ }, {
+ name: 'moveTo',
+ args: [8, 15],
+ }, {
+ name: 'lineTo',
+ args: [12, 15],
+ },{
+ name: 'closePath',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'crossRot';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
+ }, {
+ name: 'lineTo',
+ args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'moveTo',
+ args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'lineTo',
+ args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'closePath',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'star';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [10, 17]
+ }, {
+ name: 'lineTo',
+ args: [10, 13],
+ }, {
+ name: 'moveTo',
+ args: [8, 15],
+ }, {
+ name: 'lineTo',
+ args: [12, 15],
+ },{
+ name: 'moveTo',
+ args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
+ }, {
+ name: 'lineTo',
+ args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'moveTo',
+ args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'lineTo',
+ args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
+ }, {
+ name: 'closePath',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'line';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [8, 15]
+ }, {
+ name: 'lineTo',
+ args: [12, 15],
+ }, {
+ name: 'closePath',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
+ mockContext.resetCalls();
+ point._view.pointStyle = 'dash';
+ point.draw();
+
+ expect(mockContext.getCalls()).toEqual([{
+ name: 'setStrokeStyle',
+ args: ['rgba(1, 2, 3, 1)']
+ }, {
+ name: 'setLineWidth',
+ args: [6]
+ }, {
+ name: 'setFillStyle',
+ args: ['rgba(0, 255, 0)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'moveTo',
+ args: [10, 15]
+ }, {
+ name: 'lineTo',
+ args: [12, 15],
+ }, {
+ name: 'closePath',
+ args: [],
+ }, {
+ name: 'stroke',
+ args: []
+ }]);
+
});
it ('should draw correctly with default settings if necessary', function() {
point.draw();
expect(mockContext.getCalls()).toEqual([{
- name: 'beginPath',
- args: []
- }, {
- name: 'arc',
- args: [10, 15, 2, 0, 2 * Math.PI]
- }, {
- name: 'closePath',
- args: [],
- }, {
name: 'setStrokeStyle',
args: ['rgba(0,0,0,0.1)']
}, {
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
+ }, {
+ name: 'beginPath',
+ args: []
+ }, {
+ name: 'arc',
+ args: [10, 15, 2, 0, 2 * Math.PI]
+ }, {
+ name: 'closePath',
+ args: [],
}, {
name: 'fill',
args: [],
save: function() {},
setLineDash: function() {},
stroke: function() {},
- translate: function() {},
+ strokeRect: function(x, y, w, h) {},
+ setTransform: function(a, b, c, d, e, f) {},
+ translate: function(x, y) {},
};
// attach methods to the class itself