Chart.js Donut с закругленными краями и текстом по центру

Я пытаюсь добиться закругленных углов, подобных этой статье здесь, но в сочетании с текст в центре, пока у меня есть код ниже, но я не уверен, как объединить обе идеи

Любая помощь будет оценена по достоинству!

изображение выглядит так, как показано ниже, пончик с текстовым изображением:

и мой код выглядит следующим образом для создания текста внутри пончика.

Chart.types.Doughnut.extend({
   name: "DoughnutTextInside",
   showTooltip: function() {
       this.chart.ctx.save();
       Chart.types.Doughnut.prototype.showTooltip.apply(this, arguments);
       this.chart.ctx.restore();
},
draw: function() {
    Chart.types.Doughnut.prototype.draw.apply(this, arguments);

    var width = this.chart.width,
        height = this.chart.height;

    var fontSize = (height / 114).toFixed(2);
    this.chart.ctx.font = fontSize + "em Lato";
    this.chart.ctx.textBaseline = "middle";

    var text = "40%",
        textX = Math.round((width - this.chart.ctx.measureText(text).width) / 2),
        textY = height / 2;

    this.chart.ctx.fillText(text, textX, textY);
   }
});

var data = [{
   label: "Wins %",
   value: 120,
   color: "#2ecc71"
}, {
   label: "Losses %",
   value: 240,
   color: "#dddddd"
}, {
   value: 0,
   color: "#888888"
}];

var DoughnutTextInsideChart = new Chart($('#myChart')  [0].getContext('2d')).DoughnutTextInside(data, {
   responsive: true,
   segmentShowStroke: false,
   animationEasing: "easeInOutQuint",
});

person Daniel    schedule 23.05.2016    source источник


Ответы (4)


В версии 2.1.3 для этого можно использовать pluginService.


Предварительный просмотр

введите здесь описание изображения


Скрипт

// round corners
Chart.pluginService.register({
    afterUpdate: function (chart) {
        if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
            var arc = chart.getDatasetMeta(0).data[chart.config.options.elements.arc.roundedCornersFor];
            arc.round = {
                x: (chart.chartArea.left + chart.chartArea.right) / 2,
                y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
                radius: (chart.outerRadius + chart.innerRadius) / 2,
                thickness: (chart.outerRadius - chart.innerRadius) / 2 - 1,
                backgroundColor: arc._model.backgroundColor
            }
        }
    },

    afterDraw: function (chart) {
        if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
            var ctx = chart.chart.ctx;
            var arc = chart.getDatasetMeta(0).data[chart.config.options.elements.arc.roundedCornersFor];
            var startAngle = Math.PI / 2 - arc._view.startAngle;
            var endAngle = Math.PI / 2 - arc._view.endAngle;

            ctx.save();
            ctx.translate(arc.round.x, arc.round.y);
            console.log(arc.round.startAngle)
            ctx.fillStyle = arc.round.backgroundColor;
            ctx.beginPath();
            ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
            ctx.arc(arc.round.radius * Math.sin(endAngle), arc.round.radius * Math.cos(endAngle), arc.round.thickness, 0, 2 * Math.PI);
            ctx.closePath();
            ctx.fill();
            ctx.restore();
        }
    },
});

// write text plugin
Chart.pluginService.register({
    afterUpdate: function (chart) {
        if (chart.config.options.elements.center) {
            var helpers = Chart.helpers;
            var centerConfig = chart.config.options.elements.center;
            var globalConfig = Chart.defaults.global;
            var ctx = chart.chart.ctx;

            var fontStyle = helpers.getValueOrDefault(centerConfig.fontStyle, globalConfig.defaultFontStyle);
            var fontFamily = helpers.getValueOrDefault(centerConfig.fontFamily, globalConfig.defaultFontFamily);

            if (centerConfig.fontSize)
                var fontSize = centerConfig.fontSize;
                // figure out the best font size, if one is not specified
            else {
                ctx.save();
                var fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
                var maxFontSize = helpers.getValueOrDefault(centerConfig.maxFontSize, 256);
                var maxText = helpers.getValueOrDefault(centerConfig.maxText, centerConfig.text);

                do {
                    ctx.font = helpers.fontString(fontSize, fontStyle, fontFamily);
                    var textWidth = ctx.measureText(maxText).width;

                    // check if it fits, is within configured limits and that we are not simply toggling back and forth
                    if (textWidth < chart.innerRadius * 2 && fontSize < maxFontSize)
                        fontSize += 1;
                    else {
                        // reverse last step
                        fontSize -= 1;
                        break;
                    }
                } while (true)
                ctx.restore();
            }

            // save properties
            chart.center = {
                font: helpers.fontString(fontSize, fontStyle, fontFamily),
                fillStyle: helpers.getValueOrDefault(centerConfig.fontColor, globalConfig.defaultFontColor)
            };
        }
    },
    afterDraw: function (chart) {
        if (chart.center) {
            var centerConfig = chart.config.options.elements.center;
            var ctx = chart.chart.ctx;

            ctx.save();
            ctx.font = chart.center.font;
            ctx.fillStyle = chart.center.fillStyle;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            var centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
            var centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
            ctx.fillText(centerConfig.text, centerX, centerY);
            ctx.restore();
        }
    },
})

а потом

    ...
    options: {
        elements: {
            arc: {
                roundedCornersFor: 0
            },
            center: {
                // the longest text that could appear in the center
                maxText: '100%',
                text: '67%',
                fontColor: '#FF6684',
                fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
                fontStyle: 'normal',
                // fontSize: 12,
                // if a fontSize is NOT specified, we will scale (within the below limits) maxText to take up the maximum space in the center
                // if these are not specified either, we default to 1 and 256
                minFontSize: 1,
                maxFontSize: 256,
            }
        }
    }
};

Вы можете избавиться от части кода, если вы не хотите, чтобы он был универсальным (например, если вы исправите fontSize, если вы исправите индекс для округления и т. д.)


Скрипт — http://jsfiddle.net/cd3fdoy9/

person potatopeelings    schedule 23.05.2016
comment
В своем коде вы отключили наведение (назначив одинаковые цвета), был бы способ оставить его включенным, но отметить 2 нарисованных круга как часть элемента (чтобы они правильно выделялись)? - person Blizz; 16.11.2016
comment
Есть ли способ вставить разрыв строки в центральную метку? Я видел ваш пост (stackoverflow.com/a/37099412/665783) о разрыве строки для всплывающей подсказки, но он не работает с этим текстом. - person Jacob; 11.02.2017
comment
Можно немного глупый вопрос? Куда добавить плагин? В файле chaart.js, который был установлен с помощью npm? - person user7334203; 13.03.2017
comment
@user7334203 user7334203 - после файла Chart.js и перед вашим вызовом нарисуйте диаграмму. - person potatopeelings; 14.03.2017
comment
привет @potatopeelings, в этом случае у меня работает. но если есть несколько частей, как мы можем получить эти закругленные углы? - person ninjadev1030; 21.09.2018

Я добавил фрагмент кода для изменения текста в центре при нажатии на легенду.

afterDraw: function (chart) {
    if (chart.center) {
        var ctx = chart.chart.ctx;

        var i,a,s;
        var n = chart;
        var total = 0;
        for(i=0,a=(n.data.datasets||[]).length;a>i;++i){
            s = n.getDatasetMeta(i);
            var x;
            for(x=0; x<s.data.length; x++){
                if (!s.data[x].hidden)
                    total += n.data.datasets[i].data[x];
            }
        }

        ctx.save();
        ctx.font = chart.center.font;
        ctx.fillStyle = chart.center.fillStyle;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        var centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
        var centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
        ctx.fillText(total, centerX, centerY);
        ctx.restore();
    }
}

Скрипт — http://jsfiddle.net/cd3fdoy9/49/

person Denis De Souza Ontag    schedule 29.12.2016
comment
Спасибо за эту настройку. :) - person CodeMonkey; 20.06.2020

Немного изменен код, чтобы обрабатывать использование arc.borderWidth в дуге, если она используется. Для версии 2.8 Charts.js

Chart.pluginService.register({
afterUpdate: function (chart) {
    if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
        var arc = chart.getDatasetMeta(0).data[chart.config.options.elements.arc.roundedCornersFor];
        arc.round = {
            x: (chart.chartArea.left + chart.chartArea.right + chart.config.options.elements.arc.borderWidth) / 2,
            y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
            radius: (chart.outerRadius + chart.innerRadius) / 2,
            thickness: (chart.outerRadius - chart.innerRadius - (chart.config.options.elements.arc.borderWidth /2)) / 2 -1,
            backgroundColor: arc._model.backgroundColor
        };
    }
},

afterDraw: function (chart) {
    if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
        var ctx = chart.chart.ctx;
        var arc = chart.getDatasetMeta(0).data[chart.config.options.elements.arc.roundedCornersFor];
        var startAngle = Math.PI / 2 - arc._view.startAngle;
        var endAngle = Math.PI / 2 - arc._view.endAngle;

        ctx.save();
        ctx.translate(arc.round.x, arc.round.y);
        ctx.fillStyle = arc.round.backgroundColor;
        ctx.beginPath();
        ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
        ctx.arc(arc.round.radius * Math.sin(endAngle), arc.round.radius * Math.cos(endAngle), arc.round.thickness, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.fill();
        ctx.restore();
    }
}

});

person littlevahn    schedule 03.07.2019

Я работаю над тем же проектом и делаю это введите здесь описание изображения

  Chart.defaults.RoundedDoughnut = Chart.helpers.clone(Chart.defaults.doughnut);
  const costume = Chart.controllers.doughnut.extend({
    draw(ease) {
      Chart.controllers.doughnut.prototype.draw.call(this, ease);
      const { ctx } = this.chart.chart;

      const easingDecimal = ease || 1;
      const arcs = this.getMeta().data;

      Chart.helpers.each(arcs, function(arc, index) {
        arc.transition(easingDecimal).draw();

        const vm = arc._view;

        const radius = (vm.outerRadius + vm.innerRadius) / 2;
        const thickness = (vm.outerRadius - vm.innerRadius - 2) / 2;
        const startAngle = Math.PI - vm.startAngle - Math.PI / 2;
        const angle = Math.PI - vm.endAngle - Math.PI / 2;

        if (index % 2 == 1) {
          ctx.save();
          ctx.fillStyle = vm.backgroundColor;

          ctx.translate(vm.x, vm.y);
          ctx.beginPath();
          ctx.arc(
            radius * Math.sin(startAngle),
            radius * Math.cos(startAngle),
            thickness,
            0,
            2 * Math.PI,
          );
          ctx.fill();
          ctx.fillStyle = vm.backgroundColor;
          ctx.beginPath();
          ctx.arc(
            radius * Math.sin(angle),
            radius * Math.cos(angle),
            thickness,
            0,
            2 * Math.PI,
          );
          ctx.fill();
          ctx.restore();
        }
        if (index == 2) {
          ctx.save();
          ctx.fillStyle = arcs[1]._view.backgroundColor;

          ctx.translate(vm.x, vm.y);
          ctx.beginPath();
          ctx.arc(
            radius * Math.sin(startAngle),
            radius * Math.cos(startAngle),
            thickness,
            0,
            2 * Math.PI,
          );
          ctx.fill();
          ctx.restore();
        }
      });
    },
  });

  const deliveredData = {
    labels: ['1', '2', '3', '4'],
    datasets: [
      {
        data: [5, 40, 5, 30],
        backgroundColor: ['#fff', '#ff0000', '#fff', '#ffff00'],
      },
    ],
  };

  Chart.controllers.RoundedDoughnut = costume;
  const newChartInstance = new Chart(chartRef.current, {
    type: 'RoundedDoughnut',
    data: deliveredData,
    options: { cutoutPercentage: 75, legend: { display: false }, tooltips: { enabled: false } },
  });
person Mehran Motiee    schedule 19.09.2020