I'm trying to create a Chart with Chart.js. I Got everything to work as I want it to, only thing left is that the Animation always floats from the bottom up into position. I just want a Fade-In animation, but couldn't find anything on how to do this.
Is it even possible to do a Fade Animation in Chart.js? Here is my Code so far:
#code
EDIT:
I got v3 to work, but a function, I created for this Chart doesn't work properly now. I start the Chart Animation 1s after its scrolled in view. By that time the Line chart is hidden and only the bar charts appear. After another 1.5s the Line is supposed to fade in on its spot.
I used to do this by giving the Line hidden at first and after a delay unhide it and update the chart. Updating the Chart now resets the Bar animation too. In v2 it didn't do this. Is there a way to play the animations shortly after another?
Also due to the Update the Plugin Datalabels doesn't work anymore. Is there a alternative in v3?
Here is my Code again:
<div class="chartcontainer">
<div class="chartcontainer--inner">
<script src="https://unpkg.com/chart.js#3.0.0-beta/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-deferred"></script>
<style type="text/css">
#media screen and (min-width: 1100px) {
#myChart {min-height: 540px}
}
.chartcontainer {max-width: 67.625rem; margin-right: auto; margin-left: auto; overflow: hidden;}
#media screen and (max-width: 700px){
.chartcontainer--inner {width: 50rem;}
.chartcontainer {overflow: scroll;}
}
</style>
<canvas id="myChart"></canvas>
<script type="text/javascript">
var inView = false;
function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemTop <= docViewBottom) && (elemBottom >= docViewTop));
}
$(window).scroll(function() {
if (isScrolledIntoView('#myChart') && !inView) {
// if (inView) { return; }
inView = true;
console.log('in View: '+ inView);
setTimeout(function(){
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['2018', '2019', '2020'],
datasets: [{
label: ['a'],
data: [7.1, 6.6, 6.6],
backgroundColor: [
'#05368b',
'#05368b',
'#05368b'
],
order: 2
},{
label: ['b'],
data: [6.5, 6.4, 6.7],
backgroundColor: [
'#1792d7',
'#1792d7',
'#1792d7'
],
order: 2
},{
label: ['c'],
data: [6.1, 5.8, 6.1],
backgroundColor: [
'#6c2475',
'#6c2475',
'#6c2475'
],
order: 2
},{
label: ['d'],
data: [5.7, 5.8, 5.8],
backgroundColor: [
'#0d60b5',
'#0d60b5',
'#0d60b5'
],
order: 2
},{
label: ['e'],
data: [4.3, 6.1, 6.3],
type: 'line',
lineTension: 0.4,
borderColor: "#f48d3b",
fill: false,
pointBackgroundColor: function() {
var color = "rgba(244, 141, 59, 1)";
console.log(color);
return color;
},
pointRadius: function(context) {
var width = context.chart.width;
var size = Math.round(width / 42);
console.log(size);
return size
},
borderWidth: 1,
order: 1,
hidden: true,
borderWidth: 5
}]
},
options: {
tooltips: {enabled: false},
hover: {mode: null},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
color: '#eee'
}
}],
xAxes: [{
gridLines: {
color: '#eee'
}
}]
},
legend: {
position: 'bottom',
labels: {
padding: 20
}
},
plugins: {
datalabels: {
align: 'top',
anchor: 'start',
color: 'white',
// offset: 6,
font: function(context) {
var width = context.chart.width;
var size = Math.round(width / 42);
return {
size: size
};
}
}
}
}
});
}, 1000);
setTimeout(function(){
console.log(myChart.config.data.datasets[4].hidden);
myChart.config.data.datasets[4].hidden = false;
myChart.config.options.animation = {numbers: { duration: 0 },colors: {type: "color",duration: 1000,from: "transparent",}};
myChart.update();
}, 2500);
} else {
// inView = false;
}
});
</script>
</div>
</div>
For Chart.js v3 you can use the following animation options:
chart.options.animation = {
numbers: { duration: 0 },
colors: {
type: "color",
duration: 1000,
from: "transparent",
}
}
For v2 I don't think they provide any fine-grain control over animations. If this is important to you, you might want to update to v3.
Snippet attached to show the effect.
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['2018', '2019', '2020'],
datasets: [{
label: ['x'],
data: [7.1, 6.6, 6.6],
backgroundColor: [
'#05368b',
'#05368b',
'#05368b'
],
order: 2
},{
label: ['y'],
data: [6.5, 6.4, 6.7],
backgroundColor: [
'#1792d7',
'#1792d7',
'#1792d7'
],
order: 2
},{
label: ['z'],
data: [6.1, 5.8, 6.1],
backgroundColor: [
'#6c2475',
'#6c2475',
'#6c2475'
],
order: 2
},{
label: ['n'],
data: [5.7, 5.8, 5.8],
backgroundColor: [
'#0d60b5',
'#0d60b5',
'#0d60b5'
],
order: 2
},{
label: ['av'],
data: [4.3, 6.1, 6.3],
type: 'line',
lineTension: 0.4,
borderColor: "#f48d3b",
fill: false,
pointBackgroundColor: function() {
var color = "rgba(244, 141, 59, 1)";
console.log(color);
return color;
},
pointRadius: function(context) {
var width = context.chart.width;
var size = Math.round(width / 42);
console.log(size);
return size
},
borderWidth: 1,
order: 1,
hidden: true,
borderWidth: 5
}]
},
options: {
animation: {
numbers: { duration: 0 },
colors: {
type: "color",
duration: 1000,
from: "transparent",
},
},
tooltips: {enabled: false},
hover: {mode: null},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
color: '#eee'
}
}],
xAxes: [{
gridLines: {
color: '#eee'
}
}]
},
legend: {
position: 'bottom',
labels: {
padding: 20
}
},
plugins: {
datalabels: {
align: 'top',
anchor: 'start',
color: 'white',
// offset: 6,
font: function(context) {
var width = context.chart.width;
var size = Math.round(width / 42);
return {
size: size
};
}
}
}
}
});
<script src="https://unpkg.com/chart.js#3.0.0-beta/dist/chart.min.js"></script>
<canvas id="myChart"></canvas>
I Got it to work as intended! I went back to v2, because v3 brought too much trouble with it.
While tinkering with v3 I realized that my v2 Version worked, because Updating in v2 doesn't reset the Animation. So my answer is based on that. If you call .update(0) the update happens without the Animation. So I crated this:
setTimeout(function(){
myChart.config.data.datasets[4].hidden = false;
myChart.update(0);
fadeLoop();
}, 2500);
var i = 0.15;
function fadeLoop() {
setTimeout(function() {
myChart.config.data.datasets[4].pointBackgroundColor = "rgba(244, 141, 59, " + i +")";
myChart.config.data.datasets[4].borderColor = "rgba(244, 141, 59, " + i +")";
myChart.update(0);
i += 0.05;
i = Math.round(i * 100) / 100;
if (i <= 1) fadeLoop();
}, 10)
}
Since my Line Fade-in Animation was supposed to start after the Bar animation I initialized the with the hidden attribute. After 2.5 sec I removed the hidden attribute and started the FadeLoop.
I count i from 0.15 to 1 in 0.05 increments every 0.01 sec. In the Loop I Updated the BG and Border Color of my Line and Updated it "without animation".
This is not the most efficient Method, and I don't recommend it for larger Charts or slow sites. But for me this is sufficient.
Related
I'm currently struggling with building a web frontend with a responsive chart, which should be filled with live data while the user is visiting the application.
My goal was to have the x-axis always scale automatically to perfectly cover the data. Then I found the bounds attribute of Chart.js, which when set to 'data' should do exactly what I want.
As you can see in this example, the whole thing only works well for the first few datasets - from x-values > 5 onwards, the scaling of the x-axis somehow gets messed up.
Did anyone else have this problem before? Did I assign other attributes incorrectly?
Here's my JavaScript code:
var data = [];
var graph = new Chart("chart", {
type: 'scatter',
data: {
datasets: [{
data: data,
pointRadius: 0,
borderWidth: 2,
pointHitRadius: 4,
pointHoverRadius: 6,
tension: 0,
showLine: true,
xAxisID: "xAxis",
yAxisID: "yAxis",
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false,
plugins: {
legend: {
display: false,
},
},
interaction: {
mode: 'nearest',
intersect: true,
},
scales: {
xAxis: {
type: 'linear',
position: 'bottom',
title: {
display: true,
text: 't (s)',
color: "black",
font: {
size: 15
}
},
ticks: {
color: "black",
callback: (value, index, values) => (index == (values.length - 1)) ? "" : value,
},
bounds: 'data'
},
yAxis: {
type: 'linear',
position: 'left',
ticks: {
color: "black",
maxTicksLimit: 7,
}
}
}
}
});
var x = 0;
setInterval(function() {
var newData = {
x: Math.round(x * 100) / 100,
y: Math.round(x * 100) / 100
};
data.push(newData);
graph.update();
x += 0.1;
}, 100);
I am trying to hide all the grid lines on y axis except the middle line which shows the positive values above x axis and negative below y axis.
I found out that zeroWidthLine option isnt avaiable in version 3 anymore.I am attching the js fiddle link in comment.
You can use scriptable options for the grid color to achieve this:
Chart.register(ChartDataLabels);
chartLabels = ['2018', '2019', '2020', 'TTM']
equityToAssetData = [4.32, -5.37, 4.73, 4.89, 3.6, ];
var equityToAssetDatasets = {
labels: chartLabels,
datasets: [{
type: 'line',
label: 'Equity to Asset ',
data: equityToAssetData,
backgroundColor: 'rgb(97,207,5)',
borderColor: 'rgb(97,207,5)',
borderWidth: 1.8,
lineTension: 0.4,
pointStyle: 'rectRot'
}]
}
var chartStylingSingle = {
animation: {
duration: 500,
},
responsive: true,
layout: {
padding: 20
},
interaction: {
mode: 'index',
intersect: false
},
elements: {
point: {
hoverRadius: 5
}
},
plugins: {
legend: {
display: false,
},
datalabels: {
borderWidth: 0.5,
color: 'green',
anchor: 'start',
align: 'end',
offset: 6,
formatter: (v, ctx) => {
let label = ctx.chart.data.labels[ctx.dataIndex];
if (label != 'TTM') {
label = ' ' + label;
}
return label + '\n ' + v;
},
font: {
size: 11,
weight: 'bold',
}
}
},
scales: {
y: {
display: true,
grid: {
color: (ctx) => (ctx.tick.value === 0 ? 'rgba(0, 0, 0, 0.1)' : 'transparent')
}
},
x: {
display: true,
grid: {
display: false,
}
}
}
}
var ctx = document.getElementById('equityToAsset').getContext('2d');
var myChart = new Chart(ctx, {
data: equityToAssetDatasets,
options: chartStylingSingle
})
<canvas id="equityToAsset"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0/chartjs-plugin-datalabels.min.js"></script>
I'm using Charts.js library to display a line graph from data which is constantly updated. Is there a way to display only the final point value at all times? I want it to do this to monitor the added values at all times.
javascript code:
var config2 = {
type: 'line',
data: {
labels: 0,
datasets: [{
label: "No selection",
lineTension: 0,
borderColor: "rgba(222, 44, 31)",
pointBackgroundColor: "#fff675",
fill: false,
data: 0,
}
]
},
options: {
responsive: true,
title: {
display: false,
text: 'Chart.js Time Point Data'
},
scales: {
x: {
type: 'time',
display: true,
scaleLabel: {
display: true,
labelString: 'Date'
},
ticks: {
major: {
enabled: true
},
fontStyle: function(context) {
return context.tick && context.tick.major ? 'bold' : undefined;
},
fontColor: function(context) {
return context.tick && context.tick.major ? '#FF0000' : undefined;
}
}
},
y: {
display: true,
scaleLabel: {
display: true,
labelString: 'value'
}
}
}
}
};
Each time, a new value is available, you can simply remove outdated labels and dataset.data values once a certain limit is reached. This can be done using Array.shift(), which removes the first element from an array. Once these array are updated, you need to invoke chart.update().
var maxValues = 4;
setInterval(() => {
chart.data.labels.push(new Date());
chart.data.datasets[0].data.push(Math.floor((Math.random() * 20) + 1));
if (chart.data.labels.length > maxValues) {
chart.data.labels.shift();
chart.data.datasets[0].data.shift();
}
chart.update();
}, 1000);
For displaying the value on the last added data point, you can use the Plugin Core API. It offers different hooks that may be used for executing custom code. In below runnable code snippet, I use the afterDraw hook to draw text directly on the canvas.
var chart = new Chart('chart', {
type: "line",
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
var iLastValue = chart.data.labels.length - 1;
var lastValue = chart.data.datasets[0].data[iLastValue];
var x = xAxis.getPixelForValue(chart.data.labels[iLastValue]);
var y = yAxis.getPixelForValue(lastValue);
ctx.save();
ctx.textAlign = 'center';
ctx.font = '14px Arial';
ctx.fillStyle = "red";
ctx.fillText('Value: ' + lastValue, x, y - 15);
ctx.restore();
}
}],
responsive: true,
maintainAspectRatio: false,
data: {
labels: [],
datasets: [{
label: "Data",
data: [],
fill: false,
lineTension: 0,
backgroundColor: "white",
borderColor: "red",
}]
},
options: {
layout: {
padding: {
right: 50
}
},
scales: {
xAxes: [{
type: 'time',
ticks: {
source: 'auto'
},
time: {
unit: 'second',
displayFormats: {
second: 'mm:ss'
},
tooltipFormat: 'mm:ss'
},
}],
yAxes: [{
ticks: {
min: 0,
max: 20,
stepSize: 5
}
}]
}
}
});
var maxValues = 4;
setInterval(() => {
chart.data.labels.push(new Date());
chart.data.datasets[0].data.push(Math.floor((Math.random() * 20) + 1));
if (chart.data.labels.length > maxValues) {
chart.data.labels.shift();
chart.data.datasets[0].data.shift();
}
chart.update();
}, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="chart" height="90"></canvas>
I was having some problem when trying to redraw chart on canvas using chart.js in Angular. Here is my HTML component:
<div class="form-group">
<select [(ngModel)]="timeMode" id="timeModeInput" class="browser-default custom-select" (change)="onTimeModeChange($event)">
<option value="all" selected>Yearly</option>
<option value="monthly">Monthly</option>
</select>
</div>
<canvas id="expenseTimelineCanvas"></canvas>
Upon dropdown select, I repopulate the chart:
if (chart != null) { chart.destroy(); chart.clear(); }
chart = new Chart(chartName, {
type: "line",
data: {
labels: labels,
datasets: [
{
data: chartData,
borderColor: lineColor,
fill: false,
borderWidth: 1,
borderDash: [10, 10],
pointBackgroundColor: "white",
pointBorderColor: lineColor,
pointRadius: 5
}
]
}
}
});
However, when I mouse over at certain part of the canvas, the old chart will be displayed. Any ideas on how to totally destroy the canvas before replotting on the same DOM element?
Thanks!
this happens because chartjs uses canvas and for redrawing you just have to destroy old drawing from the canvas.
when you make a new chart then you have to destroy the old chart.
but in angular you can also just reload the component to destroy the chart
your method
//this method
initChart(){
//this will generate chart you need
//before generating just destroy the old chart
}
second method
//in html file
<thisIsHTMLTagOfChartComponent *ngIf="showChart">
</thisIsHTMLTagOfChartComponent>
//in typescript
public showChart = false;
//this function will be called everytime you fetch the data from server
getChartData(){
this.showChart = false; //this will remove the component from the page
fetchData(uri)
.then(result => {
//do your stuff here
this.showChart = true // this will load the component in the page again
}
)
i prefer to not destroy your chart just change the value of it for e.g: in your change function just load a new chart definition in your same chart with new config, we have 2 different configs:
config = {
type: 'bar',
data: {
labels: dringlichkeiten.map(x => x.id),
datasets: [
{
label: 'My Bar Chart',
data: dringlichkeiten.map(x => x.value),
backgroundColor: ['red', 'green', 'yellow', 'blue', 'orange']
}
]
},
}
config2 = {
type: 'line',
data: {
datasets: [{
label: 'Höhenlinie',
backgroundColor: "rgba(255, 99, 132,0.4)",
borderColor: "rgb(255, 99, 132)",
fill: true,
data: [
{ x: 1, y: 2 },
{ x: 2500, y: 2.5 },
{ x: 3000, y: 5 },
{ x: 3400, y: 4.75 },
{ x: 3600, y: 4.75 },
{ x: 5200, y: 6 },
{ x: 6000, y: 9 },
{ x: 7100, y: 6 },
],
}]
},
options: {
responsive: true,
title: {
display: true,
text: 'Höhenlinie'
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
ticks: {
userCallback: function (tick) {
if (tick >= 1000) {
return (tick / 1000).toString() + 'km';
}
return tick.toString() + 'm';
}
},
scaleLabel: {
labelString: 'Länge',
display: true,
}
}],
yAxes: [{
type: 'linear',
ticks: {
userCallback: function (tick) {
return tick.toString() + 'm';
}
},
scaleLabel: {
labelString: 'Höhe',
display: true
}
}]
}
}
}
chart: Chart ;
in your change function just load a new config:
change(){
this.chart = new Chart('canvas', this.config2)
}
DEMO.
How to set line height only until points? I have used 'gridLines: display:false' but it hides all lines. Here on image below how it should be
It's not a native option in Chart.js but you can implement it yourself via plugin. See the annotations in the below code.
new Chart(document.getElementById('chart'), {
type: 'line',
data: {
labels: [0, 1, 2, 3, 4, 5],
datasets: [{
label: 'series 1',
data: [0, 2, 4, 3, 1, 0]
}]
},
options: {
maintainAspectRatio: false,
scales: {
xAxes: [{
gridLines: {
display: false, // must be false since we're going to draw our own 'gridlines'!
color: 'rgba(255, 0, 0, .2)', // can still set the colour.
lineWidth: 5 // can still set the width.
}
}],
yAxes: [{
gridLines: {
display: false
},
ticks: {
beginAtZero: true
}
}]
}
},
plugins: [{ // this is the magical bit :)
afterRender: function(c, options) {
let meta = c.getDatasetMeta(0),
max;
c.ctx.save();
c.ctx.strokeStyle = c.config.options.scales.xAxes[0].gridLines.color;
c.ctx.lineWidth = c.config.options.scales.xAxes[0].gridLines.lineWidth;
c.ctx.beginPath();
meta.data.forEach(function(e) {
if (max == undefined || c.config.data.datasets[0].data[e._index] > max) {
max = c.config.data.datasets[0].data[e._index];
}
c.ctx.moveTo(e._model.x, meta.dataset._scale.bottom);
c.ctx.lineTo(e._model.x, e._model.y);
});
c.ctx.textBaseline = 'top';
c.ctx.textAlign = 'right';
c.ctx.fillStyle = 'black';
c.ctx.fillText('Max value: ' + max, c.width - 10, 10);
c.ctx.stroke();
c.ctx.restore();
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="chart"></canvas>