Как визуализировать текст внутри кольцевой диаграммы, я использую 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/kdvuxbtj/
В пончике потребуется любое количество текста, размер которого идеально подходит для пончика. Чтобы не касаться краев, вы можете установить боковые отступы в процентах от диаметра внутренней части круга. Если вы не установите его, по умолчанию будет 20. Вы также можете выбрать цвет, шрифт и текст. Об остальном позаботится плагин.
Код плагина начинается с базового размера шрифта 30 пикселей. Оттуда он проверит ширину текста и сравнит ее с радиусом круга и изменит его размер в зависимости от соотношения ширины круга / текста.
Минимальный размер шрифта по умолчанию составляет 20 пикселей. Если текст выйдет за границы при минимальном размере шрифта, он будет переносить текст. Высота строки по умолчанию при обтекании текста составляет 25 пикселей, но вы можете ее изменить. Если вы установите для минимального размера шрифта по умолчанию значение false, текст станет бесконечно маленьким и не будет переноситься.
Он также имеет максимальный размер шрифта по умолчанию 75 пикселей на случай, если текста недостаточно, а надписи будут слишком большими.
Это код плагина
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 за помощь в математике , используемой в этом растворе.
Вот очищенный и объединенный пример вышеуказанных решений - отзывчивый (попробуйте изменить размер окна), поддерживает самовыравнивание анимации, поддерживает всплывающие подсказки
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>
Uncaught TypeError: Cannot read property 'extend' of undefined
есть идеи?
Я бы избегал модификации кода 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" />
</div>
Основываясь на ответе @ 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);
}
});
});
Вот пример кода. Попробуйте изменить размер окна. http://jsbin.com/wapono/13/edit
Вы можете использовать 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>
Это основано на обновлении Cmyker для Chart.js 2. (опубликовано как еще один ответ, поскольку я еще не могу комментировать)
У меня возникла проблема с выравниванием текста в Chrome при отображении легенды, поскольку высота диаграммы не включает ее, поэтому она неправильно выровнена по середине. Исправлено это путем учета этого при вычислении fontSize и textY.
Я вычислил процент внутри метода, а не установленное значение, поскольку у меня их несколько на странице. Предполагается, что ваша диаграмма имеет только 2 значения (иначе каков процент? И что первое - это то, для которого вы хотите показать процент. У меня также есть несколько других диаграмм, поэтому я проверяю type = donut. Я использую пончики только для отображения процентов, так что у меня это работает.
Цвет текста кажется немного удачным и пропущенным в зависимости от того, в каком порядке выполняются вещи и т. Д., Поэтому при изменении размера я столкнулся с проблемой, когда текст менял цвет (между черным и основным цветом в одном случае и вторичным цветом и белым в другом), поэтому Я «сохраняю» существующий стиль заливки, рисую текст (в цвете первичных данных), а затем восстанавливаю старый стиль заливки. (Сохранение старого стиля заливки не кажется необходимым, но вы никогда не знаете.)
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();
}
}
});
Вы также можете вставить код mayankcpdixit в onAnimationComplete
option:
// ...
var myDoughnutChart = new Chart(ctx).Doughnut(data, {
onAnimationComplete: function() {
ctx.fillText(data[0].value + "%", 100 - 20, 100, 200);
}
});
Текст будет отображаться после анимации
save
метода
Я создаю демонстрацию с 7 слайдером jQueryUI и 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
});
Ответы @ 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
Одно небольшое улучшение: имеет смысл проверить соответствующий идентификатор холста, см. Измененный фрагмент ниже. В противном случае текст (например, 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
для рендеринга, поэтому вы можете просто реализовать свои собственные всплывающие подсказки, взаимодействуя с ним напрямую.
Надеюсь это поможет.
Решение 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;
}
}
}
});