Skip to content

Commit 56e5c62

Browse files
committed
Draw inner border for arc elements
1 parent 3cb2d70 commit 56e5c62

23 files changed

+481
-55
lines changed

docs/charts/doughnut.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,21 @@ The doughnut/pie chart allows a number of properties to be specified for each da
5555

5656
| Name | Type | Description
5757
| ---- | ---- | -----------
58-
| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
59-
| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
60-
| `borderWidth` | `Number[]` | The border width of the arcs in the dataset.
61-
| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered.
62-
| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered.
63-
| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered.
58+
| `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
59+
| `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
60+
| `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset.
61+
| `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment)
62+
| `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered.
63+
| `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered.
64+
| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered.
65+
66+
### Border Alignment
67+
68+
The following values are supported for `borderAlign`.
69+
* `'center'` (default)
70+
* `'inner'`
71+
72+
When `'center'` is set, the borders of arcs next to each other will overlap. When `'inner'` is set, it is guaranteed that all the borders are not overlap.
6473

6574
## Config Options
6675

docs/charts/polar.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,21 @@ The following options can be included in a polar area chart dataset to configure
4646

4747
| Name | Type | Description
4848
| ---- | ---- | -----------
49-
| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
50-
| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
51-
| `borderWidth` | `Number[]` | The border width of the arcs in the dataset.
52-
| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered.
53-
| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered.
54-
| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered.
49+
| `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
50+
| `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
51+
| `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset.
52+
| `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment)
53+
| `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered.
54+
| `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered.
55+
| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered.
56+
57+
### Border Alignment
58+
59+
The following values are supported for `borderAlign`.
60+
* `'center'` (default)
61+
* `'inner'`
62+
63+
When `'center'` is set, the borders of arcs next to each other will overlap. When `'inner'` is set, it is guaranteed that all the borders are not overlap.
5564

5665
## Config Options
5766

src/controllers/controller.doughnut.js

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,8 @@ module.exports = DatasetController.extend({
143143
var chart = me.chart;
144144
var chartArea = chart.chartArea;
145145
var opts = chart.options;
146-
var arcOpts = opts.elements.arc;
147-
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
148-
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
146+
var availableWidth = chartArea.right - chartArea.left;
147+
var availableHeight = chartArea.bottom - chartArea.top;
149148
var minSize = Math.min(availableWidth, availableHeight);
150149
var offset = {x: 0, y: 0};
151150
var meta = me.getMeta();
@@ -171,7 +170,7 @@ module.exports = DatasetController.extend({
171170
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
172171
}
173172

174-
chart.borderWidth = me.getMaxBorderWidth(meta.data);
173+
chart.borderWidth = me.getMaxBorderWidth();
175174
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
176175
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
177176
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
@@ -231,6 +230,7 @@ module.exports = DatasetController.extend({
231230
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
232231
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
233232
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
233+
model.borderAlign = custom.borderAlign ? custom.borderAlign : valueOrDefault(dataset.borderAlign, index, elementOpts.borderAlign);
234234

235235
// Set correct angles if not resetting
236236
if (!reset || !animationOpts.animateRotate) {
@@ -276,18 +276,41 @@ module.exports = DatasetController.extend({
276276

277277
// gets the max border or hover width to properly scale pie charts
278278
getMaxBorderWidth: function(arcs) {
279+
var me = this;
279280
var max = 0;
280-
var index = this.index;
281-
var length = arcs.length;
282-
var borderWidth;
283-
var hoverWidth;
281+
var index = me.index;
282+
var chart = me.chart;
283+
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
284+
var elementOpts = chart.options.elements.arc;
285+
var i, ilen, dataset, custom, borderAlign, borderWidth, hoverWidth;
286+
287+
if (!arcs) {
288+
// Find the outmost visible dataset
289+
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
290+
if (chart.isDatasetVisible(i)) {
291+
arcs = chart.getDatasetMeta(i).data;
292+
index = i;
293+
break;
294+
}
295+
}
296+
}
297+
298+
if (!arcs) {
299+
return 0;
300+
}
284301

285-
for (var i = 0; i < length; i++) {
286-
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
287-
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
302+
dataset = chart.data.datasets[index];
288303

289-
max = borderWidth > max ? borderWidth : max;
290-
max = hoverWidth > max ? hoverWidth : max;
304+
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
305+
custom = arcs[i].custom || {};
306+
borderAlign = custom.borderAlign ? custom.borderAlign : valueAtIndexOrDefault(dataset.borderAlign, i, elementOpts.borderAlign);
307+
if (borderAlign !== 'inner') {
308+
borderWidth = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(dataset.borderWidth, i, elementOpts.borderWidth);
309+
hoverWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueAtIndexOrDefault(dataset.hoverBorderWidth, i, borderWidth);
310+
311+
max = borderWidth > max ? borderWidth : max;
312+
max = hoverWidth > max ? hoverWidth : max;
313+
}
291314
}
292315
return max;
293316
}

src/controllers/controller.polarArea.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,9 @@ module.exports = DatasetController.extend({
148148
var chart = me.chart;
149149
var chartArea = chart.chartArea;
150150
var opts = chart.options;
151-
var arcOpts = opts.elements.arc;
152151
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
153152

154-
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
153+
chart.outerRadius = Math.max(minSize / 2, 0);
155154
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
156155
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
157156

@@ -206,6 +205,7 @@ module.exports = DatasetController.extend({
206205
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
207206
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
208207
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
208+
model.borderAlign = custom.borderAlign ? custom.borderAlign : valueOrDefault(dataset.borderAlign, index, elementOpts.borderAlign);
209209

210210
arc.pivot();
211211
},

src/elements/element.arc.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ defaults._set('global', {
99
arc: {
1010
backgroundColor: defaults.global.defaultColor,
1111
borderColor: '#fff',
12-
borderWidth: 2
12+
borderWidth: 2,
13+
borderAlign: 'center'
1314
}
1415
}
1516
});
@@ -85,23 +86,51 @@ module.exports = Element.extend({
8586
var vm = this._view;
8687
var sA = vm.startAngle;
8788
var eA = vm.endAngle;
89+
var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
90+
var angleMargin;
8891

89-
ctx.beginPath();
92+
ctx.save();
9093

91-
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
94+
ctx.beginPath();
95+
ctx.arc(vm.x, vm.y, vm.outerRadius - pixelMargin, sA, eA);
9296
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
93-
9497
ctx.closePath();
95-
ctx.strokeStyle = vm.borderColor;
96-
ctx.lineWidth = vm.borderWidth;
9798

9899
ctx.fillStyle = vm.backgroundColor;
99-
100100
ctx.fill();
101-
ctx.lineJoin = 'bevel';
102101

103102
if (vm.borderWidth) {
103+
if (vm.borderAlign === 'inner') {
104+
// Draw an inner border by cliping the arc and drawing a double-width border
105+
// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
106+
ctx.beginPath();
107+
angleMargin = pixelMargin / vm.outerRadius;
108+
ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin);
109+
if (vm.innerRadius > pixelMargin) {
110+
angleMargin = pixelMargin / vm.innerRadius;
111+
ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true);
112+
} else {
113+
ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2);
114+
}
115+
ctx.closePath();
116+
ctx.clip();
117+
118+
ctx.beginPath();
119+
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
120+
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
121+
ctx.closePath();
122+
123+
ctx.lineWidth = vm.borderWidth * 2;
124+
ctx.lineJoin = 'round';
125+
} else {
126+
ctx.lineWidth = vm.borderWidth;
127+
ctx.lineJoin = 'bevel';
128+
}
129+
130+
ctx.strokeStyle = vm.borderColor;
104131
ctx.stroke();
105132
}
133+
134+
ctx.restore();
106135
}
107136
});

test/context.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Context.prototype._initMethods = function() {
7979
beginPath: function() {},
8080
bezierCurveTo: function() {},
8181
clearRect: function() {},
82+
clip: function() {},
8283
closePath: function() {},
8384
fill: function() {},
8485
fillRect: function() {},
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"config": {
3+
"type": "doughnut",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
]
23+
}]
24+
},
25+
"options": {
26+
"responsive": false,
27+
"legend": false,
28+
"title": false
29+
}
30+
}
31+
}
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"config": {
3+
"type": "doughnut",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
],
23+
"borderAlign": "inner"
24+
}]
25+
},
26+
"options": {
27+
"responsive": false,
28+
"legend": false,
29+
"title": false
30+
}
31+
}
32+
}
Loading
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"config": {
3+
"type": "pie",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
]
23+
}]
24+
},
25+
"options": {
26+
"responsive": false,
27+
"legend": false,
28+
"title": false
29+
}
30+
}
31+
}
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"config": {
3+
"type": "pie",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
],
23+
"borderAlign": "inner"
24+
}]
25+
},
26+
"options": {
27+
"responsive": false,
28+
"legend": false,
29+
"title": false
30+
}
31+
}
32+
}
Loading
Loading
Loading

0 commit comments

Comments
 (0)