Chart.js v2:甜甜圈内甜甜圈之间的空间

时间:2022-12-04 08:29:37

I am using chart.js v2.5.0. I put doughnut inside doughnut.

我正在使用chart.js v2.5.0。我把甜甜圈放在甜甜圈里面。

I want the disdance between 2 doughnuts(A) to be larger without affecting the distance between slices inside the same doughnut(B).

我希望2个甜甜圈(A)之间的差异更大,而不会影响同一个甜甜圈内的切片之间的距离(B)。

Please see the following image:

请看下图:

Chart.js v2:甜甜圈内甜甜圈之间的空间

Currently I am using the property borderWidth. However, this also affects the width of B.

目前我正在使用属性borderWidth。但是,这也会影响B的宽度。

Please see the following code:

请参阅以下代码:

options: {
                elements: {
                    arc: {
                        borderWidth: 18,
                    },
                },
                cutoutPercentage: 60,
                responsive: true,
            }

I want the doughnuts to look like this:

我希望甜甜圈看起来像这样:

Chart.js v2:甜甜圈内甜甜圈之间的空间

2 个解决方案

#1


2  

The only way to achieve this is to extend the existing doughnut controller and overwrite the update method with your own logic for determining the spacing.

实现此目的的唯一方法是扩展现有的甜甜圈控制器并使用您自己的逻辑覆盖更新方法以确定间距。

Here is an example demonstrating how you would do this. With this implementation, I added a new doughnut chart option property called datasetRadiusBuffer that controls the white space between each dataset.

这是一个演示如何执行此操作的示例。通过这个实现,我添加了一个名为datasetRadiusBuffer的新的圆环图选项属性,该属性控制每个数据集之间的空白区域。

var helpers = Chart.helpers;

// this option will control the white space between embedded charts when there is more than 1 dataset
helpers.extend(Chart.defaults.doughnut, {
  datasetRadiusBuffer: 0
});

Chart.controllers.doughnut = Chart.controllers.doughnut.extend({
  update: function(reset) {
    var me = this;
    var chart = me.chart,
        chartArea = chart.chartArea,
        opts = chart.options,
        arcOpts = opts.elements.arc,
        availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
        availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
        minSize = Math.min(availableWidth, availableHeight),
        offset = {
          x: 0,
          y: 0
        },
        meta = me.getMeta(),
        cutoutPercentage = opts.cutoutPercentage,
        circumference = opts.circumference;

    // 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) {
      var startAngle = opts.rotation % (Math.PI * 2.0);
      startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
      var endAngle = startAngle + circumference;
      var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
      var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
      var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
      var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
      var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
      var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
      var cutout = cutoutPercentage / 100.0;
      var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
      var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
      var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
      minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
      offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
    }

    chart.borderWidth = me.getMaxBorderWidth(meta.data);
    chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
    chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
    chart.radiusLength = ((chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount()) + 25;
    chart.offsetX = offset.x * chart.outerRadius;
    chart.offsetY = offset.y * chart.outerRadius;

    meta.total = me.calculateTotal();

    me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
    me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);

    // factor in the radius buffer if the chart has more than 1 dataset
    if (me.index > 0) {
      me.outerRadius -= opts.datasetRadiusBuffer;
      me.innerRadius -= opts.datasetRadiusBuffer;
    }

    helpers.each(meta.data, function(arc, index) {
      me.updateElement(arc, index, reset);
    });
  },
});

You can see a live example at this codepen.

您可以在此codepen中看到一个实时示例。

#2


0  

To make it work with the latest ChartJS 2.7.2, I've just copied the source as suggested from https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.doughnut.js. Then I added the patch:

为了使它能够使用最新的ChartJS 2.7.2,我刚刚从https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.doughnut.js建议复制了源代码。然后我添加了补丁:

if (me.index > 0) {
  me.outerRadius -= opts.datasetRadiusBuffer;
  me.innerRadius -= opts.datasetRadiusBuffer;
}

Everything was working as expected.

一切都按预期工作。

#1


2  

The only way to achieve this is to extend the existing doughnut controller and overwrite the update method with your own logic for determining the spacing.

实现此目的的唯一方法是扩展现有的甜甜圈控制器并使用您自己的逻辑覆盖更新方法以确定间距。

Here is an example demonstrating how you would do this. With this implementation, I added a new doughnut chart option property called datasetRadiusBuffer that controls the white space between each dataset.

这是一个演示如何执行此操作的示例。通过这个实现,我添加了一个名为datasetRadiusBuffer的新的圆环图选项属性,该属性控制每个数据集之间的空白区域。

var helpers = Chart.helpers;

// this option will control the white space between embedded charts when there is more than 1 dataset
helpers.extend(Chart.defaults.doughnut, {
  datasetRadiusBuffer: 0
});

Chart.controllers.doughnut = Chart.controllers.doughnut.extend({
  update: function(reset) {
    var me = this;
    var chart = me.chart,
        chartArea = chart.chartArea,
        opts = chart.options,
        arcOpts = opts.elements.arc,
        availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
        availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
        minSize = Math.min(availableWidth, availableHeight),
        offset = {
          x: 0,
          y: 0
        },
        meta = me.getMeta(),
        cutoutPercentage = opts.cutoutPercentage,
        circumference = opts.circumference;

    // 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) {
      var startAngle = opts.rotation % (Math.PI * 2.0);
      startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
      var endAngle = startAngle + circumference;
      var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
      var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
      var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
      var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
      var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
      var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
      var cutout = cutoutPercentage / 100.0;
      var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
      var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
      var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
      minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
      offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
    }

    chart.borderWidth = me.getMaxBorderWidth(meta.data);
    chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
    chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
    chart.radiusLength = ((chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount()) + 25;
    chart.offsetX = offset.x * chart.outerRadius;
    chart.offsetY = offset.y * chart.outerRadius;

    meta.total = me.calculateTotal();

    me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
    me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);

    // factor in the radius buffer if the chart has more than 1 dataset
    if (me.index > 0) {
      me.outerRadius -= opts.datasetRadiusBuffer;
      me.innerRadius -= opts.datasetRadiusBuffer;
    }

    helpers.each(meta.data, function(arc, index) {
      me.updateElement(arc, index, reset);
    });
  },
});

You can see a live example at this codepen.

您可以在此codepen中看到一个实时示例。

#2


0  

To make it work with the latest ChartJS 2.7.2, I've just copied the source as suggested from https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.doughnut.js. Then I added the patch:

为了使它能够使用最新的ChartJS 2.7.2,我刚刚从https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.doughnut.js建议复制了源代码。然后我添加了补丁:

if (me.index > 0) {
  me.outerRadius -= opts.datasetRadiusBuffer;
  me.innerRadius -= opts.datasetRadiusBuffer;
}

Everything was working as expected.

一切都按预期工作。