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>
Related
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 am trying to create a timeline using Chart.js.
My Goal is to have a single row that shows various icons/events that happened in a game.
By using a linechart and removing the lines, leaving only the points, you can place markers on the timeline.
You can display these points as custom images by assigning an image variable to the pointStyle attribute of the dataset.
With a single image this works perfectly fine, as you can see in the following snippet.
//Create the image
const img = new Image(20,20);
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Skull_Icon_%28Noun_Project%29.svg/1200px-Skull_Icon_%28Noun_Project%29.svg.png";
var ctx = document.getElementById('chart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: "Deaths",
data: [{x:"00:01:23", y: 0},{x:"00:03:41", y: 0},{x:"00:04:29", y: 0},{x:"00:05:35", y: 0},{x:"00:06:27", y: 0},{x:"00:07:07", y: 0},{x:"00:08:48", y: 0},{x:"00:09:31", y: 0}],
//Assign the image in the point Style attribute
pointStyle:img,
showLine: false,
fill: false,
tooltip: "Player Died",
borderColor: "#000000",
backgroundColor: "#000000"
},
]},
//The Rest is just styling
options: {
interaction:{
mode:'nearest'
},
tooltips:{
custom: function(tooltip){
if(!tooltip)return;
tooltip.displayColors = false;
},
callbacks:{
title: function(tooltipItem, data) {
return data.datasets[tooltipItem[0].datasetIndex]['tooltip'];
},
label: function(tooltipItem, data) {
return tooltipItem.xLabel;
}
}
},
legend: {
display: true
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'hh:mm:ss',
tooltipFormat: 'HH:mm:ss',
displayFormats: {
second: "HH:mm:ss"
},
unitStepSize: 30
},
ticks:{
fontColor: "white"
}
}],
yAxes: [{
ticks: {
display: false,
},
gridLines: {
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<canvas id="chart" height= "50px" style="background-color: rgb(200, 200, 200);"></canvas>
As you can see i am creating an Image(), pointing it to the source and assigning it as the pointStyle attribute of the dataset.
But with this method i need to create a new Dataset whenever i want to display another image.
For my purpose i need to be able to dynamically assign different images for every point.
That is why i looked into scriptables in Chart.js. On the documentation it is stated that you can use custom functions to return an image/styling for pointStyles.
https://www.chartjs.org/docs/latest/general/options.html
In the following snippet you can see my current code.
I moved the creation of the image to a custom function in the point Style attribute of the dataset. With this i will be able to select from more images later.
But for now i can not make it work with one image. So how can i make the function return an image, so that the styling works? With this i will later be able to select from multiple images. Or is there maybe another solution i did not come up with?
Thank you very much for your help.
var ctx = document.getElementById('chart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: "Deaths",
data: [{x:"00:01:23", y: 0},{x:"00:03:41", y: 0},{x:"00:04:29", y: 0},{x:"00:05:35", y: 0},{x:"00:06:27", y: 0},{x:"00:07:07", y: 0},{x:"00:08:48", y: 0},{x:"00:09:31", y: 0}],
//Try to create the image in a custom function
pointStyle: function(context){
var img = new Image(20,20);
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Skull_Icon_%28Noun_Project%29.svg/1200px-Skull_Icon_%28Noun_Project%29.svg.png";
return img;
},
showLine: false,
fill: false,
tooltip: "Player Died",
borderColor: "#000000",
backgroundColor: "#000000"
},
]},
options: {
interaction:{
mode:'nearest'
},
tooltips:{
custom: function(tooltip){
if(!tooltip)return;
tooltip.displayColors = false;
},
callbacks:{
title: function(tooltipItem, data) {
return data.datasets[tooltipItem[0].datasetIndex]['tooltip'];
},
label: function(tooltipItem, data) {
return tooltipItem.xLabel;
}
}
},
legend: {
display: true
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'hh:mm:ss',
tooltipFormat: 'HH:mm:ss',
displayFormats: {
second: "HH:mm:ss"
},
unitStepSize: 30
},
ticks:{
fontColor: "white"
}
}],
yAxes: [{
ticks: {
display: false,
},
gridLines: {
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<canvas id="chart" height= "50px" style="background-color: rgb(200, 200, 200);"></canvas>
You can add different images via an array.
//Create the image
const img = new Image(20, 20);
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Skull_Icon_%28Noun_Project%29.svg/1200px-Skull_Icon_%28Noun_Project%29.svg.png";
const img2 = new Image(20, 20);
img2.src = "https://pngimg.com/uploads/pacman/pacman_PNG21.png";
const img3 = new Image(20, 20);
img3.src = "https://pngimg.com/uploads/pacman/pacman_PNG70.png"
var ctx = document.getElementById('chart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: "Deaths",
data: [{
x: "00:01:23",
y: 0
}, {
x: "00:03:41",
y: 0
}, {
x: "00:04:29",
y: 0
}, {
x: "00:05:35",
y: 0
}, {
x: "00:06:27",
y: 0
}, {
x: "00:07:07",
y: 0
}, {
x: "00:08:48",
y: 0
}, {
x: "00:09:31",
y: 0
}],
//Assign the image in the point Style attribute
pointStyle: [img2, img, img3],
showLine: false,
fill: false,
tooltip: "Player Died",
borderColor: "#000000",
backgroundColor: "#000000"
}, ]
},
//The Rest is just styling
options: {
interaction: {
mode: 'nearest'
},
tooltips: {
custom: function(tooltip) {
if (!tooltip) return;
tooltip.displayColors = false;
},
callbacks: {
title: function(tooltipItem, data) {
return data.datasets[tooltipItem[0].datasetIndex]['tooltip'];
},
label: function(tooltipItem, data) {
return tooltipItem.xLabel;
}
}
},
legend: {
display: true
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'hh:mm:ss',
tooltipFormat: 'HH:mm:ss',
displayFormats: {
second: "HH:mm:ss"
},
unitStepSize: 30
},
ticks: {
fontColor: "white"
}
}],
yAxes: [{
ticks: {
display: false,
},
gridLines: {
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<canvas id="chart" height="50px" style="background-color: rgb(200, 200, 200);"></canvas>
How you get the images to the array is up to you.
You can update it with something like this
function addimage(img) {
myChart.data.datasets[0].pointStyle.push(img);
myChart.update();
};
addimage(img2)
Of course you will want to add your images in some other dynamic way I’m sure but this should help.
I have this barChart and I'm with difficult to set a color by script to just one bar of the index.
Bellow is my chartbar code:
var ctx = document.getElementById("myBarChart");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [{
label: "Valor",
backgroundColor: "rgba(2,117,216,1)",
borderColor: "rgba(2,117,216,1)",
data: [],
}],
},
options: {
plugins: {
datalabels: {
anchor: 'end',
align: 'top',
formatter: function (value, context) {
if (value != 0) {
return new Date(value * 1000).toISOString().substr(11, 8);
} else {
value = " "
return value;
}
},
font: {
weight: 'bold'
}
}
},
responsive: true,
tooltips: {
/* intersect: false, */
callbacks: {
title: function (tooltipItem, data) {
return data['labels'][tooltipItem[0]['index']];
},
label: function (tooltipItem, data) {
return new Date(parseInt(data['datasets'][0]['data'][tooltipItem[
'index']]) * 1000).toISOString().substr(11,
8);
},
},
},
scales: {
xAxes: [{
time: {
unit: 'month'
},
gridLines: {
display: false
},
}],
yAxes: [{
ticks: {
min: 0,
suggestedMax: 36000,
stepSize: 3600,
beginAtZero: true,
},
}],
},
}
});
I have tried this:
myBarChart.data.datasets[0].backgroundColor[0]
But it doesnt work.
If someone know how to do this in the last version of the chartjs, please help me.
You can provide an array to the backgroundColor property. In this array you can fill it with the default color except for the index where you want the bar to be a different collor
I am working on stacked Bar charts using chart.js.
I need to show labels in middle of Bars as percentage and total sum on top of bars stacked together. Currently, I am able to show their percentage after searching for code. But that percentages are not correct mathematically. I have added that code in js fiddle. Hope I got some help for it. I am just weak in js.
https://jsfiddle.net/n4nish/hca3wdgq/4/
HTML -
var data = [{
label: 'New',
backgroundColor: '#1d3f74',
data: [6310, 5742, 4044, 5564]
}, {
label: 'Repeat',
backgroundColor: '#6c92c8',
data: [11542, 12400, 12510, 11450]
}];
var options = {
maintainAspectRatio: false,
spanGaps: false,
responsive: true,
legend: {
display: true,
position: 'bottom',
labels: {
fontColor: "#fff",
boxWidth: 14,
fontFamily: 'proximanova'
}
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
var type = data.datasets[tooltipItem.datasetIndex].label;
var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var total = 0;
for (var i = 0; i < data.datasets.length; i++)
total += data.datasets[i].data[tooltipItem.index];
if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
return type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, '1,');
} else {
return [type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, '1,'), "Overall : " + total];
}
}
}
},
plugins: {
datalabels: {
formatter: function (value, ctx) {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value * 100 / sum).toFixed(0) + "%";
return percentage;
},
font: {
weight: "normal"
},
color: "#fff"
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
ticks: {
fontColor: "#fff"
}
}],
yAxes: [{
stacked: true,
display: false,
ticks: {
fontColor: "#fff"
}
}]
}
};
var ctx = document.getElementById("mychart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["Jun", "July", "Aug", "Sept"],
datasets: data
},
options: options
});
You can set options that will apply to:
all labels in the chart:
options.plugins.datalabels.*
only a single dataset:
dataset.datalabels.*
// Label formatter function
const formatter = (value, ctx) => {
const otherDatasetIndex = ctx.datasetIndex === 0 ? 1 : 0;
const total =
ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;
return `${(value / total * 100).toFixed(0)}%`;
};
const data = [{
// stack: 'test',
label: "New",
backgroundColor: "#1d3f74",
data: [6310, 5742, 4044, 5564],
// Change options only for labels of THIS DATASET
datalabels: {
color: "white",
formatter: formatter
}
},
{
// stack: 'test',
label: "Repeat",
backgroundColor: "#6c92c8",
data: [11542, 12400, 12510, 11450],
// Change options only for labels of THIS DATASET
datalabels: {
color: "yellow",
formatter: formatter
}
}
];
const options = {
maintainAspectRatio: false,
spanGaps: false,
responsive: true,
legend: {
display: true,
position: "bottom",
labels: {
fontColor: "#fff",
boxWidth: 14,
fontFamily: "proximanova"
}
},
tooltips: {
mode: "label",
callbacks: {
label: function(tooltipItem, data) {
const type = data.datasets[tooltipItem.datasetIndex].label;
const value =
data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
let total = 0;
for (let i = 0; i < data.datasets.length; i++)
total += data.datasets[i].data[tooltipItem.index];
if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
return (
type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, "1,")
);
} else {
return [
type +
" : " +
value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, "1,"),
"Overall : " + total
];
}
}
}
},
plugins: {
// Change options for ALL labels of THIS CHART
datalabels: {
color: "#white",
align: "center"
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
ticks: {
fontColor: "#fff"
}
},
{
type: 'category',
offset: true,
position: 'top',
ticks: {
fontColor: "#fff",
callback: function(value, index, values) {
return data[0].data[index] + data[1].data[index]
}
}
}
],
yAxes: [{
stacked: true,
display: false,
ticks: {
fontColor: "#fff"
}
}]
}
};
const ctx = document.getElementById("mychart").getContext("2d");
new Chart(ctx, {
type: "bar",
data: {
labels: ["Jun", "July", "Aug", "Sept"],
datasets: data
},
options: options
});
body {
background: #20262e;
font-family: Helvetica;
padding-top: 50px;
}
#mychart {
height: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#0.4.0/dist/chartjs-plugin-datalabels.min.js"></script>
<canvas id="mychart"></canvas>
Codepen
I am trying to achieve something like this using chart.js. I wanted to show data of male/female according to each age group:
Here is my chart options:
var options = {
layout: {
padding: {
top: 5,
}
},
scales:
{
yAxes: [{
display: true,
barPercentage: 0.4,
ticks: {
fontSize: 12
},
stacked: true,
}],
xAxes: [{
stacked: true,
}]
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: ageGroup,
datasets: [{
label: 'Male',
data: maleData,
backgroundColor: '#2196F3',
hoverBackgroundColor: '#2196F3'
},
{
label: 'Female',
data: femaleData,
backgroundColor: '#E91E63',
hoverBackgroundColor: '#E91E63'
}]
},
options: options
};
I changed the positive in femaleData array into a negative number to achieve the result above:
for (var i = 0; i < femaleData.length; i++) {
femaleData[i] = -Math.abs(femaleData[i]);
}
However, the y-axis at 0 is not centralized as it pushed to the right hand side since left hand side got more data. I not even sure if this is the correct way to set the chart in opposite direction. How can I do this correctly?
as per the requirements mentioned in OP's comment section
ꜱʜᴏᴡ ᴘᴏꜱɪᴛɪᴠᴇ ᴠᴀʟᴜᴇꜱ ᴏɴ x-ᴀxɪꜱ
use the following callback function for x-axis ticks :
callback: function(t, i) {
return t < 0 ? Math.abs(t) : t;
}
ꜱʜᴏᴡ ᴘᴏꜱɪᴛɪᴠᴇ ᴠᴀʟᴜᴇ ᴏɴ ᴛᴏᴏʟᴛɪᴘ
use the following callback function for tooltips :
callbacks: {
label: function(t, d) {
var datasetLabel = d.datasets[t.datasetIndex].label;
var xLabel = Math.abs(t.xLabel);
return datasetLabel + ': ' + xLabel;
}
}
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
var ageGroup = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61-70', '71-80', '80+'];
var maleData = [30, 0, 0, 0, 10, 0, 0, 0, 0];
var femaleData = [0, 0, 0, -20, -50, -20, 0, 0, 0];
var options = {
layout: {
padding: {
top: 5,
}
},
scales: {
yAxes: [{
display: true,
barPercentage: 0.4,
ticks: {
fontSize: 12
},
stacked: true,
}],
xAxes: [{
stacked: true,
ticks: {
callback: function(t, i) {
return t < 0 ? Math.abs(t) : t;
}
}
}]
},
tooltips: {
callbacks: {
label: function(t, d) {
var datasetLabel = d.datasets[t.datasetIndex].label;
var xLabel = Math.abs(t.xLabel);
return datasetLabel + ': ' + xLabel;
}
}
},
responsive: true,
//maintainAspectRatio: false,
legend: {
display: false,
},
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: ageGroup,
datasets: [{
label: 'Male',
data: maleData,
backgroundColor: '#2196F3',
hoverBackgroundColor: '#2196F3'
}, {
label: 'Female',
data: femaleData,
backgroundColor: '#E91E63',
hoverBackgroundColor: '#E91E63'
}]
},
options: options
};
new Chart(ctx, opt);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>