如何使用 Chart.js 在甜甜圈图表中添加文本?

如何呈现文字内部的甜甜圈图表,我使用 图表

284638 次浏览

首先,选择 Chart.js 值得赞扬!我正在使用它在我目前的一个项目,我绝对喜欢它-它完美地完成了工作。

虽然标签/工具提示还不是库的一部分,但是您可能希望看一下这三个 pull 请求:

而且,正如 干杯提到的,Chart.js 使用 canvas进行渲染,所以您也可以通过直接与它交互来实现自己的工具提示。

希望这个能帮上忙。

你必须修改代码,例如: 在 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/

也可以在 onAnimationComplete选项中粘贴 mayankcpdixit 的代码:

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

动画后将显示文本

我会避免修改 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;
}

输出如下:

enter image description here

我这边也是。

<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" />
</div>

enter image description here

基于@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 尝试调整窗口的大小

@ 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/

这里是清理和上述解决方案的组合示例-响应(尝试调整窗口大小) ,支持动画自对齐,支持工具提示

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>

我用7个 jQueryUI Slider 和 ChartJs 创建了一个演示(其中包含动态文本)

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
});

JSFIDLE 中的演示

enter image description here

这是基于 Cmyker 对 Chart.js 2的更新

我有一个问题与文字对齐在 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>

其他的答案都没有根据文本的数量和甜甜圈的大小来调整文本的大小。下面是一个小脚本,您可以使用它动态地将任何数量的文本放置在中间,它将自动调整其大小。

示例: < a href = “ http://jsfiddle.net/kdvuxbtj/”rel = “ noReferrer”> http://jsfiddle.net/kdvuxbtj/

Doughnut With Dynamic Text in the Middle

它将采取任何数量的文字在甜甜圈大小完美的甜甜圈。为了避免接触边缘,你可以设置一个边填充作为一个百分比的直径内的圆。如果你不设置它,它将默认为20。还需要颜色、字体和文本。插件会处理剩下的事情。

插件代码将以30px 的基本字体大小开始。然后它会检查文本的宽度,并将其与圆的半径进行比较,然后根据圆/文本宽度的比率调整大小。

它的默认最小字体大小为20px。如果文本超出最小字体大小的范围,它将包装文本。包装文本时的默认行高是25px,但是您可以更改它。如果将默认的最小字体大小设置为 false,则文本将变得无限小并且不会换行。

它还有一个默认的最大字体大小为75px 的情况下,没有足够的文字和字体会太大。

这是插件代码

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 maxFontSize = centerConfig.maxFontSize || 75;
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, maxFontSize);
var minFontSize = centerConfig.minFontSize;
var lineHeight = centerConfig.lineHeight || 25;
var wrapText = false;


if (minFontSize === undefined) {
minFontSize = 20;
}


if (minFontSize && fontSizeToUse < minFontSize) {
fontSizeToUse = minFontSize;
wrapText = true;
}


// 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;


if (!wrapText) {
ctx.fillText(txt, centerX, centerY);
return;
}


var words = txt.split(' ');
var line = '';
var lines = [];


// Break words up into multiple lines if necessary
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > elementWidth && n > 0) {
lines.push(line);
line = words[n] + ' ';
} else {
line = testLine;
}
}


// Move the center up depending on line height and number of lines
centerY -= (lines.length / 2) * lineHeight;


for (var n = 0; n < lines.length; n++) {
ctx.fillText(lines[n], centerX, centerY);
centerY += lineHeight;
}
//Draw text in center
ctx.fillText(line, centerX, centerY);
}
}
});

并在图表对象中使用下列选项

options: {
elements: {
center: {
text: 'Red is 2/3 the total numbers',
color: '#FF6384', // Default is #000000
fontStyle: 'Arial', // Default is Arial
sidePadding: 20, // Default is 20 (as a percentage)
minFontSize: 20, // Default is 20 (in px), set to false and text will not wrap.
lineHeight: 25 // Default is 25 (in px), used for when text wraps
}
}
}

在这个解决方案中使用的数学方面的帮助归功于 @ Jenna Sloan

你可以使用带有相对/绝对定位的 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>

Alesana 的解决方案总体上对我来说非常有效,但是像其他人一样,我希望能够指定在哪里发生换行。我做了一些简单的修改,在 n’字符处换行,只要文本已经被包装。一个更完整的解决方案将强制换行,如果有任何 n’字符在文本中,但我现在没有时间使用字体大小工作。在包装时,这个变化也使水平方向的中心更好一些(避免了尾随空间)。代码在下面(我还不能发表评论)。

如果有人把这个插件放到 GitHub 上就太酷了。

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 maxFontSize = centerConfig.maxFontSize || 75;
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, maxFontSize);
var minFontSize = centerConfig.minFontSize;
var lineHeight = centerConfig.lineHeight || 25;
var wrapText = false;


if (minFontSize === undefined) {
minFontSize = 20;
}


if (minFontSize && fontSizeToUse < minFontSize) {
fontSizeToUse = minFontSize;
wrapText = true;
}


// 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;


if (!wrapText) {
ctx.fillText(txt, centerX, centerY);
return;
}


var lines = [];
var chunks = txt.split('\n');
for (var m = 0; m < chunks.length; m++) {
var words = chunks[m].split(' ');
var line;


// Break words up into multiple lines if necessary
for (var n = 0; n < words.length; n++) {
var testLine = (n == 0) ? words[n] : line + ' ' + words[n];
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > elementWidth && n > 0) {
lines.push(line);
line = words[n];
} else {
line = testLine;
}
}
lines.push(line);
}


// Move the center up depending on line height and number of lines
centerY -= ((lines.length-1) / 2) * lineHeight;


// All but last line
for (var n = 0; n < lines.length; n++) {
ctx.fillText(lines[n], centerX, centerY);
centerY += lineHeight;
}
}
}
});

我知道答案很老套,但也许有人能派上用场。 最简单的方法是使用 onAnimationProgress事件。

像这样。

var myDoughnutChart = new Chart(ctx).Doughnut(data, {
onAnimationProgress: function() {
        

const dataset = this.data.datasets[0];
const model = dataset._meta[Object.keys(dataset._meta)[0]]?.data[0]?._model;
if (!model) { return; }
// model.x and model.y is the center of the chart
this.ctx.fillText('00%', model.x, model.y);
}
});

自从这个问题被提出以来,世界发生了一些变化。 值得庆幸的是,今天我们有了 Flexbox,并且可以通过以下几行来实现响应:

<!-- In your html/template files -->
<section class="container">
<canvas id="chartJsChart"></canvas>
<p class="label">Some text</p>
</section>


<style>
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.label {
position: absolute;
}
</style>

我希望这对别人有帮助!

分步教程: 如何添加多个文本标签堆叠在甜甜圈图表在图表 JS

[ https://www.youtube.com/watch?v=_7w52t9aemo ][1]