You can do that by setting animations.x.from and animations.y.from callbacks (docs); they are called for each point of the chart
so case has to be taken to identify the last datapoint that was just added.
const fromX = function(context){
if(!context.element){
return;
}
if(context.dataIndex === context.dataset.data.length -1){
const [prevValue] = context.chart.data.labels.slice(-2); // penultimate element
return context.chart.getDatasetMeta(context.datasetIndex)['xScale'].getPixelForValue(prevValue);
}
return context.element.x;
};
const fromY = function(context){
if(!context.element){
return;
}
if(context.dataIndex === context.dataset.data.length -1){
const [prevValue] = context.dataset.data.slice(-2); // penultimate element
return context.chart.getDatasetMeta(context.datasetIndex)['yScale'].getPixelForValue(prevValue);
}
return context.element.y;
};
and the configuration:
options: {
....... other options
animations: {
x: {
from: fromX
},
y: {
from: fromY
}
}
}
This works fine as long as the new data is inside the existing chart area; if it is not, and the chart will rescale with the new point, things get more complicated. We have to put the initial position of the new point using the initial axes scales, and its final position will be computed using the final scales. Here's a simple approach that starts the new point at halfway between the current and previous positions:
let labels = [1, 2, 3, 4, 5, 6, 7];
let data = [65, 59, 80, 81, 56, 55, 40];
const chartData = {
labels: labels,
datasets: [
{
label: "My First dataset",
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
data: data,
},
],
};
const fromX = function(context){
if(!context.element){
return;
}
if(context.dataIndex === context.dataset.data.length -1){
const [prevValue, lastValue] = context.chart.data.labels.slice(-2);
const scale = context.chart.getDatasetMeta(context.datasetIndex)['xScale'];
return (scale.getPixelForValue(prevValue) + scale.getPixelForValue(lastValue))/2;
}
return context.element.x;
}
const fromY = function(context){
if(!context.element){
return;
}
if(context.dataIndex === context.dataset.data.length -1){
const [prevValue, lastValue] = context.dataset.data.slice(-2); // penultimate element
const scale = context.chart.getDatasetMeta(context.datasetIndex)['yScale'];
return (scale.getPixelForValue(prevValue) + scale.getPixelForValue(lastValue))/2;
}
return context.element.y;
};
const options = {
responsive: true,
scales: {
x: {
display: true,
title: {
display: true,
text: "Time",
},
},
y: {
display: true,
title: {
display: true,
text: "Value",
},
},
},
animations: {
x: {
duration: 800,
from: fromX
},
y: {
duration: 800,
from: fromY
}
},
};
const chart = new Chart("canvas", {
type: "line",
data: chartData,
options: options,
});
let interval;
function startInterval(){
interval = setInterval(() => {
data.push(Math.random() * data.length + 1);
labels.push(labels.length + 1);
chart.update();
}, 2000);
}
addEventListener("DOMContentLoaded", () => {startInterval();});
document.querySelector('#reset').onclick = () => {
chart.data.datasets[0].data = [1];
chart.data.labels = [1];
data = chart.data.datasets[0].data;
labels = chart.data.labels;
chart.update()
};
document.querySelector('#stop').onclick = () => {
clearInterval(interval);
};
<div>
<button id="reset">reset</button> <button id="stop">stop</button>
<canvas id="canvas"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
and codesandbox fork
Note This code is based on code from what I think must have been a similar answer before, but for some reason I don't find it on SO; by the dates of my original code, it must have been for this post, which I also upvoted, but for some reason I must've not posted it. If that's wrong and I already posted something similar before, please let me know.