program story

Chart.js를 사용하여 도넛 차트 내부에 텍스트를 추가하는 방법은 무엇입니까?

inputbox 2020. 10. 6. 08:10
반응형

Chart.js를 사용하여 도넛 차트 내부에 텍스트를 추가하는 방법은 무엇입니까?


도넛 차트 내부에 텍스트를 렌더링하는 방법은 ChartJs를 사용 하고 있습니다.


다음과 같이 코드를 수정해야합니다. chart.Doughnut.defaults

labelFontFamily : "Arial",
labelFontStyle : "normal",
labelFontSize : 24,
labelFontColor : "#666"

그리고 기능 drawPieSegments

ctx.fillText(data[0].value + "%", width/2 - 20, width/2, 200);

이 풀을 참조하십시오 : https://github.com/nnnick/Chart.js/pull/35

여기 에 같은 것을 구현 하는 바이올린 http://jsfiddle.net/mayankcpdixit/6xV78/이 있습니다.


다른 답변은 텍스트 양과 도넛 크기에 따라 텍스트 크기를 조정하지 않습니다. 다음은 중간에 원하는 양의 텍스트를 동적으로 배치하는 데 사용할 수있는 작은 스크립트이며 자동으로 크기가 조정됩니다. http://jsfiddle.net/nkzyx50o/

중간에 동적 텍스트가있는 도넛

도넛에 딱 맞는 크기의 도넛에 텍스트 양이 필요합니다. 가장자리를 건드리지 않도록 원 안쪽 지름의 백분율로 측면 패딩을 설정할 수 있습니다. 설정하지 않으면 기본값은 20입니다. 색상, 글꼴 및 텍스트도 있습니다. 플러그인이 나머지를 처리합니다.

플러그인 코드는 30px의 기본 글꼴 크기로 시작합니다. 거기에서 텍스트의 너비를 확인하고 원의 반경과 비교하고 원 / 텍스트 너비 비율에 따라 크기를 조정합니다.

이것은 플러그인 코드입니다

 Chart.pluginService.register({
  beforeDraw: function (chart) {
    if (chart.config.options.elements.center) {
      //Get ctx from string
      var ctx = chart.chart.ctx;

      //Get options from the center object in options
      var centerConfig = chart.config.options.elements.center;
      var fontStyle = centerConfig.fontStyle || 'Arial';
      var txt = centerConfig.text;
      var color = centerConfig.color || '#000';
      var sidePadding = centerConfig.sidePadding || 20;
      var sidePaddingCalculated = (sidePadding/100) * (chart.innerRadius * 2)
      //Start with a base font of 30px
      ctx.font = "30px " + fontStyle;

      //Get the width of the string and also the width of the element minus 10 to give it 5px side padding
      var stringWidth = ctx.measureText(txt).width;
      var elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated;

      // Find out how much the font can grow in width.
      var widthRatio = elementWidth / stringWidth;
      var newFontSize = Math.floor(30 * widthRatio);
      var elementHeight = (chart.innerRadius * 2);

      // Pick a new font size so it will not be larger than the height of label.
      var fontSizeToUse = Math.min(newFontSize, elementHeight);

      //Set font settings to draw it correctly.
      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.font = fontSizeToUse+"px " + fontStyle;
      ctx.fillStyle = color;

      //Draw text in center
      ctx.fillText(txt, centerX, centerY);
    }
  }
});

그리고 차트 개체에서 다음 옵션을 사용합니다.

options: {
  elements: {
      center: {
      text: 'Desktop',
      color: '#36A2EB', //Default black
      fontStyle: 'Helvetica', //Default Arial
      sidePadding: 15 //Default 20 (as a percentage)
    }
  }
}

이 솔루션에 사용 된 수학에 도움 주신 @Jenna Sloan 에게 감사드립니다 .


다음은 위 솔루션의 정리 및 결합 된 예입니다. 반응 형 (창 크기 조정 시도), 애니메이션 자체 정렬 지원, 툴팁 지원

https://jsfiddle.net/cmyker/u6rr5moq/

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 Verdana";
        this.chart.ctx.textBaseline = "middle";

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

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

var data = [{
    value: 30,
    color: "#F7464A"
}, {
    value: 50,
    color: "#E2EAE9"
}, {
    value: 100,
    color: "#D4CCC5"
}, {
    value: 40,
    color: "#949FB1"
}, {
    value: 120,
    color: "#4D5360"
}];

var DoughnutTextInsideChart = new Chart($('#myChart')[0].getContext('2d')).DoughnutTextInside(data, {
    responsive: true
});
<html>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<body>
    <canvas id="myChart"></canvas>
</body>
</html>

업데이트 17.06.16 :

동일한 기능이지만 chart.js 버전 2의 경우 :

https://jsfiddle.net/cmyker/ooxdL2vj/

var data = {
  labels: [
    "Red",
    "Blue",
    "Yellow"
  ],
  datasets: [
    {
      data: [300, 50, 100],
      backgroundColor: [
        "#FF6384",
        "#36A2EB",
        "#FFCE56"
      ],
      hoverBackgroundColor: [
        "#FF6384",
        "#36A2EB",
        "#FFCE56"
      ]
    }]
};

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx;

    ctx.restore();
    var fontSize = (height / 114).toFixed(2);
    ctx.font = fontSize + "em sans-serif";
    ctx.textBaseline = "middle";

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

    ctx.fillText(text, textX, textY);
    ctx.save();
  }
});

var chart = new Chart(document.getElementById('myChart'), {
  type: 'doughnut',
  data: data,
  options: {
  	responsive: true,
    legend: {
      display: false
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.bundle.js"></script>
<canvas id="myChart"></canvas>


이것을 달성하기 위해 chart.js 코드를 수정하는 것을 피하고 싶습니다. 일반 CSS와 HTML을 사용하면 매우 쉽기 때문입니다. 내 해결책은 다음과 같습니다.

HTML :

<canvas id="productChart1" width="170"></canvas>
<div class="donut-inner">
    <h5>47 / 60 st</h5>
    <span>(30 / 25 st)</span>
</div>

CSS :

.donut-inner {
   margin-top: -100px;
   margin-bottom: 100px;
}
.donut-inner h5 {
   margin-bottom: 5px;
   margin-top: 0;
}
.donut-inner span {
   font-size: 12px;
}

출력은 다음과 같습니다.

여기에 이미지 설명 입력


이것은 또한 내 끝에서 작동합니다 ...

<div style="width: 100px; height: 100px; float: left; position: relative;">
<div style="width: 100%; height: 40px; position: absolute; top: 50%; left: 0; margin-top: -20px; line-height:19px; text-align: center; z-index: 999999999999999">
    99%<Br />
    Total 
</div>
<canvas id="chart-area" width="100" height="100" />

여기에 이미지 설명 입력


@ rap-2-h 답변을 기반으로 대시 보드에서 사용하기 위해 Chart.js의 도넛 차트에 텍스트를 사용하는 코드가 있습니다. 반응 형 옵션에 대한 동적 글꼴 크기가 있습니다.

HTML :

<div>text
<canvas id="chart-area" width="300" height="300" style="border:1px solid"/><div>

스크립트:

var doughnutData = [
            {
                value: 100,
                color:"#F7464A",
                highlight: "#FF5A5E",
                label: "Red"
            },
            {
                value: 50,
                color: "#CCCCCC",
                highlight: "#5AD3D1",
                label: "Green"
            }
        ];

$(document).ready(function(){
  var ctx = $('#chart-area').get(0).getContext("2d");

  var myDoughnut = new Chart(ctx).Doughnut(doughnutData,{
     animation:true,
     responsive: true,
     showTooltips: false,
     percentageInnerCutout : 70,
     segmentShowStroke : false,
     onAnimationComplete: function() {

     var canvasWidthvar = $('#chart-area').width();
     var canvasHeight = $('#chart-area').height();
     //this constant base on canvasHeight / 2.8em
     var constant = 114;
     var fontsize = (canvasHeight/constant).toFixed(2);
     ctx.font=fontsize +"em Verdana";
     ctx.textBaseline="middle"; 
     var total = 0;
     $.each(doughnutData,function() {
       total += parseInt(this.value,10);
   });
  var tpercentage = ((doughnutData[0].value/total)*100).toFixed(2)+"%";
  var textWidth = ctx.measureText(tpercentage).width;

   var txtPosx = Math.round((canvasWidthvar - textWidth)/2);
    ctx.fillText(tpercentage, txtPosx, canvasHeight/2);
  }
 });
});

여기 샘플 code.try 창 크기를 조정하십시오. http://jsbin.com/wapono/13/edit


이것은 Chart.js 2에 대한 Cmyker의 업데이트를 기반으로합니다. (아직 댓글을 달 수 없으므로 다른 답변으로 게시 됨)

차트 높이에 이것을 포함하지 않아서 중간에 올바르게 정렬되지 않았기 때문에 범례가 표시 될 때 Chrome에서 텍스트 정렬에 문제가있었습니다. fontSize 및 textY 계산에서이를 고려하여이 문제를 해결했습니다.

페이지에 여러 개가 있으므로 설정된 값이 아닌 메서드 내부에서 백분율을 계산했습니다. 차트에 2 개의 값만 있다고 가정합니다 (그렇지 않으면 백분율은 무엇입니까? 첫 번째는 백분율을 표시하려는 차트입니다. 다른 차트도 여러 개 있으므로 유형 = 도넛을 확인합니다. 나는 도넛을 사용하여 백분율을 표시하기 때문에 나를 위해 작동합니다.

텍스트 색상은 어떤 순서로 실행되는지 등에 따라 약간 맞고 놓치는 것처럼 보이므로 크기를 조정할 때 텍스트 색상이 변경되는 문제가 발생했습니다 (한 경우에는 검은 색과 기본 색상, 다른 경우에는 보조 색상과 흰색). 기존 채우기 스타일이 무엇이든 "저장"하고 텍스트 (기본 데이터 색상)를 그린 다음 이전 채우기 스타일을 복원합니다. (이전 채우기 스타일을 유지하는 것은 필요하지 않은 것 같지만 알 수 없습니다.)

https://jsfiddle.net/g733tj8h/

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx,
        type = chart.config.type;

    if (type == 'doughnut')
    {
      var percent = Math.round((chart.config.data.datasets[0].data[0] * 100) /
                    (chart.config.data.datasets[0].data[0] +
                    chart.config.data.datasets[0].data[1]));
      var oldFill = ctx.fillStyle;
      var fontSize = ((height - chart.chartArea.top) / 100).toFixed(2);

      ctx.restore();
      ctx.font = fontSize + "em sans-serif";
      ctx.textBaseline = "middle"

      var text = percent + "%",
          textX = Math.round((width - ctx.measureText(text).width) / 2),
          textY = (height + chart.chartArea.top) / 2;

      ctx.fillStyle = chart.config.data.datasets[0].backgroundColor[0];
      ctx.fillText(text, textX, textY);
      ctx.fillStyle = oldFill;
      ctx.save();
    }
  }
});

var data = {
  labels: ["Red","Blue"],
  datasets: [
    {
      data: [300, 50],
      backgroundColor: ["#FF6384","#36A2EB"],
    }]
};

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx,
        type = chart.config.type;

    if (type == 'doughnut')
    {
    	var percent = Math.round((chart.config.data.datasets[0].data[0] * 100) /
                    (chart.config.data.datasets[0].data[0] +
                    chart.config.data.datasets[0].data[1]));
			var oldFill = ctx.fillStyle;
      var fontSize = ((height - chart.chartArea.top) / 100).toFixed(2);
      
      ctx.restore();
      ctx.font = fontSize + "em sans-serif";
      ctx.textBaseline = "middle"

      var text = percent + "%",
          textX = Math.round((width - ctx.measureText(text).width) / 2),
          textY = (height + chart.chartArea.top) / 2;
			
      ctx.fillStyle = chart.config.data.datasets[0].backgroundColor[0];
      ctx.fillText(text, textX, textY);
      ctx.fillStyle = oldFill;
      ctx.save();
    }
  }
});

var myChart = new Chart(document.getElementById('myChart'), {
  type: 'doughnut',
  data: data,
  options: {
  	responsive: true,
    legend: {
      display: true
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.bundle.js"></script>
<canvas id="myChart"></canvas>


onAnimationComplete옵션에 mayankcpdixit의 코드를 붙여 넣을 수도 있습니다.

// ...
var myDoughnutChart = new Chart(ctx).Doughnut(data, {
    onAnimationComplete: function() {
        ctx.fillText(data[0].value + "%", 100 - 20, 100, 200);
    }
});

애니메이션 후 텍스트가 표시됩니다.


7 개의 jQueryUI Slider 및 ChartJ (동적 텍스트 포함)로 데모를 만듭니다.

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 / 140).toFixed(2);
            this.chart.ctx.font = fontSize + "em Verdana";
            this.chart.ctx.textBaseline = "middle";

            var red = $( "#red" ).slider( "value" ),
            green = $( "#green" ).slider( "value" ),
            blue = $( "#blue" ).slider( "value" ),
            yellow = $( "#yellow" ).slider( "value" ),
            sienna = $( "#sienna" ).slider( "value" ),
            gold = $( "#gold" ).slider( "value" ),
            violet = $( "#violet" ).slider( "value" );
            var text = (red+green+blue+yellow+sienna+gold+violet) + " minutes";
            var textX = Math.round((width - this.chart.ctx.measureText(text).width) / 2);
            var textY = height / 2;
            this.chart.ctx.fillStyle = '#000000';
            this.chart.ctx.fillText(text, textX, textY);
        }
    });


var ctx = $("#myChart").get(0).getContext("2d");
var myDoughnutChart = new Chart(ctx).DoughnutTextInside(data, {
    responsive: false
});

JSFIDDLE 데모

여기에 이미지 설명 입력


반응을 원하면 상대 / 절대 위치와 함께 CSS를 사용할 수 있습니다. 또한 여러 줄을 쉽게 처리 할 수 ​​있습니다.

https://jsfiddle.net/mgyp0jkk/

<div class="relative">
  <canvas id="myChart"></canvas>      
  <div class="absolute-center text-center">
    <p>Some text</p>
    <p>Some text</p>
  </div>
</div>

@ rap-2-h 및 @Ztuons Ch의 대답은 showTooltips옵션을 활성화 하는 것을 허용하지 않지만 canvas차트를 렌더링하는 개체 뒤에 두 번째 개체를 만들고 레이어를 만드는 것입니다 .

중요한 부분은 div와 캔버스 객체 자체에 필요한 스타일링으로 서로 위에 렌더링되도록하는 것입니다.

var data = [
    {value : 100, color : 'rgba(226,151,093,1)', highlight : 'rgba(226,151,093,0.75)', label : "Sector 1"},
    {value : 100, color : 'rgba(214,113,088,1)', highlight : 'rgba(214,113,088,0.75)', label : "Sector 2"},
    {value : 100, color : 'rgba(202,097,096,1)', highlight : 'rgba(202,097,096,0.75)', label : "Sector 3"}
]

var options = { showTooltips : true };
     
var total = 0;
for (i = 0; i < data.length; i++) {
     total = total + data[i].value;
}

var chartCtx = $("#canvas").get(0).getContext("2d");
var chart = new Chart(chartCtx).Doughnut(data, options);

var textCtx = $("#text").get(0).getContext("2d");
textCtx.textAlign = "center";
textCtx.textBaseline = "middle";
textCtx.font = "30px sans-serif";
textCtx.fillText(total, 150, 150);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<html>
<body>
<div style="position: relative; width:300px; height:300px;">
    <canvas id="text" 
            style="z-index: 1; 
                   position: absolute;
                   left: 0px; 
                   top: 0px;" 
            height="300" 
            width="300"></canvas>
    <canvas id="canvas" 
            style="z-index: 2; 
                   position: absolute;
                   left: 0px; 
                   top: 0px;" 
            height="300" 
            width="300"></canvas>
</div>
</body>
</html>

jsfiddle은 다음과 같습니다 : https://jsfiddle.net/68vxqyak/1/


@Cmyker, chart.js v2를위한 훌륭한 솔루션

약간의 개선 사항 : 적절한 캔버스 ID를 확인하는 것이 좋습니다. 아래 수정 된 스 니펫을 참조하세요. 그렇지 않으면 텍스트 (예 : 75 %)도 페이지 내의 다른 차트 유형 중간에 렌더링됩니다.

  Chart.pluginService.register({
    beforeDraw: function(chart) {
      if (chart.canvas.id === 'doghnutChart') {
        let width = chart.chart.width,
            height = chart.chart.outerRadius * 2,
            ctx = chart.chart.ctx;

        rewardImg.width = 40;
        rewardImg.height = 40;
        let imageX = Math.round((width - rewardImg.width) / 2),
            imageY = (height - rewardImg.height ) / 2;

        ctx.drawImage(rewardImg, imageX, imageY, 40, 40);
        ctx.save();
      }
    }
  });

범례 ( http://www.chartjs.org/docs/latest/configuration/legend.html 참조 )는 차트 높이를 확대하므로 높이 값은 반경으로 구해야합니다.


우선, Chart.js 선택에 대한 찬사! 현재 프로젝트 중 하나에서 사용하고 있으며 정말 마음에 듭니다. 완벽하게 작동합니다.

레이블 / 툴팁은 아직 라이브러리의 일부가 아니지만 다음 세 가지 풀 요청을 살펴볼 수 있습니다.

그리고 Cracker0dks가 언급했듯이 Chart.js는 canvas렌더링에 사용 하므로 직접 상호 작용하여 자신의 툴팁을 구현할 수도 있습니다.

도움이 되었기를 바랍니다.

참고 URL : https://stackoverflow.com/questions/20966817/how-to-add-text-inside-the-doughnut-chart-using-chart-js

반응형