chart.js doughnut chart with links if onclick - javascript

I try to make a link if a onclick event on a doughnut chart slice happens. My datasources are 3 arrays with labels, value, and the id for the url.
HTML:
<canvas id="pie-chart" style='display: none;'></canvas>
<!-- Php Arrays to JS -> PIE-CHARTDATA -->
<script type="text/javascript">
var chartIds = [[12,14,17,18]];
var chartValues = [[208.09,296.86,634.975,972.808]];
var chartLabels = [["BTC","AAPL","MSFT","ETH"]];
</script>
JS:
if (chartValues.length != 0 ) {
document.getElementById("pie-chart").style.display= "block";
}
Chart.register(ChartDataLabels);
var chartValuesInt = [];
length = chartValues[0].length;
for (var i = 0; i < length; i++)
chartValuesInt.push(parseInt(chartValues[0][i]));
var data = [{
data: chartValuesInt,
chartIds,
backgroundColor: [
"#f38000",
"#5f44f5",
"#333333",
],
borderColor: "#000"
}];
var options = {
borderWidth: 4,
hoverOffset: 6,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false,
},
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value*100 / sum).toFixed(2)+"%";
return [ctx.chart.data.labels[ctx.dataIndex],
percentage,
'$' + value ] ;
},
textAlign: 'center',
color: '#fff',
borderRadius: 50,
padding:10,
labels: {
title: {
font: {
weight: 'bold',
size: '16px'
}
},
}
}
},
options:{
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
console.log("In click", datasetLabel, value);
//link to url with:[chartIds]
}
}
};
//IMAGE CENTER
const image = new Image();
image.src = 'img/pie-home2.png';
const plugin = {
id: 'custom_canvas_background_image',
beforeDraw: (chart) => {
if (image.complete) {
const ctx = chart.ctx;
const {top, left, width, height} = chart.chartArea;
const x = left + width / 2 - image.width / 2;
const y = top + height / 2 - image.height / 2;
ctx.drawImage(image, x, y);
} else {
image.onload = () => chart.draw();
}
}
};
var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: chartLabels[0],
datasets: data,
chartIds
},
options: options,
plugins: [plugin],
});
why does the onclick didn't work ?
how do i get the id with the right index from the slice where the event happens?
I searched already, but couldn't find a answer to these 2 questions.

You onClick function does not work because you define an options object within your options object and put the onClick in there. This is not supported. When you remove the inner options layer it will work:
const options = {
borderWidth: 4,
hoverOffset: 6,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false,
},
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value * 100 / sum).toFixed(2) + "%";
return [ctx.chart.data.labels[ctx.dataIndex],
percentage,
'$' + value
];
},
textAlign: 'center',
color: '#fff',
borderRadius: 50,
padding: 10,
labels: {
title: {
font: {
weight: 'bold',
size: '16px'
}
},
}
}
},
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
console.log("In click", datasetLabel, value);
//link to url with:[chartIds]
}
};

Related

How to add images to chart labels with vue-chartjs?

I want to add flag icons under the country code labels but am completely stuck.
Image of the chart with my current code
The images are named BR.svg, FR.svg and MX.svg and are located under #/assets/icons/flags/
I am using vue#2.6.12 and vue-chartjs#3.5.1 in my project. This is my Chart.vue component:
<script>
import { Bar } from 'vue-chartjs'
export default {
extends: Bar,
data: () => ({
chartdata: {
labels: ['BR', 'FR', 'MX'],
datasets: [
{
label: 'Lorem ipsum',
backgroundColor: '#AF78D2',
data: [39, 30, 30],
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
tooltips: {
"enabled": false
},
scales : {
xAxes : [ {
gridLines : {
display : false
}
} ],
yAxes: [{
ticks: {
beginAtZero: true,
suggestedMin: 0,
suggestedMax: 40,
stepSize: 5,
}
}]
},
"hover": {
"animationDuration": 0
},
"animation": {
"duration": 1,
"onComplete": function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
var data = dataset.data[index] + '%';
ctx.fillText(data, bar._model.x, bar._model.y - 5);
});
});
}
},
}
}),
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
</script>
This runnable code below is the closest to a solution I have come by hours of searching. But it still won't do the trick because I don't know how to integrate it with what I have.
const labels = ['Red Vans', 'Blue Vans', 'Green Vans', 'Gray Vans'];
const images = ['https://i.stack.imgur.com/2RAv2.png', 'https://i.stack.imgur.com/Tq5DA.png', 'https://i.stack.imgur.com/3KRtW.png', 'https://i.stack.imgur.com/iLyVi.png'];
const values = [48, 56, 33, 44];
new Chart(document.getElementById("myChart"), {
type: "bar",
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
xAxis.ticks.forEach((value, index) => {
var x = xAxis.getPixelForTick(index);
var image = new Image();
image.src = images[index],
ctx.drawImage(image, x - 12, yAxis.bottom + 10);
});
}
}],
data: {
labels: labels,
datasets: [{
label: 'My Dataset',
data: values,
backgroundColor: ['red', 'blue', 'green', 'lightgray']
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}],
xAxes: [{
ticks: {
padding: 30
}
}],
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>
When I have added the plugin code into my own code I get an error message saying 'plugins' is already defined in props, but I can't manage to use it somehow.
Anyone who knows how to implement this afterDraw plugin into my code? I appreciate any input.
Thanks a lot in advance! :)
In the mounted of your vue component you can call the addPlugin (has to be done before the renderChart method) like this:
this.addPlugin({
id: 'image-label',
afterDraw: (chart) => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
xAxis.ticks.forEach((value, index) => {
var x = xAxis.getPixelForTick(index);
var image = new Image();
image.src = images[index],
ctx.drawImage(image, x - 12, yAxis.bottom + 10);
});
}
})
Documentation: https://vue-chartjs.org/api/#addplugin
It works in ChartJS version 4.0.1. data needs to return 'plugins':
data() {
return {
plugins: [{
afterDraw: chart => {
const ctx = chart.ctx;
const xAxis = chart.scales['x'];
const yAxis = chart.scales['y'];
xAxis.ticks.forEach((value, index) => {
let x = xAxis.getPixelForTick(index);
ctx.drawImage(images[index], x - 12, yAxis.bottom + 10)
});
}
}],
data: {...
Please note that ctx should be chart.ctx and NOT chart.chart.ctx.. Similarly, it should be chart.scales['x'] and NOT chart.scales['x-axis-0'].
After you return plugins, this needs to be referenced in your Bar component like so..
<Bar :data="data" :options="options" :plugins="plugins"/>

Risers or bars of Dynamically built ChartJs from json not aligning to bottom of y axis

I have strange behaviour from bars or risers in a dynamically built Chartjs chart.
They don't begin at point 0 on the y-axis and not all of them show.
I have tried a variety of ways as found here or other forums, but no success.
Please help.
The code to build it is from json ex ajax to MVC Controller/Action.
chartSetup.jsonDataSets = new List<ChartDataset>();
ChartDataset jsonDataSets = new ChartDataset
{
data = "[408, 547, 675, 534]",
label = "Actual",
backgroundColor = "#8e5ea2"
};
chartSetup.jsonDataSets.Add(jsonDataSets);
jsonDataSets = new ChartDataset
{
data= "[350, 447, 725, 534]",
label = "Budgeted",
backgroundColor = "red"
};
chartSetup.jsonDataSets.Add(jsonDataSets);
If I hardcode particularly the datasets, for the chart, then no problem.
var type = 'bar';
var xLabels = ["May", "Jun", "Jul", "Aug", "Sept"];
var topTitle = { display: true, text: 'Maintenance Costs', fontStyle: 'bold', fontSize: 18, fontColor: 'white' };
var canvasTyreCostsChart = $("#canvasMaintenanceBar").get(0).getContext("2d");
var datasources = [
{
label: "Budgeted",
backgroundColor: 'red',
data: [133, 221, 783, 1078]
},
{
label: "Actual",
backgroundColor: "#8e5ea2",
data: [408, 547, 675, 734]
}
];
var regionalCountCostBar = new Chart(canvasTyreCostsChart,
{
type: type,
data: {
labels: xLabels,
datasets: datasources,
},
options:
{
responsive: true,
title: topTitle,
legend: { position: 'bottom' },
}
});
But done dynamically the bars float above the zero point on the y axis.
Here's the code.
datasources = jsonData['jsonDataSets'];
var dynamicChartCanvas = $("#canvas-" + columnID).get(0).getContext("2d");
var xLabelsArr = [];
$.each(xLabels, function (key, val)
{
var rec = xLabels[key];
xLabelsArr.push(rec['label']);
});
var label;
var backgroundColor;
var data2 = "";
var data3 = [];
var DS = [];
var dataSource2;
// Extract the individual elements
$.each(datasources, function (key, val)
{
var rec = datasources[key];
$.each(rec, function (key, val)
{
if (key == "label")
{
label = val;
}
if (key == "backgroundColor")
{
backgroundColor = val;
}
if (key == "data")
{
data2 = rec[key];
var xx = data2.replace("[", "").replace("]","").split(",");
data3.push(xx);
}
});
dataSource2 =
{
label: label,
backgroundColor: backgroundColor,
data: data3,
}
console.log("ds label " + JSON.stringify(label));
console.log("data3 " + JSON.stringify(data3));
data3 = [];
DS.push(dataSource2);
});
console.log("DS " + JSON.stringify(DS));
var tempData2 = {
labels: xLabelsArr,
datasets: DS,
};
var topTitle = { display: true, text: 'Maintenance Costs', fontStyle: 'bold', fontSize: 18, fontColor: 'white' };
var chart = new Chart(dynamicChartCanvas,
{
type: type,
data: tempData2,
options:
{
responsive: responsive,
title: topTitle,
legend: { position: 'bottom' },
}
});
The object array as logged:
DS [{"label":"Actual","backgroundColor":"#8e5ea2","data":[["408"," 547"," 675"," 534"]]},{"label":"Budgeted","backgroundColor":"red","data":[["350"," 447"," 725"," 534"]]}]
What was required, was a further iteration thru the JSON array in the data object, getting each value and building it into a js array.
if (key == "data")
{
var dataRec = rec[key];
var jsonData = JSON.parse(dataRec);
xValuesArray = [];
for (var i = 0; i < jsonData.length; i++)
{
var value = jsonData[i];
xValuesArray.push(value);
}
}
});
chartDataset =
{
label: label,
backgroundColor: backgroundColor,
data: xValuesArray,
}
chartDatasets.push(chartDataset);

How to make a dynamically growing data chart in Chart.JS?

I am trying to develop a Crash Game, where a multiplier (Y) increases exponentially and dynamically over time (X), causing the chart to re-render at each tick.
You can see an example of the chart game here
TL;DR: I am trying to achieve a "zoom-out" effect of the chart as my ticks increase in values (x,y).
Where my code fails is when ticks data values (x,y, respectively time and multiplier) surpass suggestedMax tick values. The only reason I am using suggestedMax is to have some labels on the chart at the beginning.
I have tried to achieve this by using both line and scatter chart type, but the final outcome is simply unacceptable from a performance point of view.
Here is my code:
const HomePlaygroundView = () => {
var chart = undefined;
const chartText = useRef(null);
let last_tick_received = 0;
const incrementChart = () => {
last_tick_received += 100;
};
const onServerTickReceived = (multiplier, msLapsed) => {
// Update chart multiplied
if (chart.data.datasets[0].data.length >= 100) {
// Halve the array to save performance (lol)
for (let i = 1; i < 100; i += 2) {
console.log("Reducing chart data");
chart.data.datasets[0].data.splice(i, 1);
}
}
chart.data.datasets[0].data.push({
x: msLapsed,
y: multiplier,
});
// This is basically my zoom out effect implementation...
if (multiplier >= 2.5) { // Increase suggestedMax only if bigger data needs to be fit
chart.options.scales.yAxes[0].ticks.suggestedMax = multiplier;
}
if (msLapsed > 9000) { // Same logic as above
chart.options.scales.xAxes[0].ticks.suggestedMax = msLapsed;
}
if (msLapsed < 10000) {
// Fit msLapsed in the pre-existing 10 seconds labels of x axis (this is a hell of a workaround)
let willInsertAtIndex = undefined;
for (let i = 0; i < chart.data.labels.length; i++) {
let current = chart.data.labels;
if (current < msLapsed) {
// Insert at i + 1? Check the next index if it's bigger than msLapsed
let nextVal = chart.data.labels[i + 1];
if (nextVal) {
if (nextVal > msLapsed) {
willInsertAtIndex = i + 1;
break;
}
} else {
willInsertAtIndex = i + 1;
break;
}
}
}
if (willInsertAtIndex) {
chart.data.labels.splice(willInsertAtIndex, 0, msLapsed);
}
} else {
chart.data.labels.push(msLapsed);
}
// Decimate data every so and so
chartText.current.innerText = `${multiplier}x`;
// Re-render canvas
chart.update();
};
useEffect(() => {
console.log("rendered chart");
var ctx = document.getElementById("myChart").getContext("2d");
ctx.height = "350px";
chart = new Chart(ctx, {
// The type of chart we want to create
type: "scatter",
// The data for our dataset
data: {
labels: [...Array(11).keys()].map((s) => s * 1000),
datasets: [
{
label: "testt",
backgroundColor: "transparent",
borderColor: "rgb(255, 99, 132)",
borderWidth: 10,
showLine: true,
borderJoinStyle: "round",
borderCapStyle: "round",
data: [
{
y: 1,
x: 0,
},
],
},
],
animation: {
duration: 0,
},
responsiveAnimationDuration: 100, // animation duration after a resize
},
// Configuration options go here
options: {
spanGaps: true,
events: [],
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
elements: {
point: {
radius: 0,
},
},
scales: {
xAxes: [
{
type: "linear",
ticks: {
callback: function (value, index, values) {
let s = Math.round(value / 1000);
return s.toString() + "s";
//return value;
},
autoSkipPadding: 100,
autoSkip: true,
suggestedMax: 10000,
stepSize: 100,
min: 0,
},
},
],
yAxes: [
{
ticks: {
// Include a dollar sign in the ticks
callback: function (value, index, values) {
return Math.round(value).toString() + "x"; // Display steps by 0,5
},
min: 1,
suggestedMax: 2.5,
stepSize: 0.01,
autoSkip: true,
autoSkipPadding: 150,
},
},
],
},
},
});
let lastTick = 1.0;
let dateStart = new Date().getTime();
setTimeout(() => {
chartText.current.innerText = "Go!";
setTimeout(() => {
setInterval(() => {
let timePassed = new Date().getTime() - dateStart;
//console.log(timePassed);
let calculateTick = Math.pow(
1.01,
0.00530133800509 * timePassed
).toFixed(2);
console.log(timePassed);
onServerTickReceived(calculateTick, timePassed);
}, 50);
}, 1000);
}, 2000);
});
const classes = useStyles();
return (
<div className={classes.canvasContainer}>
<span ref={chartText} className={classes.canvasText}>
Ready...?
</span>
<canvas id="myChart"></canvas>
</div>
);
};
export default HomePlaygroundView;

Chartjs - Insert additional data into chart tooltip

I'm trying to insert additional data into the doughnut chart.
The controller pass to the view an array like this:
[
0 => array:3 [
"profit" => 20
"sex" => array:3 [
0 => 0
1 => 8
2 => 0
]
"count" => 8
]
1 => array:3 [
"profit" => 101.5
"sex" => array:3 [
0 => 4
1 => 4
2 => 0
]
"count" => 8
]
...
]
Using chartjs and the fied profit of all array elements I create this doughnut chart:
But I would customize the content of the tooltip so that the datas of the "sex" fieds are visible. I try with the following code but the varible data contains only the values contained in the chart.
config.options.tooltips.callbacks = {
title: (tooltipItem, data) => {
return data['labels'][tooltipItem[0]['index']];
},
label: (tooltipItem, data) => {
return data['datasets'][0]['data'][tooltipItem['index']];
},
afterLabel: (tooltipItem, data) => {
var dataset = data['datasets'][0];
var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset._meta[4].total) * 100)
return `${percent} %`;
},
backgroundColor: '#FFF',
titleFontSize: 16,
titleFontColor: '#0066ff',
bodyFontColor: '#000',
bodyFontSize: 14,
displayColors: false
}
I pass the data in the config object in this way: config.data.datasets[0].data = data.map(el => el.profit);
How do I add more data to the tooltip to get something like this?
This is my code:
function createDonatsChart(ctx, title, data, labels, middleText, type) {
Chart.pluginService.register({
beforeDraw: function(chart) {
if (chart.config.options.elements.center) {
// Get ctx from string
const ctx = chart.chart.ctx;
// Get options from the center object in options
const centerConfig = chart.config.options.elements.center;
const fontStyle = centerConfig.fontStyle || 'Asap';
const txt = centerConfig.text;
const color = centerConfig.color || '#000';
const maxFontSize = centerConfig.maxFontSize || 75;
const sidePadding = centerConfig.sidePadding || 20;
const 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
const stringWidth = ctx.measureText(txt).width;
const elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated;
// Find out how much the font can grow in width.
const widthRatio = elementWidth / stringWidth;
const newFontSize = Math.floor(30 * widthRatio);
const elementHeight = (chart.innerRadius * 2);
// Pick a new font size so it will not be larger than the height of label.
const fontSizeToUse = Math.min(newFontSize, elementHeight, maxFontSize);
const minFontSize = centerConfig.minFontSize;
const lineHeight = centerConfig.lineHeight || 25;
const 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';
const centerX = ((chart.chartArea.left + chart.chartArea.right) / 2);
const centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2);
ctx.font = `${fontSizeToUse}px ${fontStyle}`;
ctx.fillStyle = color;
if (!wrapText) {
ctx.fillText(txt, centerX, centerY);
return;
}
const words = txt.split(' ');
let line = '';
let lines = [];
// Break words up into multiple lines if necessary
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = ctx.measureText(testLine);
const 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 (let n = 0; n < lines.length; n++) {
ctx.fillText(lines[n], centerX, centerY);
centerY += lineHeight;
}
//Draw text in center
ctx.fillText(line, centerX, centerY);
}
}
});
let config = {
type: 'doughnut',
data: {
datasets: [{
borderColor: '#121212',
borderWidth: 8,
backgroundColor: [
'#49C6E5',
'#EFC7C2',
'#00BD9D',
'#EF476F',
'#FFD166',
]
}],
labels: labels
},
options: {
responsive: true,
tooltips: {
},
legend: {
position: 'top',
onClick: null
},
title: {
display: true,
color: '#6c757d',
text: title,
fontFamily: "'Asap', san-serif",
fontSize: 20,
},
animation: {
animateScale: true,
animateRotate: true,
},
elements: {
center: {
text: middleText,
color: '#6c757d',
fontFamily: "'Asap', san-serif",
sidePadding: 20,
minFontSize: 12,
lineHeight: 25,
}
},
}
};
if ( type == 0 ) {
config.options.events = [];
config.data.datasets[0].data = data;
}
else {
// config.data.datasets[0].data = data.map(el => el.profit);
// config.options.tooltips.enabled = true;
// config.options.tooltips.callbacks = {
// title: (tooltipItem, data) => {
// return data['labels'][tooltipItem[0]['index']];
// },
// label: (tooltipItem, data) => {
// return data['datasets'][0]['data'][tooltipItem['index']];
// },
// afterLabel: (tooltipItem, data) => {
// var dataset = data['datasets'][0];
// var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset._meta[4].total) * 100)
// return `${percent} %`;
// },
// backgroundColor: '#FFF',
// titleFontSize: 16,
// titleFontColor: '#0066ff',
// bodyFontColor: '#000',
// bodyFontSize: 14,
// displayColors: false
// }
config.data.datasets[0].data = data.map(el => el.profit);
}
Chart.defaults.global.defaultFontFamily = 'Asap';
Chart.defaults.doughnut.cutoutPercentage = 80;
new Chart(ctx, config);
}
const data = [
{
count: 8,
profit: 20,
sex: [0, 8, 0]
},
{
count: 8,
profit: 101.5,
sex: [4, 4, 0]
},
{
count: 1,
profit: 12.5,
sex: [1, 0, 0]
},
{
count: 2,
profit: 4,
sex: [2, 0, 0]
},
{
count: 5,
profit: 56.5,
sex: [5, 0, 0]
}
];
createDonatsChart(
document.getElementById('profitPerTarget').getContext('2d'),
'Target (di chi compra)',
data,
['14-17', '18-24', '25-30', '31-40', 'Over 40'],
`Totale ${(data.map(el => el.profit).reduce((a, b) => a + b, 0))} \u20AC`,
1
);
html, body {
background-color: #121212;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<canvas id="profitPerTarget" height="500" style="padding: 10px"></canvas>
You can use closure in createDonatsChart functions. Set const as const originalData = [...data] and then you can access to data in afterLabel callback (as example):
tooltips: {
callbacks: {
afterLabel: function(tooltipItem, data) {
const sexArray = originalData[tooltipItem['index']].sex
const precent = sexArray.reduce((a, b) => a + b, 0) // your calculation here
return '(' + precent + '%)';
}
}
}
See example in playground: https://jsfiddle.net/denisstukalov/upw6asjm/63/#&togetherjs=3CN0LJDjbl

Chart js animating a line while changing x-axis labels

I achieved animating a plot using Jukka Kurkela example here.
Now I am having trouble customizing this plot further.
Logic of the custom plot
The plot starts animating with the x-axis labels being 0-20. When the plot reaches 20 then update the x-axis to be 20-40. Increment i or 20 until the x-axis reach its limit.
How to apply the logic above to the Example below?
// Generating data
var data = [];
var prev = 100;
for (var i=0;i<200;i++) {
prev += 5 - Math.random()*10;
data.push({x: i, y: prev});
}
var delayBetweenPoints = 100;
var started = {};
var ctx2 = document.getElementById("chart2").getContext("2d");
var chart2 = new Chart(ctx2, {
type: "line",
data: {
datasets: [
{
backgroundColor: "transparent",
borderColor: "rgb(255, 99, 132)",
borderWidth: 1,
pointRadius: 0,
data: data,
fill: true,
animation: (context) => {
var delay = 0;
var index = context.dataIndex;
if (!started[index]) {
delay = index * delayBetweenPoints;
started[index] = true;
}
var {x,y} = index > 0 ? context.chart.getDatasetMeta(0).data[index-1].getProps(['x','y'],
true) : {x: 0, y: 100};
return {
x: {
easing: "linear",
duration: delayBetweenPoints,
from: x,
delay
},
y: {
easing: "linear",
duration: delayBetweenPoints * 500,
from: y,
delay
},
skip: {
type: 'boolean',
duration: delayBetweenPoints,
from: true,
to: false,
delay: delay
}
};
}
}
]
},
options: {
scales: {
x: {
type: 'linear'
}
}
}
});
<div class="chart">
<canvas id="chart2"></canvas>
</div>
<script src="https://www.chartjs.org/dist/master/Chart.js"></script>
Solved it! Instead of incrementing 20 seconds, it is incrementing every 5 seconds ahead of time. Definitely a better experience for the user.
Got help from Rowf Abd's post.
var myData = [];
var prev = 100;
for (var i=0;i<60;i++) {
prev += 5 - Math.random()*10;
myData.push({x: i, y: prev});
}
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data: [myData[0]],
pointRadius: 0,
fill: false,
borderColor: "black",
lineTension: 0
}]
},
options: {
legend: {
onClick: (e) => e.stopPropagation()
},
title:{
fontColor: 'Black'
},
layout: {
padding: {
right: 10
}
},
scales: {
xAxes: [{
type: 'linear',
ticks: {
}
}],
yAxes: [{
scaleLabel: {
// fontFamily: 'Lato',
fontSize: 19,
fontColor: "Black"
}
}]
}
}
});
var next = function() {
var data = chart.data.datasets[0].data;
var count = data.length;
var xabsmin = 20;
var xabsmax = 60;
var incVar = 5;
data[count] = data[count - 1];
chart.update({duration: 0});
data[count] = myData[count];
chart.update();
if (count < myData.length - 1) {
setTimeout(next, 500);
}
if (data[count].x < xabsmin) {
chart.config.options.scales.xAxes[0].ticks.min = xabsmin - xabsmin;
chart.config.options.scales.xAxes[0].ticks.max = xabsmin;
chart.update();
}
if(data[count].x >= xabsmin && data[count].x < (xabsmax)){
var currentT = parseFloat(data[count].x);
var modDiv = (currentT % incVar);
var tempXMax = (currentT) + (incVar - modDiv);
chart.config.options.scales.xAxes[0].ticks.max = tempXMax;
chart.config.options.scales.xAxes[0].ticks.min = tempXMax - xabsmin;
chart.update();
}
}
setTimeout(next, 500);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart"></canvas>

Categories