Is there anyway to implement an animated indicator for chart.js doughnut charts? What I am looking to accomplish looks like this:
I've got the doughnut part of the chart complete, but can't seem to find a way to add the value (large text: 89% (dynamic)) or the dot for the indicator.
The code i've used is the following:
HTML
<canvas id="dashboardChart" width="400" height="400"></canvas>
JS
var ctx = document.getElementById("dashboardChart");
var dashboardChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Red", "Orange", "Green"],
datasets: [{
label: '# of Votes',
data: [33, 33, 33],
backgroundColor: [
'rgba(231, 76, 60, 1)',
'rgba(255, 164, 46, 1)',
'rgba(46, 204, 113, 1)'
],
borderColor: [
'rgba(255, 255, 255 ,1)',
'rgba(255, 255, 255 ,1)',
'rgba(255, 255, 255 ,1)'
],
borderWidth: 5
}]
},
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
legend: {
display: false
},
tooltip: {
enabled: false
},
cutoutPercentage: 95
}
});
Any help is greatly appreciated!
To get you started, I was able to get a half-doughnut, or how I call it, a speedometer graph, using the following options configuration:
var chart = new Chart(canvas, {
type: 'doughnut',
data: ['400', '200'],
options: {
rotation: -90,
circumference: 180,
}
chartjs version 3.5.1
Original Answer:
I'm not sure there is a quick and simple solution to this.
Perhaps you can create a second pie chart directly on top of the existing one and have that pie chart only have circumference for a few pixels but rotated at at X percent, in this case 89%. A bit of Maths is required to work out where 89% of half a circle is. this won't give you a nice circle marker as per your image above. It will give you a small coloured segment where that circle marker ought to be and with the help of some css this second pie chart segment can be rounded off to look like what you want.
The second pie chart may look like this ...
var ctx2 = document.getElementById("dashboardChart2");
var dashboardChart2 = new Chart(ctx2, {
type: 'doughnut',
data: {
labels: ["Purple"],
datasets: [{
label: '# of Votes',
data: [5],
backgroundColor: [
'rgba(159, 90, 253, 1)'
],
borderColor: [
'rgba(255, 255, 255 ,1)',
],
borderWidth: 2
}]
},
options: {
rotation: 1 * Math.PI,/** This is where you need to work out where 89% is */
circumference: 1 * Math.PI,/** put in a much smaller amount so it does not take up an entire semi circle */
legend: {
display: false
},
tooltip: {
enabled: false
},
cutoutPercentage: 95
}
});
As for the large 89% that will probably involve css. Positioning the text directly 'infront' of the pie chart (involving things like z-index and position absolutely)
New Answer:
Perhaps you can create a doughnut chart directly on top of the existing one and have that doughnut chart have its first and third 'bars' with an opacity of 0 so they can not be seen. If the first bar has a value of 88.5 and the second bar has a value of 1 and the third bar has a value of 10.5 you will have effectively put the second bar at 89%, with a width of 1% (88.5 + 1 + 10.5 = 100).
datasets: [{
data: [88.5, 1,10.5],// how much space each bar should take
backgroundColor: [
"rgba(0,0,0,0)", // bar 1: opacity 0
"rgba(255,255,255,1)", // bar 2 is white
"rgba(0,0,0,0)", // bar 3: opacity 0
],
borderColor: [
'rgba(0, 0, 0 ,0)',// bar 1 border opacity 0
'rgba(46, 204, 113, 1)',// bar 2 border is green
'rgba(0, 0, 0 ,0)'// bar 3 border opacity 0
],
borderWidth: 3
}]
As for the large 89% that will probably involve css. Positioning the text directly 'infront' of the pie chart
.percent {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
font-size: 80px;
bottom: 0;
}
After having a go in a fiddle I have this ...
Example Here:
https://jsfiddle.net/rjtsbeLc/3/
Note that with relative and absolute positioning I have placed the second doughnut chart on top of the existing one, and the percentage text on top of them at the bottom in the centre.
.outer {
position: relative;
width: 600px;
height: 400px;
}
canvas {
position: absolute;
}
It isn't quite what you are looking for as the 'circle' is rectangular but I came accross this, it might help you work out how to round off the rectangle into a circle ...
Chart.js Doughnut with rounded edges https://stackoverflow.com/a/36964890/5178301
Related
Note: I'm quite new to javascript and ChartJS, so I have done my best to look at similar questions but I'm unsure how to apply the solutions to my issue. I'm really quite confused by the plugins, and it seems whenever I try to literally copy and paste in solutions from other posted questions, it either breaks the graph or just doesn't do anything at all. So, with that in mind, I appreciate any help!
I'm trying to format the outer of two overlaid doughnut graphs using ChartJS so that the outer graph looks like a rounded bracket. The intention is to be able to group certain slices of the inner doughnut together, EG: Inner graph shows number of years John has lived in 5 different places, the outer graph is grouping the 2nd, 3rd, and 4th slice to indicate that he lived in these locations while he was a wildland forest firefighter for the US Forest Service.
See below. Please note, the tick marks on the edges are important. It should look like a rounded bracket -> ] <-
Things I have tried:
Doing borderWidth: { top: 1, right: 1, bottom: 0, left: 1 } breaks the graph, as I'm not sure there's a "bottom" persay to a doughnut graph
Trying to change borderColor breaks the graph in the same way
I'm not sure how to select just the cutout to do any custom styling, as I'm not well versed on HTML canvases and how they work.
Although I am currently attempting to remove one of the borders, I have also considered solutions like:
Adding an additional border to the inner doughnut chart and forcing it to clip over the innermost edge of the outer doughnut
adding a round, white background to the inner doughnut that clips over the innermost edge of the outer doughnut
Changing just the color of the innermost edge of the outer doughnut to transparent or white
Hiding the border entirely, letting the background color of the outer doughnut act like the spine of the rounded bracket I'm trying to create, then adding two new data segments to the outer doughnut at the beginning and end each with a different cutout percentage than the spine. (I hope that makes sense. Essentially the data would look like {1, 98, 1} with the cutout specified on each slice so it would look like cutout: {85, 90, 85}, in theory. This version is untested.)
Pardon the comments in the js, those are just there for me to remember what's going on.
var chart1 = document.getElementById('chart1').getContext('2d');
let doughnut1 = new Chart(chart1, {
type: 'doughnut',
data: {
datasets: [{ // OUTER ring
data: [100], //leave at 100
backgroundColor: ['#fff'],
circumference: 300, //determines circumference of outer border X out of 360
weight: 0.15,
radius: '100%',
borderColor: 'black',
borderWidth: 4
}, {
data: [14, 14, 22, 37, 13],
backgroundColor: ['#f5ce42', '#ccc3a3', '#fc95f2', '#cdb2ed', '#423225'],
radius: '95%',
borderColor: 'black',
borderAlign: 'inside'
}]
}
});
.wrap__chart {width: 50vw; margin: 0 auto;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<div class="wrap__chart">
<canvas id="chart1"></canvas>
</div>
Parameters for this graph are:
The graph has to be dynamic, cannot include any static images or the like as the data will be filled from a calculator
The graph must be responsive (Ignoring the fact that it largely isn't right now lol)
Can't use JQuery, unfortunately...
You can leave a single line instead of a rectangle by modifying the width property weight: 0.15 to weight: 0.001 and the border property borderWidth: 4 to borderWidth: 1. There is no other property you can change to make it more like what you want.
The new code:
var chart1 = document.getElementById('chart1').getContext('2d');
let doughnut1 = new Chart(chart1, {
type: 'doughnut',
data: {
datasets: [{ // OUTER ring
data: [100], //leave at 100
backgroundColor: ['#fff'],
circumference: 300, //determines circumference of outer border X out of 360
weight: 0.001,
radius: '100%',
borderColor: 'black',
borderWidth: 1
}, {
data: [14, 14, 22, 37, 13],
backgroundColor: ['#f5ce42', '#ccc3a3', '#fc95f2', '#cdb2ed', '#423225'],
radius: '95%',
borderColor: 'black',
borderAlign: 'inside'
}]
}
});
Ok I finally found a solution for my own question. It's quite roundabout, so I don't know if it's an actual answer for anyone else.
It involves using three graphs, unfortunately:
var sliceA = 60000,
sliceB = 24000,
sliceC = 36000;
var protected = (((sliceA + sliceB)/(sliceA + sliceC + sliceB)) * 360);
//colors
var outerRing = 'green';
var ctx = document.getElementById("chart");
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
datasets: [{
data: [sliceA, sliceB, sliceC],
backgroundColor: ['limegreen', 'skyblue', 'firebrick'],
radius: '82%',
cutout: '50%',
}]
}
});
var chart2 = document.getElementById('chart2').getContext('2d');
let doughnut2 = new Chart(chart2, {
type: 'doughnut',
data: {
datasets: [{ // OUTER ring
data: [1, 98, 1], //leave at 100
backgroundColor: [outerRing, 'transparent', outerRing],
circumference: (protected + 5), //determines circumference of outer border X out of 360
weight: 0.5,
radius: '100%',
borderWidth: 0,
borderAlign: 'inside',
borderColor: 'transparent',
rotation: -2
},
{ data: //14, 14, 22, 37, 13
[100],
backgroundColor: [outerRing],
radius: '97%',
cutout: '92%',
circumference: (protected + 2),
borderWidth: '5px',
borderColor: outerRing,
rotation: -1
}
]
},
options: {
parsing: {
key: 'nested.value'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<div class="container">
<canvas id="chart" style="position:absolute;"></canvas>
<canvas id="chart2" style="position:absolute;"></canvas>
</div>
it's not perfect, i'm still working out how to make the back rotation and addition to the variable "protected" in the outer rings responsive, with the intention of making it so that the inside of each tick mark aligns perfectly with 0 degrees and var protected degrees on the circle, to properly indicate that the outer ring encompasses all slices of the doughnut chart within the specified range.
you can change the outer ring style from a capital i to a [ style by changing the radius of each of the two layers of the outer ring. Switching the second dataset to be 100% and the first to be ~95% will change it accordingly.
I'm trying to display live data given the past day, week, month, etc. Currently, the graph displays only a section of live data but I'd like for more to be shown. I'm using a barchart for now, and I'm aware of the live data having the same time logged, but I wanted to see if there is a way for more than 20 points to be shown on the chart. I added the code that I think I should edit. I've tried ticks and bounds but it's a little difficult for me to navigate exactly what I want on the chart.js site.
Edit: after some fixes to my database, I can see more data points displayed. However, when I choose "past month", I am supposed to have every recorded point from the past month. Would a scatter plot with a line through it be more reasonable to display?
Below is the application test image and code block:
Application test image
var myChart = document.getElementById("chart").getContext('2d');
gradient = myChart.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(29, 140, 248, 0.75)');
gradient.addColorStop(0.5, 'rgba(29, 140, 248, 0.5)');
gradient.addColorStop(1, 'rgba(29, 140, 248, 0)');
// Global Options
Chart.defaults.global.defaultFontFamily = 'inherit';
Chart.defaults.global.defaultFontSize = 18; //Effects x and y's number font size
Chart.defaults.global.defaultFontColor = '#777';
barchart = new Chart(myChart, {
type:'bar',
data: {
labels: {{ data.x_vals | tojson }},
datasets:[{
label:'Liquid Level',
data: { { data.y_vals } },
backgroundColor: gradient,
borderWidth:1, //Effects plotted line on chart
borderColor:'white',
hoverBorderWidth:1,
hoverBorderColor:'white',
barThickness:15
}]
},
options: {
legend: {
display:true,
position:'right',
labels: { fontColor:'#000' }
},
scales: {
yAxes: [{
display: true,
ticks: {
suggestedMin: 0,
steps: 10,
stepValue: 10,
max: 100
}
}],
xAxes: [{
display: true,
ticks: {
autoSkip: true,
padding: 4,
fontSize: 12
}
}]
},
layout:{
padding:{
left:0,
right:0,
bottom:0,
top:0
}
}
}
});
In my project I need to plot, on web page, an ECG diagram.
My scenario is the following one: I have an ECG device connected to a mobile phone via bluetooth.
By using this device and relative SDK on Android I can plot diagram on my mobile by using data sent by the device.
On mobile device the heart wave is well plotted (by using device SDK for Android)
So far so good.
Generally speaking an ECG diagram is a diagram like this one
On server side I'm able in receiving data sent by the device and I'm using chartjs (version 2.9.3) in order to plot the diagram.
My result is this one:
As you can see it's not a clear ECG diagram (let's not focus on grid, I need to change them and use an image as background because in real ECG diagram the grid has x and y axis in millimeters)
By reading this diagram a doctor will think that basically it is a fibrillation heart diagram that is not real. I can't show the PQRST waves correctly.
I made the following assumptions:
on X axis I'll put the time (but I'm not sure this is correct..)
on Y axis I'll put the heart electric voltage measured by the ECG device and these measurements are correct
This is the code I wrote:
var chartColors = {
red: 'rgb(255, 0, 0)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)',
black:'rgb(0, 0, 0)'
};
var color = Chart.helpers.color;
var config = {
type: 'line',
data: {
//labels: [],
datasets:
[{
backgroundColor: color(chartColors.red).alpha(0.5).rgbString(),
borderColor: chartColors.black,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
}]
},
options: {
responsive: true,
legend: {
display: false
},
title: {
display: false,
text: ''
},
elements: {
point:{
radius: 0
}
},
scales: {
xAxes:
[{
ticks: {
display: false, //this will remove only the label
},
gridLines: {
color: "rgb(247, 174, 210)"
//color: "rgba(0, 0, 0, 0)"
},
type: 'time',
time:
{
unit: 'seconds',
unitStepSize: '1'
}
}],
yAxes: [{
gridLines: {
color: "rgb(247, 174, 210)"
//color: "rgba(0, 0, 0, 0)"
},
ticks: {
/*max: 500,
min: 0,*/
stepSize:1,
display: false //this will remove only the label
}
}]
},
tooltips: false,
hover:false
}
};
var context = $("#"+idEcg)[0].getContext('2d');
var ecgDiagram = new Chart(context, config);
var ecgDataFromServer = ..... //recover data from ecgDataFromServer.
//Note Every point has the following properties:
//tempo: it's the time when mobile device receives the measurement
//tensione: it's the heart electric voltage measured
for( var t = 0; t < ecgDataFromServer.length; t++ ){
var ecgDataPoint = ecgDataFromServer[t];
chart.config.data.datasets[0].data.push({
x: new Date(ecgDataPoint.tempo),
y: ecgDataPoint.tensione
});
}
chart.update({
preservation: true
});
I'm stucked on this diagram from several days and I can't figure how I can represent as I wish.
I can't find some formula that eventually allows to me to make a good data interpolation.
Now I'm evaluating to use d3.js as library (but I'm very new to it and I need to learn it; this is the reason I put d3.js tag as well).
I may also use some server side strategy (in java, matalab, R or any other) in order to generate a PNG o JPG image of the diagram and represents it.
Can anyone suggest to me if what I need it's feasible?
How can I plot my diagram like the ecgDiagram I showed?
Thank you
Angelo
This question already has answers here:
Chart.js Picture inside doughnut segment
(2 answers)
Closed 3 years ago.
I have draw a doughnut chart using Chart.js https://www.chartjs.org/docs/latest/charts/doughnut.html and i want to add image (i.e .png .svg) inside of every slice of doughnut chart as well as gradient background color.
this goal i want to achieve...image link here
html code here....
<canvas id="myChart" width="600" height="400"></canvas>
js code here
var ctx = $('#myChart');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Instagram: 35% ($38.21, 100 items)',
'Pinterest: 20% ($23.98, 69 items)',
'Direct: 15% ($17.77, 48 items)',
'Facebook: 12% ($13.54, 41 items)',
'Twitter: 10% ($11.02, 35 items)',
'youtube: 8% ($9.63, 21 items)'],
datasets: [{
data: [35, 20, 15, 12, 10, 8],
backgroundColor: [
'#ff0',
'#BD081C',
'#232323',
'#1877F2',
'#1DA1F2',
'#FF0000'
]
}]
},
options: {
rotation:-0.2 * Math.PI,
responsive: false,
scales: {
Axes: [{
gridLines: {
drawBorder: false,
},
}]
},
legend: {
display: true,
position: "right",
align: "center",
onClick: null,
fontSize: 30,
fullWidth:true,
labels: {
fontColor: '#000',
fontSize:16,
padding:30,
}
},
}
});
Do something like this
var ctx=myChart.width/2;
var cy=myChart.height/2;
ctx.translate(cx,cy);
var startAngle = myChart.segments[thisWedgeIndex].startAngle;
var endAngle = myChart.segments[thisWedgeIndex].endAngle;
var midAngle = startAngle+(endAngle-startAngle)/2;
ctx.rotate(midAngle);//mid angle rotation
//inner radious
var midWedgeRadius=myChart.innerRadius+(chart.radius-chart.innerRadius)/2;
context.translate(midWedgeRadius,0);
context.setTransform(1,0,0,1,0,0);//rotate
I am using chart.js library and I am having a issue. I want the years axis to start from 0. Right now its starting from the negative value(-50 in my case).
Currently its coming like this-
What I want-
What I am trying-
var config = {
type: 'bar',
data: {
labels: ["Year 0", "Year 1", "Year 2", "Year 3", "Year 4", "Year 5", "Year 6"],
datasets: [{
type: 'line',
label: 'Accumulative Flow',
data: [0, -50, 20, 30, 40, 50],
borderColor: 'red',
fill: false,
lineTension: 0,
borderJoinStyle: 'miter',
xAxes: [{
barPercentage: 0.4
}]
}, {
type: 'bar',
label: 'Benifit(One time)',
backgroundColor: "#005998",
data: [40, 50, 60, 80, 50, 60],
}, ]
},
options: {
title: {
display: true,
text: 'Custom Chart Title'
},
scales: {
xAxes: [{
time: {
displayFormats: {
quarter: ' YYYY'
}
},
beginAtZero: true,
barPercentage: 0.3,
id: 'x-axis-label',
position: 'bottom',
scaleStartValue: 20,
gridLines: {
display: false
},
}],
yAxes: [{
id: 'y-axis-label',
ticks: {
max: 300,
min: -50,
stepSize: 50,
},
position: 'left',
gridLines: {
display: false
},
}]
},
legend: {
position: 'right'
},
maintainAspectRatio: false,
scaleBeginAtZero: true
}
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
.GraphContain {
max-height: 500px;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.0/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<div class="GraphContain">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
Is there any parameter to do this ?
Thanks In advance!
After reading thru the core scale source, I was able to come up with a way to configure your chart to get pretty dang close to your desired behavior.
It requires that we manually set gridline colors as well as manipulate the internal scale instance ticks array (which is used by the scale draw method to paint on the canvas).
First, in order to hide some gridlines but show others, I used the gridLines.color property and passed in an array of colors where the first index color is the default gridline color and all others are white (the first index is used to color the "zero" gridline). Note, since we are later going to manipulate the internal scale ticks array, you must add an extra index to the color array with a color of white. Here is an example of what I mean.
gridLines: {
// since we only want to show the "zero line" gridline (chart.js
// uses the color of the first index to paint this line), we
// set the first index to the default color and all others to white
// note, later we add a "dummy" tick (explained later) so the length
// of this array has to be 1 greater than the number of gridlines you
// want displayed
color: [
"rgba(0, 0, 0, 0.1)", // this is for the zero line
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",],
}
}
Next, we use the afterTickToLabelConversion scale callback configuration property to manipulate the internal scale ticks array to force the gridlines to display like you want. Here is the implementation that works for your use case.
// we will use this callback to manipulate the ticks array
// before they are drawn on the canvas
afterTickToLabelConversion: function(scaleInstance) {
// the top gridline (which is at index 0) is always shown
// so to overcome this we add a "dummy" tick at index 0
scaleInstance.ticks.unshift(null);
// we have to do the same this to this tick array as well
// because it uses the values in this array to map to the data
scaleInstance.ticksAsNumbers.unshift(null);
// since we have added an extra tick, we need to move the
// zero line index to point to the new index of 0
scaleInstance.zeroLineIndex++
}
So putting it all together you end up with a chart that looks like this.
Check out this codepen for a working example (note, I left the other attempts to solve this...so see option 3 at the bottom).