I'm currently having a problem which seems to be well known already, but of the answer provided none have worked so far for me.
Whenever I'm hovering over the line in a linegraph, the chart changes back to a chart displayed before as shown in the GIF below:
https://i.stack.imgur.com/cmknf.gif
The solution which seemed the best to me so far is destroying the chart before creating a new one if it's not the first set of data. (bool 'first' and chart 'pauzeAmountChart' are global variables)
function setupAvgPauseGraph(currVideo, currData) {
if (!first) {
pauzeAmountChart.destroy();
}
first = false;
var ctx = document.getElementById("pauzeduurChart");
...
avgPauseChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Gemiddelde pauzeduur / seconde',
data: data,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [
{ticks: {min: 0, stepSize: step}}],
xAxes: [
{scaleLabel: {display: true, labelString: 'Seconden in video'}}]
},
elements: {point: {radius: 0}}
}
});
}
However, when it's not the first dataset, thus getting to the line 'pauzeAmountChart.destroy()' AND which means the chart already has been initialized and filled with data before, I get this error:
http://prntscr.com/e57x4a
I'm unfortunately not sure what this error means, and Google didn't help me any further either.
Also, when I keep switching between the data the error appears again + 1 more time compared to the previous time the error appeared. Which means it's not actually destroying the graph? (So try #x to show the data gives x errors)
Please let me know if you need any more information.
Thank you in advance!
[EDIT]
Thanks to Guus Punt it's fixed! With a few adjustments, it works now. See below for solution:
function setupPauzeAmountGraph(currVideo, currData) {
var ctx = document.getElementById("aantalPauzesChart");
...
if (pauzeAmountChart != undefined) {
pauzeAmountChart.chart.config.data.labels = labels;
pauzeAmountChart.chart.config.data.datasets[0].data = data;
pauzeAmountChart.update();
} else {
pauzeAmountChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Aantal pauzes / seconde',
data: data,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [
{ticks: {min: 0, stepSize: step}}],
xAxes: [
{scaleLabel: {display: true, labelString: 'Seconden in video'}}]
},
elements: {point: {radius: 0}}
}
});
}
}
I would need to run your example to be sure but maybe the following would help.
Instead of destroying the chart you can add and remove datasets,
Remove dataset before adding new data
data.labels = []; //Empty label array
data.datasets.splice(0, 1);//Removes first dataset
Add new dataset and labels
Update chart
pauzeAmountChart.update();
I have a working example which retrieves new data from a webapi and updates every 10 seconds, if this isnt sufficient
Related
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
I have a template that, among other things, displays a current market price through a placeholder, i.e., {{ market.current_price }}, and an accompanying price graph using ChartJS, rendered in the template in a canvas tag, <canvas id="myChart"></canvas>.
My question: since the market price changes as different users engage with the underlying market, is there a straightforward way to periodically (say, every 10 seconds) refresh the placeholder and chart in the template, to make sure each user is looking at the most recent price/graph?
I'm trying to avoid having to refresh the entire page, if at all possible.
If it makes any difference, the chart is rendered through Ajax, as follows:
{% block jquery %}
var endpoint = "{% url 'chart_data' market.id %}"
var defaultData = []
var labels = []
$.ajax({
method: "GET",
url: endpoint,
success: function(data){
defaultData = data.prices
price_array_length = defaultData.length + 1
labels = data.price_dates
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets : [{
label: 'Market price',
data: defaultData,
backgroundColor: [
'rgba(54, 162, 235, 0.2)',
],
borderColor: [
'rgba(54, 162, 235, 1)',
],
borderWidth: 2
}]
},
options: {
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 500 // general animation time
},
hover: {
animationDuration: 500 // duration of animations when hovering an item
},
responsiveAnimationDuration: 500 // animation duration after a resize
}
})
}
})
{% endblock %}
Many thanks in advance for any advice!
Here's one solution: Building on #MichaelCacciano's suggestion, I wrapped the JQuery in a function (refresh_graph), and also dropped the Django placeholder in favour of a tag (currentPrice), both of which now update every second, as follows:
<script>
function refresh_graph() {
{% block jquery %}
var endpoint = "{% url 'chart_data' market.id %}"
var defaultData = []
var labels = []
$.ajax({
method: "GET",
url: endpoint,
success: function(data){
defaultData = data.prices
price_array_length = defaultData.length + 1
labels = data.price_dates
var current = defaultData[defaultData.length-1];
document.getElementById('currentPrice').innerHTML = current;
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets : [{
label: 'Market price',
data: defaultData,
backgroundColor: [
'rgba(54, 162, 235, 0.2)',
],
borderColor: [
'rgba(54, 162, 235, 1)',
],
borderWidth: 2
}]
},
options: {
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 0 // general animation time
},
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsiveAnimationDuration: 500 // animation duration after a resize
}
})
}
})
setTimeout(refresh_graph, 1000);
}
setTimeout(refresh_graph, 0);
{% endblock %}
</script>
When I use a left and right line plot in Chartjs, I sometimes get inconsistent Y Axis tick interval counts. So, I might have like 7 intervals on the left, and Chartjs automatically might put 10 on the right. An example of a hard-to-read chart would look like this:
Therefore, the question is -- how do I set the Y Axis tick interval on the right so that it is consistent with the left?
When defining the options.scales.yAxes[1] (the right Y axis), add a beforeUpdate callback so that you can tweak its stepSize, like so:
beforeUpdate: function(scale) {
// get the max data point on the right
var nMax = Math.max.apply(Math,scale.chart.config.data.datasets[1].data);
// Get the count of ticks on the left that Chartjs automatically created.
// (Change the 'Clicks' to the 'id' property of that left Y Axis.)
var nLeftTickCount = scale.chart.scales['Clicks'].ticks.length;
// Add some exception logic so that we don't go less than 7 (a failsafe).
// Also, we need the count of spaces between the ticks,
// not the count of total ticks.
nLeftTickCount = (nLeftTickCount < 7) ? 7 : nLeftTickCount - 1;
// compute our tick step size
var nStepSize = nMax / nLeftTickCount;
// Assign the right Y Axis step size.
scale.chart.options.scales.yAxes[1].ticks.stepSize = nStepSize;
return;
}
This creates a consistent chart like so:
Here is the entire example of the area chart with a left and right Y Axis:
<script src="vendor/chartjs/chart.js/dist/Chart.min.js"></script>
<div class="chart-container">
<canvas id="my-canvas" width="400" height="200" style="width:100%;"></canvas>
</div>
<script>
var tsCanvas = document.getElementById('my-canvas');
var tsChart = new Chart(tsCanvas, {
type: 'line',
data: {
labels: ["Feb 1","Feb 16","Mar 1","Mar 16","Mar 22"],
datasets: [
{
label: 'Clicks',
yAxisID: 'Clicks',
data: [10706, 12847, 11516, 10464, 1204],
backgroundColor: 'rgba(26, 187, 156, 0.2)',
borderColor: 'rgba(26, 187, 156, 1)',
pointBackgroundColor: 'rgba(26, 187, 156, 1)',
borderWidth: 0.5,
pointRadius:2,
tension:0
},
{
label: 'Revenue',
yAxisID: 'Revenue',
data: [106.66, 342.86, 313.67, 461.18, 25.84],
backgroundColor: 'rgba(90, 144, 197, 0.2)',
borderColor: 'rgba(90, 144, 197, 1)',
pointBackgroundColor: 'rgba(90, 144, 197, 1)',
borderWidth: 0.5,
pointRadius:2,
tension:0
}
]
},
options: {
maintainAspectRatio:false,
hover: {
animationDuration:0
},
tooltips: {
mode: 'index',
multiKeyBackground: 'rgba(255,255,255,0.55)'
},
scales: {
yAxes: [
{
id: 'Clicks',
type: 'linear',
position: 'left',
scaleLabel: {
display:true,
labelString: 'Clicks'
},
ticks: {
beginAtZero:true
}
},
{
beforeUpdate: function(scale) {
var nMaxRev = Math.max.apply(Math,scale.chart.config.data.datasets[1].data);
var nLeftTickCount = scale.chart.scales['Clicks'].ticks.length;
nLeftTickCount = (nLeftTickCount < 7) ? 7 : nLeftTickCount - 1;
var nTickInterval = nMaxRev / nLeftTickCount;
scale.chart.options.scales.yAxes[1].ticks.stepSize = nTickInterval;
return;
},
id: 'Revenue',
type: 'linear',
position: 'right',
scaleLabel: {
display:true,
labelString: 'Revenue'
},
ticks: {
beginAtZero:true
}
}
],
xAxes: [
{
type: 'category',
ticks: {
minRotation:50,
maxRotation:50
}
}
]
}
}
});
</script>
I've got problems with Chart.js because the bars aren't starting at zero even though I set the beginAtZero argument.
I tried multiple ways for the right position of the code, but it doesn't seem to work.
Does anyone have an idea of how to fix my problem?
Here is the code:
var chartdata = {
labels: answer,
datasets: [{
label: 'Count',
backgroundColor: 'rgba(200, 200, 200, 0.75)',
borderColor: 'rgba(200, 200, 200, 0.75)',
hoverBackgroundColor: 'rgba(200, 200, 200, 1)',
hoverBorderColor: 'rgba(200, 200, 200, 1)',
data: count
}
]
};
var options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
};
var ctx = $("#mycanvas");
var barGraph = new Chart(ctx, {
type: 'bar',
data: chartdata
options: options
});
It looks like you are using Chart.js v1 instead of v2.
First, update your the Chart.js code to v2, preferably the latest version.
Then, change the following code:
var barGraph = new Chart(ctx, {
type: 'bar',
data: chartdata
options: options
});
to:
var barGraph = Chart.Bar(ctx, {
data: chartdata,
options: options
});
beginAtZero: true should then work as expected.
Working JSFiddle: https://jsfiddle.net/ko54hp6n/
For an alternative to beginAtZero: true, you can also use the min: 0 ticks configuration option to start the bar axis at zero:
var options = {
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
Working JSFiddle: https://jsfiddle.net/ko54hp6n/1/
I've got a question regard Chart.js.
I've drawn multiple piecharts using the documentation provided. I was wondering if on click of a certain slice of one of the charts, I can make an ajax call depending on the value of that slice?
For example, if this is my data
var data = [
{
value: 300,
color:"#F7464A",
highlight: "#FF5A5E",
label: "Red"
},
{
value: 50,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Green"
},
{
value: 100,
color: "#FDB45C",
highlight: "#FFC870",
label: "Yellow"
}
],
is it possible for me to click on the Red labelled slice and call a url of the following form:
example.com?label=red&value=300? If yes, how do I go about this?
Update: As #Soham Shetty comments, getSegmentsAtEvent(event) only works for 1.x and for 2.x getElementsAtEvent should be used.
.getElementsAtEvent(e)
Looks for the element under the event point, then returns all elements
at the same data index. This is used internally for 'label' mode
highlighting.
Calling getElementsAtEvent(event) on your Chart instance passing an
argument of an event, or jQuery event, will return the point elements
that are at that the same position of that event.
canvas.onclick = function(evt){
var activePoints = myLineChart.getElementsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
Example: https://jsfiddle.net/u1szh96g/208/
Original answer (valid for Chart.js 1.x version):
You can achieve this using getSegmentsAtEvent(event)
Calling getSegmentsAtEvent(event) on your Chart instance passing an
argument of an event, or jQuery event, will return the segment
elements that are at that the same position of that event.
From: Prototype Methods
So you can do:
$("#myChart").click(
function(evt){
var activePoints = myNewChart.getSegmentsAtEvent(evt);
/* do something */
}
);
Here is a full working example:
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.2.js"></script>
<script type="text/javascript" src="Chart.js"></script>
<script type="text/javascript">
var data = [
{
value: 300,
color:"#F7464A",
highlight: "#FF5A5E",
label: "Red"
},
{
value: 50,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Green"
},
{
value: 100,
color: "#FDB45C",
highlight: "#FFC870",
label: "Yellow"
}
];
$(document).ready(
function () {
var ctx = document.getElementById("myChart").getContext("2d");
var myNewChart = new Chart(ctx).Pie(data);
$("#myChart").click(
function(evt){
var activePoints = myNewChart.getSegmentsAtEvent(evt);
var url = "http://example.com/?label=" + activePoints[0].label + "&value=" + activePoints[0].value;
alert(url);
}
);
}
);
</script>
</head>
<body>
<canvas id="myChart" width="400" height="400"></canvas>
</body>
</html>
Using Chart.JS version 2.1.3, answers older than this one aren't valid anymore.
Using getSegmentsAtEvent(event) method will output on console this message:
getSegmentsAtEvent is not a function
So i think it must be removed. I didn't read any changelog to be honest. To resolve that, just use getElementsAtEvent(event) method, as it can be found on the Docs.
Below it can be found the script to obtain effectively clicked slice label and value. Note that also retrieving label and value is slightly different.
var ctx = document.getElementById("chart-area").getContext("2d");
var chart = new Chart(ctx, config);
document.getElementById("chart-area").onclick = function(evt)
{
var activePoints = chart.getElementsAtEvent(evt);
if(activePoints.length > 0)
{
//get the internal index of slice in pie chart
var clickedElementindex = activePoints[0]["_index"];
//get specific label by index
var label = chart.data.labels[clickedElementindex];
//get value by index
var value = chart.data.datasets[0].data[clickedElementindex];
/* other stuff that requires slice's label and value */
}
}
Hope it helps.
Chart.js 2.0 has made this even easier.
You can find it under common chart configuration in the documentation. Should work on more then pie graphs.
options:{
onClick: graphClickEvent
}
function graphClickEvent(event, array){
if(array[0]){
foo.bar;
}
}
It triggers on the entire chart, but if you click on a pie the model of that pie including index which can be used to get the value.
Working fine chartJs sector onclick
ChartJS : pie Chart - Add options "onclick"
options: {
legend: {
display: false
},
'onClick' : function (evt, item) {
console.log ('legend onClick', evt);
console.log('legd item', item);
}
}
I was facing the same issues since several days, Today i have found the solution. I have shown the complete file which is ready to execute.
<html>
<head><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js">
</script>
</head>
<body>
<canvas id="myChart" width="200" height="200"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
},
onClick:function(e){
var activePoints = myChart.getElementsAtEvent(e);
var selectedIndex = activePoints[0]._index;
alert(this.data.datasets[0].data[selectedIndex]);
}
}
});
</script>
</body>
</html>
If using a Donught Chart, and you want to prevent user to trigger your event on click inside the empty space around your chart circles, you can use the following alternative :
var myDoughnutChart = new Chart(ctx).Doughnut(data);
document.getElementById("myChart").onclick = function(evt){
var activePoints = myDoughnutChart.getSegmentsAtEvent(evt);
/* this is where we check if event has keys which means is not empty space */
if(Object.keys(activePoints).length > 0)
{
var label = activePoints[0]["label"];
var value = activePoints[0]["value"];
var url = "http://example.com/?label=" + label + "&value=" + value
/* process your url ... */
}
};
If you are using TypeScript, the code is a little funky because there is no type inference, but this works to get the index of the data that has been supplied to the chart:
// events
public chartClicked(e:any):void {
//console.log(e);
try {
console.log('DS ' + e.active['0']._datasetIndex);
console.log('ID ' + e.active['0']._index);
console.log('Label: ' + this.doughnutChartLabels[e.active['0']._index]);
console.log('Value: ' + this.doughnutChartData[e.active['0']._index]);
} catch (error) {
console.log("Error In LoadTopGraph", error);
}
try {
console.log(e[0].active);
} catch (error) {
//console.log("Error In LoadTopGraph", error);
}
}
To successfully track click events and on what graph element the user clicked, I did the following in my .js file I set up the following variables:
vm.chartOptions = {
onClick: function(event, array) {
let element = this.getElementAtEvent(event);
if (element.length > 0) {
var series= element[0]._model.datasetLabel;
var label = element[0]._model.label;
var value = this.data.datasets[element[0]._datasetIndex].data[element[0]._index];
}
}
};
vm.graphSeries = ["Series 1", "Serries 2"];
vm.chartLabels = ["07:00", "08:00", "09:00", "10:00"];
vm.chartData = [ [ 20, 30, 25, 15 ], [ 5, 10, 100, 20 ] ];
Then in my .html file I setup the graph as follows:
<canvas id="releaseByHourBar"
class="chart chart-bar"
chart-data="vm.graphData"
chart-labels="vm.graphLabels"
chart-series="vm.graphSeries"
chart-options="vm.chartOptions">
</canvas>
var ctx = document.getElementById('pie-chart').getContext('2d');
var myPieChart = new Chart(ctx, {
// The type of chart we want to create
type: 'pie',
});
//define click event
$("#pie-chart").click(
function (evt) {
var activePoints = myPieChart.getElementsAtEvent(evt);
var labeltag = activePoints[0]._view.label;
});
You can add in the options section an onClick function, like this:
options : {
cutoutPercentage: 50, //for donuts pie
onClick: function(event, chartElements){
if(chartElements){
console.log(chartElements[0].label);
}
},
},
the chartElements[0] is the clicked section of your chart, no need to use getElementsAtEvent anymore.
It works on Chart v2.9.4
I have an elegant solution to this problem. If you have multiple dataset, identifying which dataset was clicked gets tricky. The _datasetIndex always returns zero.
But this should do the trick. It will get you the label and the dataset label as well.
Please note ** this.getElementAtEvent** is without the s in getElement
options: {
onClick: function (e, items) {
var firstPoint = this.getElementAtEvent(e)[0];
if (firstPoint) {
var label = firstPoint._model.label;
var val = firstPoint._model.datasetLabel;
console.log(label+" - "+val);
}
}
}
Within options place your onclick and call the function you need as an example the ajax you need, I'll leave the example so that every click on a point tells you the value and you can use it in your new function.
options: {
plugins: {
// Change options for ALL labels of THIS CHART
datalabels: {
color: 'white',
//backgroundColor:'#ffce00',
align: 'start'
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fontColor: "white"
},gridLines: {
color: 'rgba(255,255,255,0.1)',
display: true
}
}],
xAxes: [{
ticks: {
fontColor: "white"
},gridLines: {
display: false
}
}]
},
legend: {
display: false
},
//onClick: abre
onClick:function(e){
var activePoints = myChart.getElementsAtEvent(e);
var selectedIndex = activePoints[0]._index;
alert(this.data.datasets[0].data[selectedIndex]);
}
}