The existing Logarithmic Axis defaults to the base of 10. Is there a way to change the scale to a logarithm of a different base?
You can implement a custom scale to change the base of the the log scale like so:
class Log2Axis extends Chart.Scale {
constructor(cfg) {
super(cfg);
this._startValue = undefined;
this._valueRange = 0;
}
parse(raw, index) {
const value = Chart.LinearScale.prototype.parse.apply(this, [raw, index]);
return isFinite(value) && value > 0 ? value : null;
}
determineDataLimits() {
const {
min,
max
} = this.getMinMax(true);
this.min = isFinite(min) ? Math.max(0, min) : null;
this.max = isFinite(max) ? Math.max(0, max) : null;
}
buildTicks() {
const ticks = [];
let power = Math.floor(Math.log2(this.min || 1));
let maxPower = Math.ceil(Math.log2(this.max || 2));
while (power <= maxPower) {
ticks.push({
value: Math.pow(2, power)
});
power += 1;
}
this.min = ticks[0].value;
this.max = ticks[ticks.length - 1].value;
return ticks;
}
/**
* #protected
*/
configure() {
const start = this.min;
super.configure();
this._startValue = Math.log2(start);
this._valueRange = Math.log2(this.max) - Math.log2(start);
}
getPixelForValue(value) {
if (value === undefined || value === 0) {
value = this.min;
}
return this.getPixelForDecimal(value === this.min ? 0 :
(Math.log2(value) - this._startValue) / this._valueRange);
}
getValueForPixel(pixel) {
const decimal = this.getDecimalForPixel(pixel);
return Math.pow(2, this._startValue + decimal * this._valueRange);
}
}
Log2Axis.id = 'log2';
Log2Axis.defaults = {};
Chart.register(Log2Axis);
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange", "Black", "White"],
datasets: [{
label: '# of Votes',
data: [8, 3, 60, 20, 130, 80, 1030, 250],
backgroundColor: 'pink',
borderColor: 'pink'
}]
},
options: {
scales: {
x: {
display: true
},
y: {
display: true,
type: 'log2',
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.4.0/chart.js"></script>
</body>
Fiddle link: https://jsfiddle.net/Leelenaleee/oyLf0wed/6/
Credit to chart.js team who made a sample for this: https://www.chartjs.org/docs/master/samples/advanced/derived-axis-type.html
Related
Hi guys quit knew in the JS world so I have a question,
Anoyone knows how I can get the variable ' Totaal_dataset' only in the label of the chart and not as a 3rd bar in the chart?
Below the code and the ability to play with it. It's a compound interest calculator.
https://codepen.io/sietssoo/pen/oNqGpXq
(function () {
var initial_deposit = document.querySelector('#initial_deposit'),
contribution_amount = document.querySelector('#contribution_amount'),
investment_timespan = document.querySelector('#investment_timespan'),
investment_timespan_text = document.querySelector('#investment_timespan_text'),
estimated_return = document.querySelector('#estimated_return'),
future_balance = document.querySelector('#future_balance');
function updateValue(element, action) {
var min = parseFloat(element.getAttribute('min')),
max = parseFloat(element.getAttribute('max')),
step = parseFloat(element.getAttribute('step')) || 1,
oldValue = element.dataset.value || element.defaultValue || 0,
newValue = parseFloat(element.value.replace(/\€/, ''));
if (isNaN(parseFloat(newValue))) {
newValue = oldValue;
} else {
if (action == 'add') {
newValue += step;
} else if (action == 'sub') {
newValue -= step;
}
newValue = newValue < min ? min : newValue > max ? max : newValue;
}
element.dataset.value = newValue;
element.value = (element.dataset.prepend || '') + newValue + (element.dataset.append || '');
updateChart();
}
function getChartData() {
var P = parseFloat(initial_deposit.dataset.value), // Principal
r = parseFloat(estimated_return.dataset.value / 100), // Annual Interest Rate
c = parseFloat(contribution_amount.dataset.value), // Contribution Amount
n = parseInt(document.querySelector('[name="compound_period"]:checked').value), // Compound Period
n2 = parseInt(document.querySelector('[name="contribution_period"]:checked').value), // Contribution Period
t = parseInt(investment_timespan.value), // Investment Time Span
currentYear = (new Date()).getFullYear()
;
var labels = [];
for (var year = currentYear; year < currentYear + t; year++) {
labels.push(year);
}
var balance_dataset = {
label: 'Totaal:',
grouped: false,
data: []
};
var principal_dataset = {
label: 'Totaal geïnvesteerd:',
backgroundColor: 'rgb(240, 202, 98)',
data: []
};
var interest_dataset = {
label: "+ Totale winst: ",
backgroundColor: 'rgb(14, 93, 51)',
data: []
};
for (var i = 1; i <= t; i++) {
var principal = P + ( c * n2 * i ),
interest = 0,
balance = principal;
if (r) {
var x = Math.pow(1 + r / n, n * i),
compound_interest = P * x,
contribution_interest = c * (x - 1) / (r / n2);
interest = (compound_interest + contribution_interest - principal).toFixed(0)
balance = (compound_interest + contribution_interest).toFixed(0);
}
future_balance.innerHTML = '€' + balance;
principal_dataset.data.push(principal);
interest_dataset.data.push(interest);
balance_dataset.data.push(balance);
}
return {
labels: labels,
datasets: [principal_dataset, interest_dataset]
}
}
function updateChart() {
var data = getChartData();
chart.data.labels = data.labels;
chart.data.datasets[0].data = data.datasets[0].data;
chart.data.datasets[1].data = data.datasets[1].data;
chart.update();
}
initial_deposit.addEventListener('change', function () {
updateValue(this);
});
contribution_amount.addEventListener('change', function () {
updateValue(this);
});
estimated_return.addEventListener('change', function () {
updateValue(this);
});
investment_timespan.addEventListener('change', function () {
investment_timespan_text.innerHTML = this.value + ' years';
updateChart();
});
investment_timespan.addEventListener('input', function () {
investment_timespan_text.innerHTML = this.value + ' years';
});
var radios = document.querySelectorAll('[name="contribution_period"], [name="compound_period"]');
for (var j = 0; j < radios.length; j++) {
radios[j].addEventListener('change', updateChart);
}
var buttons = document.querySelectorAll('[data-counter]');
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
button.addEventListener('click', function () {
var field = document.querySelector('[name="' + this.dataset.field + '"]'),
action = this.dataset.counter;
if (field) {
updateValue(field, action);
}
});
}
var ctx = document.getElementById('myChart').getContext('2d'),
chart = new Chart(ctx, {
type: 'bar',
data: getChartData(),
options: {
legend: {
display: false
},
tooltips: {
mode: 'index',
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + '€ ' + tooltipItem.yLabel;
}
}
},
responsive: true,
scales: {
xAxes: [{
stacked: true,
scaleLabel: {
display: true,
labelString: 'Jaar'
}
}],
yAxes: [{
stacked: true,
ticks: {
callback: function (value) {
return '€' + value;
}
},
scaleLabel: {
display: true
}
}]
}
}
});
})();`
I have three doughnut charts side by side like so:
The problem is, the number of items is different between the charts, causing the legend have different height and in turn the charts are not in line. Is it possible to align the charts to the top of the container?
You will need to use an custom plugin for this that makes an html legend:
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {
type
} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink',
backgroundColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange',
backgroundColor: 'orange'
}
]
},
options: {
plugins: {
legend: {
display: false,
},
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legend-container',
}
}
},
plugins: [htmlLegendPlugin],
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<div id="legend-container"></div>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
</body>
I want to set font colour at character level (or word level) in my data-labels in chartjs. See before and after image below.
For example, if my data-label is 0.89 I would like the paint the 0 as yellow, 8 as blue and 9 as red.
Sandbox:
https://codesandbox.io/s/quizzical-hooks-zcg91?file=/src/components/LineChart.jsx
Afaik you can set the individual collors of the letters with the datalabels plugin, you will need to write your own custom plugin for that, what you can do is provide an array of collors to the color property to give each entry a different collor: https://codesandbox.io/s/dazzling-leaf-pxmzm
datalabels: {
display: true,
color: ["black", "green", "blue", "pink", "purple"],
align: "end",
padding: {
right: 2
},
labels: {
padding: { top: 10 },
title: {
font: {
weight: "bold"
}
}
},
formatter: function (value) {
return "\n" + value;
}
}
Edit:
after looking at the code the datalabels plugin uses to render the labels its not possible to color individual characters, if you want that you will need to write your own custom plugin:
const customDatalabalesPlugin = {
id: 'customDatalabels',
afterDatasetsDraw: (chart, args, opts) => {
const {
ctx,
_metasets
} = chart;
_metasets.forEach((meta) => {
meta.data.forEach((datapoint) => {
const lineHeight = ctx.measureText('M').width;
const dpVal = datapoint.parsed.y;
const text = dpVal.toString();
const textWidth = ctx.measureText(text).width;
const color = opts.color || 'black';
if (typeof color === 'string') {
ctx.fillStyle = color;
ctx.fillText(text, (datapoint.x - textWidth / 2), (datapoint.y - lineHeight));
} else if (Array.isArray(color)) {
let x = datapoint.x - textWidth / 2;
for (let i = 0; i < text.length; i++) {
const char = text.charAt(i);
ctx.fillStyle = color[i % color.length];
ctx.fillText(char, x, (datapoint.y - lineHeight));
x += ctx.measureText(char).width;
}
} else {
console.error('Invalid color type provided');
}
})
})
}
}
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12.4, 19.234, 3.23213, 5.4, 2, 3],
borderColor: 'pink'
}]
},
options: {
plugins: {
customDatalabels: {
color: ['pink', 'green', 'orange'], // Color each character individual collor
// color: 'pink' // Color whole label this collor
}
}
},
plugins: [customDatalabalesPlugin]
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.0/chart.js"></script>
</body>
The Chart.js plugin is super, excellent, beautiful and easily customizable.
But even all this did not help me solve a few problems.
I have to create pixel perfect chart according to design shown on picture 1
I hope for your help!!
How to make those lines that without signatures be longer than those with signatures
I did the indentation from the scales to the graphs using the tickMarkLength parameter, but maybe it’s possible somehow under another, because you can see the overlap of one scale on another.
How to make the grid lines of the left and right scales coincide?
I set beforeUpdate .stepSize, but in spite of the fact that I specify that there should be 8 intervals, sometimes 8, then 9.
There is a link to my current code:
function data_generation(values_obj) {
let max_val=-900;
let min_val=0;
Object.keys(values_obj).forEach(function(key) {
chart_object={};
chart_object.label= values_obj[key].name;
chart_object.data= Object.values(values_obj[key].data);
chart_object.backgroundColor= values_obj[key].color;
if(key == 'TempOutdoor') {
chart_object.yAxisID = 'right-y-axis';
chart_object.backgroundColor= "transparent";
chart_object.pointRadius= 4;
chart_object.lineTension= 0;
chart_object.pointBackgroundColor="#FFF";
chart_object.pointBorderColor= "#60AD5E";
chart_object.borderColor= "#60AD5E";
chart_object.pointBorderWidth= 2;
chart_object.type= 'line';
} else {
chart_object.yAxisID = 'left-y-axis';
chart_object.lineTension= 0;
}
config.data.datasets.push(chart_object);
//find common min and max values
//min
if(min_val>parseFloat(values_obj[key].min)) {
min_val = parseFloat(values_obj[key].min);
}
//max
if(max_val < parseFloat(values_obj[key].max)) {
max_val = parseFloat(values_obj[key].max);
}
});
}
var config = {
drawTicks:false,
type: 'bar',
data: {
datasets: [ ],
labels: ''
},
options: {
animation: {
duration: 0
},
'legend':false,
responsive:true,
maintainAspectRatio: false,
scales: {
xAxes: [{
stacked: true,
barThickness: ($(window).width()<991.99)?14:24,
ticks: {
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines : {
display : false
}
}],
yAxes: [
{
beforeUpdate: function(scale) {
//1 find max and min through all leftlabels
var left_side_list = config.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));
let max_val = Math.max.apply(Math,left_side_list_data);
let min_val = Math.min.apply(Math,left_side_list_data);
// 8 intervals - 9 lines
let left_iterval = (max_val - min_val) / 8;
//set stepsize
scale.chart.options.scales.yAxes[0].ticks.stepSize = left_iterval;
return;
},
id: 'left-y-axis',
type: 'linear',
position: 'left',
ticks: {
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
callback: function(value, index, values) {
if(index % 2 == 0 || index==0) {
return ' ';
} else {
return " "+value.toFixed(0)+" ";
}
}
},
gridLines: {
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
},
{
beforeUpdate: function(scale) {
//var nMaxRev = Math.max.apply(Math,scale.chart.config.data.datasets[1].data);
//get right object data
var temp_list = config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
//var temp_list = scale.chart.config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
//console.log(temp_list);
if(temp_list[0].data !== undefined || temp_list[0].data != []) {
var nMaxRev = Math.max.apply(Math, temp_list[0].data);
var nMinRev = Math.min.apply(Math, temp_list[0].data);
var nLeftTickCount = 8;
if(nMinRev<0) {
nLeftTickCount = 7;
}
var nTickInterval = (nMaxRev - nMinRev) / nLeftTickCount;
scale.chart.options.scales.yAxes[1].ticks.stepSize = nTickInterval;
}
return;
},
id: 'right-y-axis',
type: 'linear',
position: 'right',
ticks: {
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
callback: function(value, index, values) {
if(index % 2 == 0 || index==0) {
return '';
} else {
return " "+value.toFixed(0);
}
}
},
gridLines: {
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
}
]
}
}
};
window.onload = function() {
var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
var data = JSON.parse(data_string);
config.data.labels = data.axis;
var values_obj = data.data;
data_generation(values_obj);
//console.log(JSON.stringify(config));
var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
window.StatisticsChart = new Chart(ctx,config);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
<canvas id="StatisticsChartCanvas"></canvas>
</div>
After a huge amount of ideas, attempts and errors, I found a solution for the full customization of the scale.
For this I
Disabled the display of gridlines and ticks using property
display:false,
Wrote a plugin that draw gridlines and ticks in accordance with the design.
That's what happened
var AikGridLinePlugin = {
beforeDraw: function(chartInstance) {
var yScaleLeft = chartInstance.scales["left-y-axis"];
var yScaleRight = chartInstance.scales["right-y-axis"];
var canvas = chartInstance.chart;
var ctx = canvas.ctx;
//left axis
var left_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));
let left_side_list_max = Math.max.apply(Math,left_side_list_data);
let left_side_list_min = Math.min.apply(Math,left_side_list_data);
let left_iterval = (left_side_list_max - left_side_list_min) / 8;
// right axis
var right_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
var right_side_list_data = [].concat(...Object.keys(right_side_list).map(e => right_side_list[e].data));
let right_side_list_max = Math.max.apply(Math,right_side_list_data);
let right_side_list_min = Math.min.apply(Math,right_side_list_data);
let right_iterval = (right_side_list_max - right_side_list_min) / 8;
var current_value_left = left_side_list_min,
current_value_right = right_side_list_min,
current_value_right_text=0;
for(var i=1;i<10;i++) {
ctx.lineWidth = 1;
ctx.font = "13px Roboto";
ctx.fillStyle = "#666666";
ctx.beginPath();
if(i%2==0) {
ctx.moveTo(47, yScaleLeft.getPixelForValue(current_value_left));
ctx.lineTo((canvas.width-47), yScaleLeft.getPixelForValue(current_value_left));
ctx.fillText((current_value_left>1)?current_value_left.toFixed(0):current_value_left.toFixed(1), 5, yScaleLeft.getPixelForValue(current_value_left)+5);
current_value_right_text=(current_value_right>1 || current_value_right<-1)?current_value_right.toFixed(0):current_value_right.toFixed(1);
ctx.fillText(current_value_right_text, (canvas.width-5-ctx.measureText(current_value_right_text).width), yScaleLeft.getPixelForValue(current_value_left)+5);
} else {
ctx.moveTo(15, yScaleLeft.getPixelForValue(current_value_left));
ctx.lineTo((canvas.width-15), yScaleLeft.getPixelForValue(current_value_left));
}
ctx.strokeStyle = "#91979F";
ctx.stroke();
current_value_left = current_value_left+left_iterval;
current_value_right = current_value_right+right_iterval;
}
return;
}
};
Chart.pluginService.register(AikGridLinePlugin);
function data_generation(values_obj) {
let max_val=-900;
let min_val=0;
Object.keys(values_obj).forEach(function(key) {
chart_object={};
chart_object.label= values_obj[key].name;
chart_object.data= Object.values(values_obj[key].data);
chart_object.backgroundColor= values_obj[key].color;
if(key == 'TempOutdoor') {
chart_object.yAxisID = 'right-y-axis';
chart_object.backgroundColor= "transparent";
chart_object.pointRadius= 4;
chart_object.lineTension= 0;
chart_object.pointBackgroundColor="#FFF";
chart_object.pointBorderColor= "#60AD5E";
chart_object.borderColor= "#60AD5E";
chart_object.pointBorderWidth= 2;
chart_object.type= 'line';
} else {
chart_object.yAxisID = 'left-y-axis';
chart_object.lineTension= 0;
}
config.data.datasets.push(chart_object);
//find common min and max values
//min
if(min_val>parseFloat(values_obj[key].min)) {
min_val = parseFloat(values_obj[key].min);
}
//max
if(max_val < parseFloat(values_obj[key].max)) {
max_val = parseFloat(values_obj[key].max);
}
});
}
var config = {
drawTicks:false,
type: 'bar',
data: {
datasets: [ ],
labels: ''
},
options: {
animation: {
duration: 0
},
'legend':false,
responsive:true,
maintainAspectRatio: false,
scales: {
xAxes: [{
id: 'x-axis',
stacked: true,
barThickness: ($(window).width()<991.99)?14:24,
ticks: {
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines : {
display : false
}
}],
yAxes: [
{
id: 'left-y-axis',
type: 'linear',
position: 'left',
ticks: {
display:false,
},
gridLines: {
display : false,
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
},
{
id: 'right-y-axis',
type: 'linear',
position: 'right',
ticks: {
display : false,
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines: {
display : false,
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
}
]
}
}
};
window.onload = function() {
var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
var data = JSON.parse(data_string);
config.data.labels = data.axis;
var values_obj = data.data;
data_generation(values_obj);
//console.log(JSON.stringify(config));
var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
window.StatisticsChart = new Chart(ctx,config);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
<canvas id="StatisticsChartCanvas"></canvas>
</div>
I have web monitoring data and I want to graph with chart js library in javascript.
Multiple data are at the same moment. However, 'tooltip' displays only one piece of data. Why is this?
Please help me :(
I (A) Login-swlee.zabbix.dev selected and time is 06/25 02:23:21
I (B) First page-swlee.zabbix.dev selected and time is 06/25 02:23:21
I (A) and (B) both selected but tooltip is only one.
```
var colors = ["#FEB500", "#5F8CFC", "#ADC803", "#F0605D"]
var list = data.list;
var list2 = data.list2;
var list3 = data.list3;
var datasets = [];
var labels = [];
for (i = 0; i < list3.length; i++) {
var date = new Date(list3[i].clock * 1000).getTime();
if (labels.indexOf(date) < 0) {
labels.push(date);
}
}
labels.sort();
var tbody = document.getElementById('webTable').children[1];
for (var i = 0; i < list.length; i++) {
var item = list[i];
var td_name = [];
for (var j = 0; j < list2.length; j++) {
var item2 = list2[j];
if (item.hosts[0].hostid == item2.hostid && item2.key_.indexOf('web.test.rspcode') > -1) {
var label = item2.name.replace('$1', item2.key_.substring(17, item2.key_.length - 1).split(',')[0]).replace('$2', item2.key_.substring(17, item2.key_.length - 1).split(',')[1]);
//line chart
var dataset = {
data: [],
label: label,
fill: false,
spanGaps: true,
borderColor: colors[i]
};
for (var k = 0; k < list3.length; k++) {
var item3 = list3[k];
if (item2.itemid == item3.itemid) {
var date = new Date(list3[k].clock * 1000).getTime();
dataset.data.push(null);
if (labels.indexOf(date) > -1) {
dataset.data[labels.indexOf(date)] = item3.value;
}
}
}
datasets.push(dataset);
}
}
}
var ctx = document.getElementById("webChart").getContext("2d");
var gData = {
labels: labels,
datasets: datasets
}
var lineChart = new Chart(ctx, {
type: 'line',
data: gData,
options: {
responsive: true,
scales: {
yAxes: [{
ticks: {
stepSize: 200,
suggestedMin: 0,
suggestedMax: 600
}
}],
xAxes: [{
type: 'time',
time: {
unit: 'minute',
round: 'minute',
displayFormats: {
minute: 'h:mm'
}
},
gridLines: {
display: false
}
}]
},
elements: {
line: {
tension: 0,
}
},
animation: false,
hover: {
animationDuration: 0,
},
responsiveAnimationDuration: 0,
legend: {
display: true,
labels: {
fontColor: '#fff'
}
},
tooltips: {
callbacks: {
title: function(tooltipItems, data) {
return new Date(tooltipItems[0].xLabel).format('MM/dd hh:mm:ss');
}
}
}
}
});
You need to set tooltip's mode to index
...
tooltips: {
mode: 'index', //<-- set this
callbacks: {
title: function(tooltipItems, data) {
return new Date(tooltipItems[0].xLabel).format('MM/dd hh:mm:ss');
}
}
}
...