Add support for 'inner' border for arc elements (#5841)

This commit is contained in:
Akihiko Kusanagi 2018-12-18 21:58:02 +09:00 committed by Simon Brunel
parent 4fb259e3ac
commit db8f6c38dc
21 changed files with 511 additions and 68 deletions

View File

@ -55,12 +55,21 @@ The doughnut/pie chart allows a number of properties to be specified for each da
| Name | Type | Description | Name | Type | Description
| ---- | ---- | ----------- | ---- | ---- | -----------
| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). | `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). | `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
| `borderWidth` | `Number[]` | The border width of the arcs in the dataset. | `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset.
| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered. | `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment)
| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered. | `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered.
| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered. | `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered.
| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered.
### Border Alignment
The following values are supported for `borderAlign`.
* `'center'` (default)
* `'inner'`
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.
## Config Options ## Config Options

View File

@ -46,12 +46,21 @@ The following options can be included in a polar area chart dataset to configure
| Name | Type | Description | Name | Type | Description
| ---- | ---- | ----------- | ---- | ---- | -----------
| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). | `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). | `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors).
| `borderWidth` | `Number[]` | The border width of the arcs in the dataset. | `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset.
| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered. | `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment)
| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered. | `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered.
| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered. | `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered.
| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered.
### Border Alignment
The following values are supported for `borderAlign`.
* `'center'` (default)
* `'inner'`
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.
## Config Options ## Config Options

View File

@ -143,14 +143,15 @@ module.exports = DatasetController.extend({
var chart = me.chart; var chart = me.chart;
var chartArea = chart.chartArea; var chartArea = chart.chartArea;
var opts = chart.options; var opts = chart.options;
var arcOpts = opts.elements.arc; var availableWidth = chartArea.right - chartArea.left;
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; var availableHeight = chartArea.bottom - chartArea.top;
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
var minSize = Math.min(availableWidth, availableHeight); var minSize = Math.min(availableWidth, availableHeight);
var offset = {x: 0, y: 0}; var offset = {x: 0, y: 0};
var meta = me.getMeta(); var meta = me.getMeta();
var arcs = meta.data;
var cutoutPercentage = opts.cutoutPercentage; var cutoutPercentage = opts.cutoutPercentage;
var circumference = opts.circumference; var circumference = opts.circumference;
var i, ilen;
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
if (circumference < Math.PI * 2.0) { if (circumference < Math.PI * 2.0) {
@ -171,7 +172,11 @@ module.exports = DatasetController.extend({
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
} }
chart.borderWidth = me.getMaxBorderWidth(meta.data); for (i = 0, ilen = arcs.length; i < ilen; ++i) {
arcs[i]._options = me._resolveElementOptions(arcs[i], i, reset);
}
chart.borderWidth = me.getMaxBorderWidth();
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
@ -183,9 +188,9 @@ module.exports = DatasetController.extend({
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
helpers.each(meta.data, function(arc, index) { for (i = 0, ilen = arcs.length; i < ilen; ++i) {
me.updateElement(arc, index, reset); me.updateElement(arcs[i], i, reset);
}); }
}, },
updateElement: function(arc, index, reset) { updateElement: function(arc, index, reset) {
@ -202,7 +207,7 @@ module.exports = DatasetController.extend({
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; var options = arc._options || {};
helpers.extend(arc, { helpers.extend(arc, {
// Utility // Utility
@ -211,6 +216,10 @@ module.exports = DatasetController.extend({
// Desired view properties // Desired view properties
_model: { _model: {
backgroundColor: options.backgroundColor,
borderColor: options.borderColor,
borderWidth: options.borderWidth,
borderAlign: options.borderAlign,
x: centerX + chart.offsetX, x: centerX + chart.offsetX,
y: centerY + chart.offsetY, y: centerY + chart.offsetY,
startAngle: startAngle, startAngle: startAngle,
@ -218,20 +227,12 @@ module.exports = DatasetController.extend({
circumference: circumference, circumference: circumference,
outerRadius: outerRadius, outerRadius: outerRadius,
innerRadius: innerRadius, innerRadius: innerRadius,
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) label: helpers.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
} }
}); });
var model = arc._model; var model = arc._model;
// Resets the visual styles
var custom = arc.custom || {};
var valueOrDefault = helpers.valueAtIndexOrDefault;
var elementOpts = this.chart.options.elements.arc;
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
// Set correct angles if not resetting // Set correct angles if not resetting
if (!reset || !animationOpts.animateRotate) { if (!reset || !animationOpts.animateRotate) {
if (index === 0) { if (index === 0) {
@ -276,19 +277,58 @@ module.exports = DatasetController.extend({
// gets the max border or hover width to properly scale pie charts // gets the max border or hover width to properly scale pie charts
getMaxBorderWidth: function(arcs) { getMaxBorderWidth: function(arcs) {
var me = this;
var max = 0; var max = 0;
var index = this.index; var chart = me.chart;
var length = arcs.length; var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
var borderWidth;
var hoverWidth;
for (var i = 0; i < length; i++) { if (!arcs) {
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; // Find the outmost visible dataset
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
if (chart.isDatasetVisible(i)) {
meta = chart.getDatasetMeta(i);
arcs = meta.data;
if (i !== me.index) {
controller = meta.controller;
}
break;
}
}
}
max = borderWidth > max ? borderWidth : max; if (!arcs) {
max = hoverWidth > max ? hoverWidth : max; return 0;
}
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
arc = arcs[i];
options = controller ? controller._resolveElementOptions(arc, i) : arc._options;
if (options.borderAlign !== 'inner') {
borderWidth = options.borderWidth;
hoverWidth = options.hoverBorderWidth;
max = borderWidth > max ? borderWidth : max;
max = hoverWidth > max ? hoverWidth : max;
}
} }
return max; return max;
},
/**
* @private
*/
_resolveElementOptions: function(arc, index) {
var me = this;
var dataset = me.getDataset();
var custom = arc.custom || {};
var options = me.chart.options.elements.arc;
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
return {
backgroundColor: custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(dataset.backgroundColor, index, options.backgroundColor),
borderColor: custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(dataset.borderColor, index, options.borderColor),
borderWidth: custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(dataset.borderWidth, index, options.borderWidth),
borderAlign: custom.borderAlign ? custom.borderAlign : valueAtIndexOrDefault(dataset.borderAlign, index, options.borderAlign)
};
} }
}); });

View File

@ -148,10 +148,9 @@ module.exports = DatasetController.extend({
var chart = me.chart; var chart = me.chart;
var chartArea = chart.chartArea; var chartArea = chart.chartArea;
var opts = chart.options; var opts = chart.options;
var arcOpts = opts.elements.arc;
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); chart.outerRadius = Math.max(minSize / 2, 0);
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
@ -206,6 +205,7 @@ module.exports = DatasetController.extend({
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
model.borderAlign = custom.borderAlign ? custom.borderAlign : valueOrDefault(dataset.borderAlign, index, elementOpts.borderAlign);
arc.pivot(); arc.pivot();
}, },

View File

@ -9,7 +9,8 @@ defaults._set('global', {
arc: { arc: {
backgroundColor: defaults.global.defaultColor, backgroundColor: defaults.global.defaultColor,
borderColor: '#fff', borderColor: '#fff',
borderWidth: 2 borderWidth: 2,
borderAlign: 'center'
} }
} }
}); });
@ -85,23 +86,51 @@ module.exports = Element.extend({
var vm = this._view; var vm = this._view;
var sA = vm.startAngle; var sA = vm.startAngle;
var eA = vm.endAngle; var eA = vm.endAngle;
var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
var angleMargin;
ctx.save();
ctx.beginPath(); ctx.beginPath();
ctx.arc(vm.x, vm.y, vm.outerRadius - pixelMargin, sA, eA);
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
ctx.closePath(); ctx.closePath();
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.fillStyle = vm.backgroundColor; ctx.fillStyle = vm.backgroundColor;
ctx.fill(); ctx.fill();
ctx.lineJoin = 'bevel';
if (vm.borderWidth) { if (vm.borderWidth) {
if (vm.borderAlign === 'inner') {
// Draw an inner border by cliping the arc and drawing a double-width border
// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
ctx.beginPath();
angleMargin = pixelMargin / vm.outerRadius;
ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin);
if (vm.innerRadius > pixelMargin) {
angleMargin = pixelMargin / vm.innerRadius;
ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true);
} else {
ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2);
}
ctx.closePath();
ctx.clip();
ctx.beginPath();
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
ctx.closePath();
ctx.lineWidth = vm.borderWidth * 2;
ctx.lineJoin = 'round';
} else {
ctx.lineWidth = vm.borderWidth;
ctx.lineJoin = 'bevel';
}
ctx.strokeStyle = vm.borderColor;
ctx.stroke(); ctx.stroke();
} }
ctx.restore();
} }
}); });

View File

@ -79,6 +79,7 @@ Context.prototype._initMethods = function() {
beginPath: function() {}, beginPath: function() {},
bezierCurveTo: function() {}, bezierCurveTo: function() {},
clearRect: function() {}, clearRect: function() {},
clip: function() {},
closePath: function() {}, closePath: function() {},
fill: function() {}, fill: function() {},
fillRect: function() {}, fillRect: function() {},

View File

@ -0,0 +1,31 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [1, 5, 10, 50, 100],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
]
}]
},
"options": {
"responsive": false,
"legend": false,
"title": false
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,32 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [1, 5, 10, 50, 100],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
],
"borderAlign": "inner"
}]
},
"options": {
"responsive": false,
"legend": false,
"title": false
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,31 @@
{
"config": {
"type": "pie",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [1, 5, 10, 50, 100],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
]
}]
},
"options": {
"responsive": false,
"legend": false,
"title": false
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,32 @@
{
"config": {
"type": "pie",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [1, 5, 10, 50, 100],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
],
"borderAlign": "inner"
}]
},
"options": {
"responsive": false,
"legend": false,
"title": false
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,41 @@
{
"config": {
"type": "polarArea",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [11, 16, 21, 1, 10],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
]
}]
},
"options": {
"elements": {
"arc": {
"angle": [
0.0378, 0.1892, 0.3786, 1.8925, 3.7849
]
}
},
"responsive": false,
"legend": false,
"title": false,
"scale": {
"display": false
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,42 @@
{
"config": {
"type": "polarArea",
"data": {
"labels": ["A", "B", "C", "D", "E"],
"datasets": [{
"data": [11, 16, 21, 1, 10],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)"
],
"borderWidth": 20,
"borderColor": [
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(255, 206, 86)",
"rgb(75, 192, 192)",
"rgb(153, 102, 255)"
],
"borderAlign": "inner"
}]
},
"options": {
"elements": {
"arc": {
"angle": [
0.0378, 0.1892, 0.3786, 1.8925, 3.7849
]
}
},
"responsive": false,
"legend": false,
"title": false,
"scale": {
"display": false
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -1,4 +1,6 @@
describe('Chart.controllers.doughnut', function() { describe('Chart.controllers.doughnut', function() {
describe('auto', jasmine.fixture.specs('controller.doughnut'));
it('should be registered as dataset controller', function() { it('should be registered as dataset controller', function() {
expect(typeof Chart.controllers.doughnut).toBe('function'); expect(typeof Chart.controllers.doughnut).toBe('function');
expect(Chart.controllers.doughnut).toBe(Chart.controllers.pie); expect(Chart.controllers.doughnut).toBe(Chart.controllers.pie);
@ -106,8 +108,8 @@ describe('Chart.controllers.doughnut', function() {
].forEach(function(expected, i) { ].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(256); expect(meta.data[i]._model.x).toBeCloseToPixel(256);
expect(meta.data[i]._model.y).toBeCloseToPixel(256); expect(meta.data[i]._model.y).toBeCloseToPixel(256);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(254); expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(190); expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({ expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
startAngle: Math.PI * -0.5, startAngle: Math.PI * -0.5,
@ -129,8 +131,8 @@ describe('Chart.controllers.doughnut', function() {
].forEach(function(expected, i) { ].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(256); expect(meta.data[i]._model.x).toBeCloseToPixel(256);
expect(meta.data[i]._model.y).toBeCloseToPixel(256); expect(meta.data[i]._model.y).toBeCloseToPixel(256);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(254); expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(190); expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8); expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8); expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
@ -200,10 +202,10 @@ describe('Chart.controllers.doughnut', function() {
{c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8}, {c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8},
{c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2} {c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2}
].forEach(function(expected, i) { ].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(511); expect(meta.data[i]._model.x).toBeCloseToPixel(512);
expect(meta.data[i]._model.y).toBeCloseToPixel(511); expect(meta.data[i]._model.y).toBeCloseToPixel(512);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(510); expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(512);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(382); expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(384);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8); expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8); expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
@ -405,4 +407,52 @@ describe('Chart.controllers.doughnut', function() {
expect(arc._model.borderColor).toBe('rgb(17, 17, 17)'); expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
expect(arc._model.borderWidth).toBe(3.14159); expect(arc._model.borderWidth).toBe(3.14159);
}); });
it ('should calculate radiuses based on the border widths of the visible outermost dataset', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [2, 4],
borderWidth: 4,
hidden: true
}, {
data: [1, 3],
borderWidth: 8
}, {
data: [1, 0],
borderWidth: 12
}],
labels: ['label0', 'label1']
},
options: {
legend: false,
title: false
}
});
chart.update();
expect(chart.chartArea.bottom - chart.chartArea.top).toBe(512);
expect(chart.borderWidth).toBe(8);
expect(chart.outerRadius).toBe(252);
expect(chart.innerRadius).toBe(126);
expect(chart.radiusLength).toBe(63);
var controller = chart.getDatasetMeta(0).controller;
expect(controller.getMaxBorderWidth()).toBe(8);
expect(controller.outerRadius).toBe(252);
expect(controller.innerRadius).toBe(189);
controller = chart.getDatasetMeta(1).controller;
expect(controller.getMaxBorderWidth()).toBe(8);
expect(controller.outerRadius).toBe(252);
expect(controller.innerRadius).toBe(189);
controller = chart.getDatasetMeta(2).controller;
expect(controller.getMaxBorderWidth()).toBe(8);
expect(controller.outerRadius).toBe(189);
expect(controller.innerRadius).toBe(126);
});
}); });

View File

@ -943,7 +943,7 @@ describe('Core.Tooltip', function() {
if (model.width <= chart.width) { if (model.width <= chart.width) {
expect(model.x + model.width).toBeLessThanOrEqual(chart.width); expect(model.x + model.width).toBeLessThanOrEqual(chart.width);
} }
expect(model.caretX).toBe(tooltipPosition.x); expect(model.caretX).toBeCloseToPixel(tooltipPosition.x);
// if tooltip is longer than chart area then all tests done // if tooltip is longer than chart area then all tests done
if (model.width > chart.width) { if (model.width > chart.width) {
break; break;

View File

@ -126,6 +126,9 @@ describe('Arc element tests', function() {
arc.draw(); arc.draw();
expect(mockContext.getCalls()).toEqual([{ expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: []
}, {
name: 'beginPath', name: 'beginPath',
args: [] args: []
}, { }, {
@ -137,12 +140,6 @@ describe('Arc element tests', function() {
}, { }, {
name: 'closePath', name: 'closePath',
args: [] args: []
}, {
name: 'setStrokeStyle',
args: ['rgb(255, 0, 0)']
}, {
name: 'setLineWidth',
args: [undefined]
}, { }, {
name: 'setFillStyle', name: 'setFillStyle',
args: ['rgb(0, 0, 255)'] args: ['rgb(0, 0, 255)']
@ -150,8 +147,8 @@ describe('Arc element tests', function() {
name: 'fill', name: 'fill',
args: [] args: []
}, { }, {
name: 'setLineJoin', name: 'restore',
args: ['bevel'] args: []
}]); }]);
}); });
@ -182,6 +179,9 @@ describe('Arc element tests', function() {
arc.draw(); arc.draw();
expect(mockContext.getCalls()).toEqual([{ expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: []
}, {
name: 'beginPath', name: 'beginPath',
args: [] args: []
}, { }, {
@ -194,23 +194,119 @@ describe('Arc element tests', function() {
name: 'closePath', name: 'closePath',
args: [] args: []
}, { }, {
name: 'setStrokeStyle', name: 'setFillStyle',
args: ['rgb(255, 0, 0)'] args: ['rgb(0, 0, 255)']
}, {
name: 'fill',
args: []
}, { }, {
name: 'setLineWidth', name: 'setLineWidth',
args: [5] args: [5]
}, {
name: 'setLineJoin',
args: ['bevel']
}, {
name: 'setStrokeStyle',
args: ['rgb(255, 0, 0)']
}, {
name: 'stroke',
args: []
}, {
name: 'restore',
args: []
}]);
});
it ('should draw correctly with an inner border', function() {
var mockContext = window.createMockContext();
var arc = new Chart.elements.Arc({
_datasetIndex: 2,
_index: 1,
_chart: {
ctx: mockContext,
}
});
// Mock out the view as if the controller put it there
arc._view = {
startAngle: 0,
endAngle: Math.PI / 2,
x: 10,
y: 5,
innerRadius: 1,
outerRadius: 3,
backgroundColor: 'rgb(0, 0, 255)',
borderColor: 'rgb(255, 0, 0)',
borderWidth: 5,
borderAlign: 'inner'
};
arc.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: []
}, {
name: 'beginPath',
args: []
}, {
name: 'arc',
args: [10, 5, 2.67, 0, Math.PI / 2]
}, {
name: 'arc',
args: [10, 5, 1, Math.PI / 2, 0, true]
}, {
name: 'closePath',
args: []
}, { }, {
name: 'setFillStyle', name: 'setFillStyle',
args: ['rgb(0, 0, 255)'] args: ['rgb(0, 0, 255)']
}, { }, {
name: 'fill', name: 'fill',
args: [] args: []
}, {
name: 'beginPath',
args: []
}, {
name: 'arc',
args: [10, 5, 3, -0.11, Math.PI / 2 + 0.11]
}, {
name: 'arc',
args: [10, 5, 1 - 0.33, Math.PI / 2 + 0.33, -0.33, true]
}, {
name: 'closePath',
args: []
}, {
name: 'clip',
args: []
}, {
name: 'beginPath',
args: []
}, {
name: 'arc',
args: [10, 5, 3, 0, Math.PI / 2]
}, {
name: 'arc',
args: [10, 5, 1, Math.PI / 2, 0, true]
}, {
name: 'closePath',
args: []
}, {
name: 'setLineWidth',
args: [10]
}, { }, {
name: 'setLineJoin', name: 'setLineJoin',
args: ['bevel'] args: ['round']
}, {
name: 'setStrokeStyle',
args: ['rgb(255, 0, 0)']
}, { }, {
name: 'stroke', name: 'stroke',
args: [] args: []
}, {
name: 'restore',
args: []
}]); }]);
}); });
}); });