I have two doughnut charts written in Chart JS with labels that would specify dollar amounts as well as percentages.
<canvas id="ikeBudgetR1" width="400px" height="400px;"></canvas>
<canvas id="ikeBudgetR2" width="400px" height="400px;"></canvas>
Javascript
<script>
var ctx = document.getElementById("ikeBudgetR1").getContext('2d');
var ikeBudgetR1 = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Administration", "Project Delivery", "Multifamily", "Homebuyers Assistance Program", "Single Family Home Repair Program"],
datasets: [{
backgroundColor: [
"#00558C",
"#64A70B",
"#DA291C",
"#DE7C00",
"#9B26B6"
],
data: [4362828, 1787858, 57682924, 10108500, 13314455]
}]
},
options: {
title: {
display: true,
text: 'Round 1'
},
legend: {
display: false,
},
tooltips: {
callbacks: {
title: function(tooltipItem, data) {
return data['labels'][tooltipItem[0]['index']];
},
label: function(tooltipItem, data) {
var value = data.datasets[0].data[tooltipItem.index];
value = value.toString();
value = value.split(/(?=(?:...)*$)/);
value = value.join(',');
return '$' + value;
},
afterLabel: function(tooltipItem, data) {
var dataset = data['datasets'][0];
var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset["_meta"][0]['total']) * 100)
return percent + '%';
}
},
}
}
});
</script>
<script>
var ctx = document.getElementById("ikeBudgetR2").getContext('2d');
var ikeBudgetR2 = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Multifamily", "Single Family Home Repair Program", "Infrastructure Projects"],
datasets: [{
backgroundColor: [
"#00558C",
"#64A70B",
"#DA291C",
"#DE7C00",
"#9B26B6"
],
data: [91400000, 63700000, 26100000]
}]
},
options: {
title: {
display: true,
text: 'Round 2'
},
legend: {
display: false,
},
tooltips: {
callbacks: {
title: function(tooltipItem, data) {
return data['labels'][tooltipItem[0]['index']];
},
label: function(tooltipItem, data) {
var value = data.datasets[0].data[tooltipItem.index];
value = value.toString();
value = value.split(/(?=(?:...)*$)/);
value = value.join(',');
return '$' + value;
},
afterLabel: function(tooltipItem, data) {
var dataset = data['datasets'][0];
var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset["_meta"][0]['total']) * 100)
return '(' + percent + '%)';
}
},
}
}
});
</script>
I can only get the percentages to calculate for one of the charts. Is it possible to make a global label for these charts where I don't have to calculate the percentages for each chart?
My fiddle
I was able to solve this by using the following formula:
afterLabel : function(tooltipItem, data) {
var allData = data.datasets[tooltipItem.datasetIndex].data;
var tooltipData = allData[tooltipItem.index];
var total = 0;
for (var i=0; i<allData.length; i++) {
total += allData[i];
}
var tooltipPercentage = Math.round((tooltipData / total) * 100);
return tooltipPercentage + '%';
},
It's not a global formula but it works on each chart I add to the page.
Related
I have a chart using Chart.js but regardless of what order I put the input data it is outputted out of order, here is a fiddle:
const response = [
{
"mmyy":"12/19",
"promocode":"promo1",
"amount":"2776"
},
{
"mmyy":"01/20",
"promocode":"promo1",
"amount":"1245"
},
{
"mmyy":"01/20",
"promocode":"promo2",
"amount":"179"
}
];
var chartColors = window.chartColors;
var color = Chart.helpers.color;
const colors = [color(chartColors.red).alpha(0.5).rgbString(),
color(chartColors.orange).alpha(0.5).rgbString(),
color(chartColors.yellow).alpha(0.5).rgbString(),
color(chartColors.green).alpha(0.5).rgbString(),
color(chartColors.blue).alpha(0.5).rgbString()];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
let i = 0;
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: colors[i++]
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {
}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
As you can see it shows 01/20 and then 12/19 when it should be in reverse order.
Can someone tell me how to make it show in chronological order (oldest to newest)?
Thank you very much.
You can use moment.js to parse and help sorting the dates.
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort((d1, d2) => moment(d1, 'MM/YY').diff(moment(d2, 'MM/YY')));
This results in the following runnable code snippet.
const response = [
{
"mmyy":"12/19",
"promocode":"promo1",
"amount":"2776"
},
{
"mmyy":"01/20",
"promocode":"promo1",
"amount":"1245"
},
{
"mmyy":"01/20",
"promocode":"promo2",
"amount":"179"
}
];
var chartColors = window.chartColors;
var color = Chart.helpers.color;
const colors = [color(chartColors.red).alpha(0.5).rgbString(),
color(chartColors.orange).alpha(0.5).rgbString(),
color(chartColors.yellow).alpha(0.5).rgbString(),
color(chartColors.green).alpha(0.5).rgbString(),
color(chartColors.blue).alpha(0.5).rgbString()];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort((d1, d2) => moment(d1, 'MM/YY').diff(moment(d2, 'MM/YY')));
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
let i = 0;
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: colors[i++]
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {
}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
You can use reverse for this
...
scales: {
xAxes: [{
stacked: false,
reverse: true // ADD THIS LINE
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
...
Herewith I used JavaScript line chart within Laravel framework. Mouse hover tool tip to display point values working perfectly with label:function(...). I need to display only y-axis value on every point at chart on create (Without mouse hovering). To perform this I used drawDatasetPointsLabels() method from How to display Line Chart dataset point labels with Chart.js? and call it on chart options. But unfortunately it is not working. Below is the code.
chart.blade
monthlabels={.......};
salesqty = {......};
stockqty = {......};
document.getElementById("history_canvas_holder").innerHTML = ' ';
document.getElementById("history_canvas_holder").innerHTML = '<canvas id="historyChart" width="500" height="350"></canvas>';
var ctx = document.getElementById("historyChart").getContext('2d');
var myLineChart = new Chart(ctx,
{
type: 'line',
data:
{
labels: monthlabels,
datasets: [{
label: 'Sales',
data: salesqty,
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 2
},
{
label: 'Stock',
data: stockqty,
borderColor: [
'rgba(0,161,232)'
],
borderWidth: 2
}
]
},
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += Math.round(tooltipItem.yLabel * 100) / 100;
return label;
},
onAnimationProgress: function() { drawDatasetPointsLabels(ctx) },
onAnimationComplete: function() { drawDatasetPointsLabels(ctx) }
}
}
}
});
function drawDatasetPointsLabels(ctx) {
ctx.font = '.9rem "ABCD",sans-serif';
ctx.fillStyle = '#AAA';
ctx.textAlign="center";
$(historyChart.datasets).each(function(idx,dataset){
$(dataset.points).each(function(pdx,pointinfo){
if ( pointinfo.value !== null ) {
ctx.fillText(pointinfo.value,pointinfo.x,pointinfo.y - 15);
}
});
});
}
In the type of v2.5.0
`options: {
tooltips: {
enabled: false
}
}`
or
`tooltips :{
custom : function(tooltipModel) {
tooltipModel.opacity = 0;
}
}`
type of v2.1.4
`Chart.defaults.global.tooltips.enabled = false;`
people said those ways are work, but not in all charts type.
Consider the following codesample donut chart using jquery-flot , now as i have added the 'image' class on click of the donut, i want to dynamically add the degree in the 'image' class so that the clicked item will be facing down at the bottom ( like on the -ve side of the y-axis ).`
var data = [{
label: "Pause",
data: 150
}, {
label: "No Pause",
data: 100
}, {
label: "yes Pause",
data: 80
}, {
label: "Sleeping",
data: 250
}];
var options = {
series: {
pie: {
show: true,
innerRadius: 0.5,
radius: 1,
startAngle: 1,
}
},
grid: {
hoverable: true,
clickable: true
},
legend: {
show: false
},
stroke: {
width: 4
},
tooltip: true,
tooltipOpts: {
cssClass: "flotTip",
content: "%s: %p.0%",
defaultTheme: false
}
};
$("#pie-placeholder").bind("plotclick", function(event, pos, obj) {
$("#pie-placeholder").addClass('image')
});
var plot = $.plot($("#pie-placeholder"), data, options);
`
Note:- this is done using Jquery flot
Here you can find my solution to your problem if I got you right.
$("#pie-placeholder").bind("plotclick", function(event, pos, obj) {
if (obj) {
var percentInRads = 0.02;
var currSegmentInRads = percentInRads * obj.datapoint[0]
var currSegmentOffset = currSegmentInRads / 2;
var currSegmentStart = currSegmentOffset >= 0.5 ? -0.5 + currSegmentOffset : 0.5 - currSegmentOffset;
var total = 0;
var beforeTotal = 0;
for (var idx = 0; idx < data.length; idx++) {
var segment = data[idx];
if (idx < obj.seriesIndex) {
beforeTotal += segment.data;
}
total += segment.data;
}
var beforePart = (beforeTotal / total * 100) * percentInRads;
var chartStartAngle = currSegmentStart - beforePart;
options.series.pie.startAngle = chartStartAngle;
$.plot($("#pie-placeholder"), data, options);
console.log(obj.series);
}
});
I need to input the value of the first percentage into the chart, like this image
In this case, put the value of pedro(33%) within the chart.
I am beginner with chartJS and do not know it completely. Is it possible to do that?
var randomScalingFactor = function() {
return Math.round(Math.random() * 100);
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: [
33,
67,
],
backgroundColor: [
"#F7464A",
"#46BFBD",
],
label: 'Expenditures'
}],
labels: [
"Pedro: 33 ",
"Henrique: 67 ",
]
},
options: {
responsive: true,
legend: {
position: 'bottom',
},
title: {
display: true,
text: 'Pedro Henrique Kuzminskas Miyazaki de Souza'
},
animation: {
animateScale: true,
animateRotate: true
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var total = dataset.data.reduce(function(previousValue, currentValue, currentIndex, array) {
return previousValue + currentValue;
});
var currentValue = dataset.data[tooltipItem.index];
var precentage = Math.floor(((currentValue / total) * 100) + 0.5);
return precentage + "%";
}
}
}
}
};
var ctx = document.getElementById("myChart").getContext("2d");
window.myDoughnut = new Chart(ctx, config); {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
<canvas id="myChart" width="400" height="200"></canvas>
This is not part of the default behavior. You will need to modify the chart.js script.
How to add text inside the doughnut chart using Chart.js?
I have some UV Sensors (currently running on Thingspeak) - but I need to have multiple series on the same chart, so I made a sample .php page on my website.
I have the basic chart working nicely, but I have not been able to get it to do live updates - my coding skills are very lacking & I would appreciate any help I can get!
The sample chart is here: http://www.sesupply.co.nz/test.php
I have the code on JSFiddle here: https://jsfiddle.net/SESupply/9xn65qrL/9/
// variables for the first series
var series_1_channel_id = 43330;
var series_1_field_number = 4;
var series_1_read_api_key = '7ZPHNX2SXPM0CA1K';
var series_1_results = 480;
var series_1_color = '#d62020';
var series_1_name = 'Zims Sensor';
// variables for the second series
var series_2_channel_id = 45473;
var series_2_field_number = 2;
var series_2_read_api_key = 'N12T3CWQB5IWJAU9';
var series_2_results = 480;
var series_2_color = '#00aaff';
var series_2_name = 'UVM30A';
// chart title
var chart_title = 'UV Sensors Zim / UVM30A';
// y axis title
var y_axis_title = 'UV Index';
// user's timezone offset
var my_offset = new Date().getTimezoneOffset();
// chart variable
var my_chart;
// when the document is ready
$(document).on('ready', function () {
// add a blank chart
addChart();
// add the first series
addSeries(series_1_channel_id, series_1_field_number, series_1_read_api_key, series_1_results, series_1_color, series_1_name);
// add the second series
addSeries(series_2_channel_id, series_2_field_number, series_2_read_api_key, series_2_results, series_2_color, series_2_name);
});
// add the base chart
function addChart() {
// variable for the local date in milliseconds
var localDate;
// specify the chart options
var chartOptions = {
chart: {
renderTo: 'chart-container',
defaultSeriesType: 'spline',
zoomType: 'x', // added here
panning: true,
panKey: 'shift',
backgroundColor: '#ffffff',
events: {
load: addSeries
}
},
title: {
text: chart_title
},
subtitle: {
text: 'Click and drag to zoom in. Hold down shift key to pan.'
},
plotOptions: {
series: {
marker: {
radius: 2
},
animation: true,
step: false,
borderWidth: 0,
turboThreshold: 0
}
},
scrollbar: {
enabled: true
// barBackgroundColor: 'gray',
// barBorderRadius: 7,
// barBorderWidth: 0,
// buttonBackgroundColor: 'gray',
// buttonBorderWidth: 0,
// buttonArrowColor: 'yellow',
// buttonBorderRadius: 7,
// rifleColor: 'yellow',
// trackBackgroundColor: 'white',
// trackBorderWidth: 1,
// trackBorderColor: 'silver',
// trackBorderRadius: 7
},
tooltip: {
// reformat the tooltips so that local times are displayed
formatter: function () {
var d = new Date(this.x + (my_offset * 60000));
var n = (this.point.name === undefined) ? '' : '<br>' + this.point.name;
return this.series.name + ':<b>' + this.y + '</b>' + n + '<br>' + d.toDateString() + '<br>' + d.toTimeString().replace(/\(.*\)/, "");
}
},
xAxis: {
type: 'datetime',
title: {
text: 'Date'
}
},
rangeSelector: {
enabled: true,
buttons: [{
type: 'minute',
count: 60,
text: 'Hour'
}, {
type: 'day',
count: 1,
text: 'Day'
}, {
type: 'week',
count: 1,
text: 'Week'
}, {
type: 'all',
text: 'All'
}]
},
yAxis: {
title: {
text: y_axis_title
}
},
exporting: {
enabled: true
},
legend: {
enabled: true
},
credits: {
text: 'ThingSpeak.com',
href: 'https://thingspeak.com/',
style: {
color: '#D62020'
}
}
};
// draw the chart
my_chart = new Highcharts.Chart(chartOptions);
}
// add a series to the chart
function addSeries(channel_id, field_number, api_key, results, color, name) {
var field_name = 'field' + field_number;
// get the data with a webservice call
$.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/fields/' + field_number + '.json?offset=0&round=2&results=' + results + '&api_key=' + api_key, function (data) {
// blank array for holding chart data
var chart_data = [];
// iterate through each feed
$.each(data.feeds, function () {
var point = new Highcharts.Point();
// set the proper values
var value = this[field_name];
point.x = getChartDate(this.created_at);
point.y = parseFloat(value);
// add location if possible
if (this.location) {
point.name = this.location;
}
// if a numerical value exists add it
if (!isNaN(parseInt(value))) {
chart_data.push(point);
}
});
// add the chart data
my_chart.addSeries({
data: chart_data,
name: name,
color: color
});
});
setTimeout(addSeries, 1000);
}
cache: false;
// converts date format from JSON
function getChartDate(d) {
// offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
return Date.parse(d) - (my_offset * 60000);
}
I have tried following the livedata example but seem to be failing miserably. The sensors update about every 60 seconds (only during the day - as there is no UV at night, I put the sensors into "sleep" mode to save battery power)