Related
We are using react chart js but the problem occurs when we try to show multi-line charts in small widgets with high y-axis values.
Overflow occurs in smaller widths and large y axes numbers. I already try a thousand suffixes but it doesn't work. Is it possible to keep the y-axis constant by making the x-axis smaller? Or handle it responsive.
function randomTwoNumbers(n1, n2) {
return Math.floor(Math.random() * (n2 - n1 + 1) + n1)
}
function generateData(count) {
let data = []
for (let index = 0; index < count; index++) {
data.push({
x: `2022-07-${index}T00:00:00.000Z`,
y: randomTwoNumbers(1000000, 9999999)
})
}
return data
}
const data = {
datasets: [
{
data: generateData(100),
label: 'Temperature',
borderColor: '#236EF1',
tension: 0.5,
pointRadius: 1,
pointHoverRadius: 5,
pointHitRadius: 20,
yAxisID: 'y0'
},
{
data: generateData(100),
label: 'Humidity',
borderColor: '#CA840C',
tension: 0.5,
pointRadius: 1,
pointHoverRadius: 5,
pointHitRadius: 20,
yAxisID: 'y1'
}
]
}
const options = {
responsive: true,
resizeDelay: 300,
scales: {
x: {
display: true,
grid: {
borderColor: '#5E646E',
tickColor: '#5E646E',
display: true,
drawOnChartArea: false
},
ticks: {
autoSkip: false,
maxRotation: 0,
font: {
size: 10,
family: 'Inter',
lineHeight: '12px',
weight: '500'
}
},
time: {
unit: 'day',
displayFormats: {
minute: 'p',
hour: 'p',
day: 'MMM, do'
}
},
type: 'time',
offset: true,
adapters: {
date: {
locale: {
code: 'en-US',
formatLong: {},
localize: {},
match: {}
}
}
}
},
y0: {
display: true,
grid: {
borderColor: '#5E646E',
tickColor: '#5E646E',
display: true,
drawOnChartArea: false
},
ticks: {
font: {
size: 10,
family: 'Inter',
lineHeight: '12px',
weight: '500'
}
},
stack: 'single',
offset: false
},
y1: {
display: true,
grid: {
borderColor: '#5E646E',
tickColor: '#5E646E',
display: true,
drawOnChartArea: false
},
ticks: {
font: {
size: 10,
family: 'Inter',
lineHeight: '12px',
weight: '500'
}
},
stack: 'single',
offset: false
}
},
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
title: {
display: false
},
tooltip: {
displayColors: false
}
}
}
var config = {
type: 'line',
data,
options
}
var ctx = document.getElementById('canvas').getContext('2d')
window.myLine = new Chart(ctx, config)
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.2/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.1/chartjs-adapter-moment.min.js"></script>
<div style="width: 200px">
<canvas id="canvas"></canvas>
</div>
I'm currently using chart.js with Vue 2 and I'm facing a problem when using border-radius with stacked bars.
I want to have a border-top radius but when doing that I get a "gap" between the border that was created and the bar above.
Is there any option that can fill that empty space?
Current behaviour
Expected behaviour
These are the current options I'm using for the graph.
options: {
responsive: true,
lineTension: 1,
plugins: {
legend: {
position: 'bottom',
// prevent filtering using labels
'onClick' : function (evt, item, legend) {
return;
},
labels: {
usePointStyle: 'circle',
},
}
},
elements: {
bar: {
borderRadius: {
topLeft: 4.6,
topRight: 4.6,
},
}
},
scales: {
y: {
stacked: true,
ticks: {
maxTicksLimit: 6,
callback: function(value, index, ticks) {
return value + ' min';
}
},
},
x: {
stacked: true,
grid: {
color: "rgba(0, 0, 0, 0)",
}
}
}
}
What you need it is not configurable by any options.
Nevertheless you can use a plugin to change the base of the bar to draw, taking care of the border radius.
EDIT: I have found a specific option in bar element, base, which should address the use case.
See snippet:
const ctx = document.getElementById("myChart");
// not used but leave it
const plugin = {
id: 'fillGaps',
beforeDatasetDraw(chart, args) {
if (args.index > 0) {
args.meta.data.forEach(function(item){
const pre = item.base;
item.base += 12;
});
}
},
};
//
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['January', 'Fabruary', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'user 1 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'rgba(40, 139, 170, 1)',
borderWidth: 0,
borderSkipped: 'start',
base: 0
},
{
label: 'user 2 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'orange',
borderWidth: 0,
borderSkipped: 'start',
base: 0
}]
},
options: {
elements: {
bar: {
borderRadius: {
topLeft: 12,
topRight: 12,
bottomLeft: 12,
bottomRight: 12
},
}
},
scales: {
y: {
stacked: true,
},
x: {
stacked: true,
}
}
}
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.9.1/dist/chart.min.js"></script>
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"/>
</div>
</body>
</html>
I would like to create a horizontal BarChart.
The data range is from 0 to 20 or from 0 to -20.
The labels of the data should always be on the opposite side of the bar.
I also need additional text on the left and right.
The picture should make it clear:
I would like to do it with Chart.js.
edit:
This is my approach with Chart.js:
var ctx = document.getElementById("myChart");
var chartStatus = Chart.getChart("myChart");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["Cats", "Dogs", "Dugs", "Mice", "Hamster", "Tiger"],
datasets: [
{
label: false,
data: [-9, 1, 9, -9, 1, 9],
// data: [12, 19, -3],
backgroundColor: [
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)",
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)"
],
borderColor: [
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)",
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)"
],
borderWidth: 6
}
]
},
options: {
responsive: true,
indexAxis: "y",
plugins: {
gridLines: {
display: false
},
title: {
display: true,
text: "Pets",
font: {
size: 22
}
},
legend: {
display: false
},
tooltip: {
enabled: false
}
},
scales: {
x: {
min: -20,
max: 20,
gridLines: {
display: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
y: {
labels: ["Text1", "Text2", "Text3", "Text4", "Text5", "Text6"],
gridLines: {
display: false
},
barPercentage: 1,
gridLines: {
display: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
gridLines: {
display: false
},
secondY: {
labels: ["Text7", "Text8", "Text9", "Text10", "Text11", "Text12"],
position: "right",
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="myChart"></canvas>
<img style="display:none" id="url" />
I could solve the different text for the left and the right side with the labels on the respective axes.
But how do I get the labels (cat, dog etc) into the graph so that they are always on the opposite side of the bars?
Solution:
var ctx = document.getElementById("myChart");
var chartStatus = Chart.getChart("myChart");
if (chartStatus != undefined) {
chartStatus.destroy();
}
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["Cats", "Dogs", "Dugs", "Mice", "Hamster", "Tiger"],
datasets: [
{
label: false,
data: [-9, 1, 9, -9, 1, 9],
// data: [12, 19, -3],
backgroundColor: [
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)",
"rgba(39, 152, 228, 0.7)",
"rgba(233, 85, 59, 0.7)",
"rgba(152, 140, 202, 0.7)"
],
borderColor: [
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)",
"rgba(39, 152, 228,1)",
"rgba(233, 85, 59, 1)",
"rgba(152, 140, 202, 1)"
],
borderWidth: 6
}
]
},
options: {
animation: {
duration: 1,
// https://github.com/jtblin/angular-chart.js/issues/605
onComplete: function () {
var chart = this;
var ctx = chart.ctx;
ctx.font = Chart.helpers.fontString(
Chart.defaults.font.size,
Chart.defaults.font.style,
Chart.defaults.font.family
);
ctx.textAlign = "left";
ctx.textBaseline = "censter";
var labels = this.data.labels;
this.data.datasets.forEach(function (dataset, i) {
var meta = chart.getDatasetMeta(i);
meta.data.forEach(function (bar, index) {
var data = dataset.data[index];
let posx;
if (data < 0) {
posx = bar.x + bar.$context.element.width + 10;
ctx.textAlign = "left";
} else {
posx = bar.x - bar.$context.element.width - 10;
ctx.textAlign = "right";
}
// posx = bar.x;
ctx.fillText(`${labels[index]} (${data})`, posx, bar.y + 3);
});
});
}
},
responsive: true,
indexAxis: "y",
plugins: {
title: {
display: true,
text: "Pets",
font: {
size: 22
}
},
legend: {
display: false
},
tooltip: {
enabled: false
}
},
scales: {
x: {
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
labels: {
display: false
},
min: -20,
max: 20,
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
y: {
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
labels: ["Text1", "Text2", "Text3", "Text4", "Text5", "Text6"],
gridLines: {
display: false
},
barPercentage: 1,
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
},
secondY: {
labels: ["Text7", "Text8", "Text9", "Text10", "Text11", "Text12"],
position: "right",
grid: {
display: false,
drawBorder: false,
drawOnChartArea: false,
drawTicks: false
},
ticks: {
color: "#d6d6d9",
font: {
size: 22
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="myChart"></canvas>
New coder and trying to get a Chart.js to work.
https://codepen.io/phillyg/pen/PojMQLp
options: {
responsive: true,
legend: {
display: false,
},
scales: {
yAxes: [
{
ticks: {
fontColor: "rgba(255, 255, 255, 1)",
},
gridLines: {
display: false,
},
},
],
xAxes: [
{
ticks: {
fontColor: "rgba(255, 255, 255, 1)",
display: false,
},
gridLines: {
color: "rgba(255, 255, 255, .2)",
borderDash: [5, 5],
zeroLineColor: "rgba(255, 255, 255, .2)",
zeroLineBorderDash: [5, 5],
},
},
],
},
},
I have set the options.scales[xAxes].ticks.display to false.
Any help?
you have a problem with your spelling .. It should be Axis not Axes
there is no need for square brackets: xAxes: [
I have cleaned up spare commas -- While they don't "hurt" the JS, they are unnecessary.
const prices = [
[1483315200000, 1019.1988000000001],
[1483401600000, 1035.5313],
[1483488000000, 1130.8495163207272],
[1483574400000, 990.6674999999997],
[1483660800000, 894.0325],
[1483747200000, 900.0787999999999],
[1483833600000, 908.0850000000004],
[1483920000000, 901.8788880390637],
];
const labels = prices.map((el, i) => new Date(el[0]).toLocaleDateString());
const data = {
labels: labels,
datasets: [
{
data: prices.map((el) => el[1]),
borderColor: "rgb(0, 0, 0)",
pointBackgroundColor: "rgba(255, 255, 255, 1)",
},
],
};
const config = {
type: "line",
data: data,
options: {
responsive: true,
legend: {
display: false,
},
scales: {
yAxis:
{
ticks: {
fontColor: "rgba(255, 255, 255, 1)"
},
gridLines: {
display: false
},
},
xAxis:
{
ticks: {
display: false
},
gridLines: {
color: "rgba(255, 255, 255, .2)",
borderDash: [5, 5],
zeroLineColor: "rgba(255, 255, 255, .2)",
zeroLineBorderDash: [5, 5]
}
}
}
}
};
var myChart = new Chart(document.getElementById("myChart"), config);
<div>
<canvas id="myChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.5.1/dist/chart.min.js"></script>
<script src="chart_data.js" defer></script>
I'm a little stuck on how I would achieve this since it's a little unorthodox in relation to what the plugin does.
I'm attempting for each dataset to keep the last tooltip open at all times and leave the rest to be displayed on hover.
Currently, I'm using this to keep them all displayed:
Chart.plugins.register({
beforeRender: function (chart) {
if (chart.config.options.showAllTooltips) {
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function (dataset, i) {
console.log(chart);
chart.getDatasetMeta(i).data.forEach(function (sector, j) {
console.log(sector);
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
chart.options.tooltips.enabled = false;
}
},
afterDraw: function (chart, easing) {
if (chart.config.options.showAllTooltips) {
if (!chart.allTooltipsOnce) {
if (easing !== 1) {
return;
}
chart.allTooltipsOnce = true;
}
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function (tooltip) {
tooltip.initialize();
tooltip.update();
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});
Is there a way to only do this for the last tooltip in each dataset?
I've come across a way to hide tooltips:
filter: function (tooltipItem, data) {
var label = data.labels[tooltipItem.index];
if (label === labels[labels.length - 1]) {
console.log(tooltipItem);
} else {
return true;
}
}
But this just doesn't display them whatsoever since it's returning false.
Ultimately it will look like this, with the other tooltips displaying on hover of the corresponding node:
Any help greatly appreciated.
I would try to use chartjs-plugin-datalabels for such task.
You can try it out directly on their pages - scriptable interactions
setup
Setting some random data:
var DATA_COUNT = 8;
var labels = [];
Utils.srand(100);
for (var i = 0; i < DATA_COUNT; ++i) {
labels.push('' + i);
}
var DATA_COUNT = 8;
var labels = [];
Utils.srand(100);
for (var i = 0; i < DATA_COUNT; ++i) {
labels.push('' + i);
}
config
Here I did some changes
{
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'France',
backgroundColor: Utils.color(0),
borderColor: Utils.color(0),
data: Utils.numbers({
count: DATA_COUNT,
min: 10,
max: 100
}),
datalabels: {
align: function(context) {
return context.active ? 'start' : 'center';
}
}
}, {
label: 'Canada',
backgroundColor: Utils.color(1),
borderColor: Utils.color(1),
data: Utils.numbers({
count: DATA_COUNT,
min: 0,
max: 100
})
}, {
label: 'USA',
backgroundColor: Utils.color(2),
borderColor: Utils.color(2),
data: Utils.numbers({
count: DATA_COUNT,
min: 0,
max: 100
}),
datalabels: {
align: function(context) {
return context.active ? 'end' : 'center';
}
}
}]
},
options: {
plugins: {
datalabels: {
backgroundColor: function(context) {
return context.active ? context.dataset.backgroundColor : 'white';
},
borderColor: function(context) {
return context.dataset.backgroundColor;
},
borderRadius: function(context) {
return context.active ? 0 : 1;
},
borderWidth: 1,
color: function(context) {
return context.active ? 'white' : context.dataset.backgroundColor;
},
font: {
weight: 'bold'
},
formatter: function(value, context) {
value = Math.round(value * 100) / 100;
if (context.dataIndex === context.dataset.data.length - 1) {
return context.dataset.label + '\n' + value + '%';
} else {
return context.active
? context.dataset.label + '\n' + value + '%'
: ''
}
},
offset: 8,
padding: 0,
textAlign: 'center'
}
},
// Core options
aspectRatio: 5 / 3,
layout: {
padding: {
bottom: 16,
right: 40,
left: 8,
top: 40
}
},
hover: {
mode: 'index',
intersect: false
},
elements: {
line: {
fill: false
}
},
scales: {
yAxes: [{
stacked: true
}]
}
}
}
The main part here is:
formatter: function(value, context) {
value = Math.round(value * 100) / 100;
if (context.dataIndex === context.dataset.data.length - 1) {
return context.dataset.label + '\n' + value + '%';
} else {
return context.active
? context.dataset.label + '\n' + value + '%'
: ''
}
},
The formatter: where I show the label permanently only for context.dataIndex === context.dataset.data.length - 1.
In other cases if it is inactive I show only empty box in other case (hover) I show the information.
How does it look?
No hover
Hover with mouse
So this is the entire hmtl file with all relevant code. As I said, I am no programmer. This effort is from loads of trial and error, including several bits of code from stackoverflow questions.
<script>
$(document).ready(function () {
showtempGraph();
});
function showtempGraph()
{
{
$.post("temperaturedata.php",
function (data)
{
console.log(data);
var temptime = [];
var temp = [];
for (var i in data) {
temptime.push(data[i].timestamp);
temp.push(data[i].temperature);
}
var tempmin = Math.min(...temp);
var tempmax = Math.max(...temp);
var charttempdata = {
labels: temptime,
datasets: [
{
label: 'Temperatur',
pointRadius: 3,
backgroundColor: 'rgba(26, 137, 245, 0.2)',
borderColor: 'rgba(26, 137, 245, 1)',
hoverBackgroundColor: 'rgba(255, 255, 255, 1)',
hoverBorderColor: 'rgba(255, 255, 255, 1)',
pointBackgroundColor: 'rgba(12, 68, 122, 1)',
pointHoverBorderColor: "rgba(255, 255, 255, 0.8)",
data: temp
}
]
};
var graphtempTarget = $("#tempgraphCanvas");
var linetempGraph = new Chart(graphtempTarget, {
type: 'line',
data: charttempdata,
options: {
maintainAspectRatio: false,
tooltips: {
enabled: true,
custom: function(tooltip) {
if (!tooltip) return;
tooltip.displayColors = false;
},
callbacks: {
title: function(tooltipItems, tempdata) {
return 'Klokken: ' + tooltipItems[0].xLabel;
},
label: function(tooltipItem, tempdata) {
return 'Temperatur: ' + Number(tooltipItem.yLabel).toFixed(2) + '°C';
}
}
},
legend: {
display: false,
},
responsive: true,
scales: {
xAxes: [{
type: 'time',
time: {
displayFormats: {
hour: 'HH:mm'
},
tooltipFormat: 'HH:mm',
},
unit : 'day',
gridLines: {
color: '#999999',
lineWidth: 1
},
ticks: {
fontColor: "#fff",
}
}],
yAxes: [
{
type: 'linear',
position: 'left',
gridLines: {
color: '#999999',
lineWidth: 1
},
ticks: {
fontColor: "#fff",
}
}, {
type: 'linear',
position: "right",
afterUpdate: function(scaleInstance) {
console.dir(scaleInstance);
},
gridLines: {
color: '#999999)',
lineWidth: 0.5
},
ticks: {
stepSize: tempmax - tempmin,
min: tempmin,
max: tempmax,
mirror: true,
padding: -10,
fontColor: "#fff",
fontSize: 18,
callback: function(value) {
return value + '°C';
}
},
scaleLabel: {
display: true,
labelString: '°C',
fontColor: "#fff",
fontSize: 14
}
}]
},
}
});
});
}
}
</script>