the issue I have now is that I'm trying to use objects in the "data" field of my Chartjs script. This is my code below:
<canvas id="QGL_Chart"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script>
var interval = window.onload = () => {
var selectedDate;
const buyPriceData = [];
const buyVolData = [];
const sellPriceData = [];
const sellVolData = [];
var ctx = document.getElementById("QGL_Chart").getContext('2d');
ctx.canvas.width = 934;
ctx.canvas.height = 400;
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Line A',
data: [{
x: 90,
y: 90
}, {
x: 20,
y: 96
}, {
x: 15,
y: 97
}]
},
{
label: 'Line B',
data: [{
x: 10,
y: 96
}, {
x: 20,
y: 95.8
}, {
x: 100,
y: 99
}]
}
]
},
options: {
title: {
display: true,
text: 'Equilibrum Graph',
fontSize: 18
},
legend: {
display: true,
position: "bottom"
},
scales: {
xAxes: [{
ticks: {
reverse: false,
// stepSize: 6,
min: 0
}
}]
}
}
});
function refreshCurveData () {
selectedDate = document.querySelectorAll(".buyclass .column-date.sorting_1")[1].textContent;
buyPriceTable = document.querySelectorAll(".buyclass td.column-price");
buyVolTable = document.querySelectorAll(".buyclass td.column-volume");
sellPriceTable = document.querySelectorAll(".sellclass td.column-price");
sellVolTable = document.querySelectorAll(".sellclass td.column-volume");
let i = 0;
do {
var buyPriceData1 = buyPriceTable[i].textContent;
var buyVolData1 = buyVolTable[i].textContent;
var sellPriceData1 = sellPriceTable[i].textContent;
var sellVolData1 = sellVolTable[i].textContent;
buyPriceData[i] = `{x: ${buyPriceData1}, y: ${buyVolData1}}`
sellPriceData[i] = `{x: ${sellPriceData1}, y: ${sellVolData1}}`
//buyPriceData[i] = buyPriceData[i].replace(/"/g, "");
//sellPriceData[i] = sellPriceData[i].replace(/"/g, "");
// buyPriceData.push ({ x: buyPriceData1, y: buyVolData1 });
// sellPriceData.push ({ x: sellPriceData1, y: sellVolData1 });
i++;
}
while ( document.querySelectorAll(".column-date.sorting_1")[i].textContent == selectedDate && i < 9);
}
setInterval(() => {
refreshCurveData();
myChart.update();
},2500);
};
</script>
When I replace the codes in the "data" fields with buyPriceData and sellPriceData respectively, the graph does not visualize, I also tried doing:
data: {
datasets: [{
label: 'Line A',
data: Object.values(buyPriceData)
},
{
label: 'Line B',
data: Object.values(sellPriceData)
}
]
},
But I still have the same problem, please can someone help me with this?
I've created a pen here: https://codepen.io/scottfranc/pen/BaLGwZK
Thank you.
it looks like you are saving a String in buyPriceData. It looks like it should be an object
Maybe try this:
buyPriceData[i] = { x: buyPriceData1, y: buyVolData1 };
and then do the same for sellPriceData
Related
I've been using Chart.JS a little more than a week, and I've faced some problems, but right now I'm really stuck.
I need to create something similar to this, there are 1 product(blue) value and the orange one is an estimate of its value on the next month. So what I need to do is basically (((a — b) * 100) / a (or b I can't really remember)) and this as a label not actual data, and the result would be some percentage that would be used as a line on top of one product.
So far I got only this code, but it's not working like I need to.
{
type: "line",
label: monthsLabels?.mesBaseLabel,
data: [
productsValues?.receitaLiquidaBase[0],
productsValues?.receitaLiquidaOrcado[0],
],
fill: false,
},
{
type: "line",
label: monthsLabels?.mesOrcadoLabel,
data: [
productsValues?.receitaLiquidaBase[0],
productsValues?.receitaLiquidaOrcado[0],
],
// data: productsValues?.receitaLiquidaOrcado,
fill: false,
},
{
type: "bar",
label: monthsLabels?.mesBaseLabel,
data: productsValues?.receitaLiquidaBase,
// [
// productsValues?.receitaLiquidaBase[0],
// productsValues?.receitaLiquidaBase[1],
// ],
backgroundColor: ["rgba(42,62,176, 1)"],
borderWidth: 4,
datalabels: {
// anchor: "end",
// align: "top",
font: {
size: 10,
},
rotation: -90,
color: "white",
formatter: (value, context) => {
// console.log(value);
if (value !== 0) {
return value?.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,");
} else {
return 0;
}
},
},
},
{
type: "bar",
label: monthsLabels?.mesOrcadoLabel,
data: productsValues?.receitaLiquidaOrcado,
backgroundColor: "orange",
borderWidth: 4,
datalabels: {
// anchor: "end",
// align: "top",
font: {
size: 10,
},
rotation: -90,
color: "black",
formatter: (value, context) => {
// console.log(value);
if (value !== 0) {
return value?.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,");
} else {
return 0;
}
},
},
},
Objects on, data and label are an array of number coming from some fetching.
You are best off using a custom inline plugin for this:
var options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'red'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
backgroundColor: 'blue'
}
]
},
options: {
plugins: {
customValue: {
offset: 5,
dash: [5, 15]
}
}
},
plugins: [{
id: 'customValue',
afterDraw: (chart, args, opts) => {
const {
ctx,
data: {
datasets
},
_metasets
} = chart;
datasets[1].data.forEach((dp, i) => {
let increasePercent = dp * 100 / datasets[0].data[i] >= 100 ? Math.round((dp * 100 / datasets[0].data[i] - 100) * 100) / 100 : Math.round((100 - dp * 100 / datasets[0].data[i]) * 100) / 100 * -1;
let barValue = `${increasePercent}%`;
const lineHeight = ctx.measureText('M').width;
const offset = opts.offset || 0;
const dash = opts.dash || [];
ctx.textAlign = 'center';
ctx.fillText(barValue, _metasets[1].data[i].x, (_metasets[1].data[i].y - lineHeight * 1.5), _metasets[1].data[i].width);
if (_metasets[0].data[i].y >= _metasets[1].data[i].y) {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y);
ctx.lineTo(_metasets[0].data[i].x, _metasets[1].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[1].data[i].y - offset);
ctx.stroke();
} else {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[0].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[1].data[i].y - offset - lineHeight * 2);
ctx.stroke();
}
});
}
}]
}
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.1/chart.js"></script>
</body>
with my current configs, I can't get tooltips to appear. I am using the latest version of Chart.js, with the latest luxon, and the luxon-chartjs plugin via cdn.
i am using a logarithmic Y axis and cartesian timeline x-axis
i can only get a tooltip to appear when I include a blank callback for label. However, that tooltip that appears only has the unformatted date of the datapoint and no other info. when i ask the label callback to return something, nothing is displayed... when i include a blank callback for title or return anything in the title callback, this works and is displayed. i have hacked together a tooltip but in previous versions I've had no issue with the chart automagically creating these for me.
here is my current code and the working fiddle
let maxArray, correctArray, incorrectArray, objectArray
let yourImage = new Image()
yourImage.src = `https://res.cloudinary.com/behaviorreportcard/image/upload/w_10,h_18,c_scale/v1623785517/Untitled_10_qbwmx4.png`;
let ctx = document.getElementById('myChart').getContext('2d');
Chart.defaults.font.size = '10px';
Chart.defaults.plugins.tooltip.enabled = true
const Interval = luxon.Interval,
DateTime = luxon.DateTime,
Duration = luxon.Duration;
let minTime = "2017-01-19",
maxTime = "2017-04-01",
start = DateTime.fromISO(minTime),
end = DateTime.fromISO(maxTime),
duration = Duration.fromObject({
days: 1
});
function getStart() {
let newStart
if (start.weekday != 7) {
return start.minus({
days: start.weekday
}).toFormat("yyyy-MM-dd")
} else {
return start.toFormat("yyyy-MM-dd")
}
}
let emptyArray = Interval.fromDateTimes(start, end).splitBy(duration).map(t => ({
date: t.s.toFormat("yyyy-MM-dd"),
correct: null,
incorrect: null
}));
let incomingData = [{
date: '2017-01-01',
correct: 10,
incorrect: 20
},
{
date: '2017-01-02',
correct: 10,
incorrect: 0.09
},
{
date: '2017-01-03',
correct: 0.9,
incorrect: null
}, {
date: '2017-01-04',
correct: 3,
incorrect: 0.09
}, {
date: '2017-01-27',
correct: 5,
incorrect: 0.09
}, {
date: '2017-01-28',
correct: 10,
incorrect: null
}, {
date: '2017-02-19',
correct: null,
incorrect: 50
}, {
date: '2017-02-20',
correct: 75,
incorrect: 50
}, {
date: '2017-02-28',
correct: 60,
incorrect: 0.085
}, {
date: '2017-03-01',
correct: 75,
incorrect: null
}, {
date: '2017-03-02',
correct: 10,
incorrect: null
}
]
let returns = []
emptyArray.forEach(x => {
let objToReplace = incomingData.find(o => o.date === x.date)
let objIndex = incomingData.indexOf(objToReplace);
if (objIndex != -1) {
returns.push(incomingData[objIndex])
} else {
returns.push(x)
}
})
let dates = []
let corrects = []
let incorrects = []
let nulls = []
let nullVal = 0.025
returns.forEach(x => {
dates.push(x.date)
if (x.correct != null && x.incorrect != null) {
nulls.push(null)
} else if (x.correct == null && x.incorrect != null) {
nulls.push(nullVal)
}
if (x.incorrect == null) {
incorrects.push(null)
} else {
incorrects.push(x.incorrect)
}
if (x.correct == null) {
corrects.push(null)
} else {
corrects.push(x.correct)
}
})
function returnList(key) {
return returns.map(x => {
return x[key]
})
}
function returnNulls() {
let list = []
returns.forEach(x => {
if ((x.correct != null && x.incorrect == null) || (x.correct == null && x.incorrect != null)) {
list.push(nullVal)
} else {
list.push(null)
}
})
return list
}
console.log(returnNulls())
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: returnList('date'),
datasets: [{
label: 'none',
data: returnNulls(),
spanGaps: false,
pointStyle: yourImage,
},
{
label: 'Corrects',
data: returnList('correct'),
borderWidth: 1,
pointRadius: 5,
spanGaps: false,
fill: false,
borderColor: 'green',
pointStyle: 'circle'
},
{
label: 'Incorrects',
data: returnList('incorrect'),
fill: false,
spanGaps: false,
borderColor: 'red',
pointStyle: 'triangle',
pointRadius: 5,
borderWidth: 1
}
]
},
options: {
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
interaction: {
mode: 'nearest'
},
plugins: {
tooltip: {
enabled: true,
callbacks: {
label: function(item) {},
title: function(item) {
let date = item[0].label.split(',').slice(0, 2).join(',')
let dataSet = item[0].dataset.label
if (dataSet == 'none') {
return
}
return date + '\n' + dataSet
}
}
},
legend: {
display: true,
position: 'bottom',
labels: {
filter: (x, y) => {
if (!x.text.includes('none')) {
return x
}
},
usePointStyle: 'true',
fontColor: 'black',
fontSize: 12
}
},
title: {
display: true,
text: 'Your daily average results for chapter __',
fontColor: 'black',
fontSize: 18,
}
},
scales: {
x: {
ticks: {
font: {
size: 12
}
},
min: getStart(),
max: '2017-03-31',
type: 'time',
time: {
unit: 'day',
stepSize: 7
}
},
y: {
type: 'logarithmic',
max: 100,
min: 0.01,
ticks: {
autoSkip: false,
callback: function(value,
index,
values) {
switch (value) {
case 1:
return '1 per minute';
case 0.1:
return '.1/min';
case 0.01:
return '.01/min';
case 0.001:
return '.001/min';
case 10:
return '10 per min';
case 100:
return '100 per min';
case 1000:
return '1000 per min';
}
},
}
},
},
},
}
);
the HTML
<div id='chart' class='chart' width='400px' height='100%'>
<canvas id="myChart" class='chart' ></canvas>
</div>
and the scripts I'm calling out to
https://cdn.jsdelivr.net/npm/chart.js#3.3.2/dist/chart.min.js
https://cdnjs.cloudflare.com/ajax/libs/luxon/1.27.0/luxon.min.js
https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon#0.2.1
I achieved animating a plot using Jukka Kurkela example here.
Now I am having trouble customizing this plot further.
Logic of the custom plot
The plot starts animating with the x-axis labels being 0-20. When the plot reaches 20 then update the x-axis to be 20-40. Increment i or 20 until the x-axis reach its limit.
How to apply the logic above to the Example below?
// Generating data
var data = [];
var prev = 100;
for (var i=0;i<200;i++) {
prev += 5 - Math.random()*10;
data.push({x: i, y: prev});
}
var delayBetweenPoints = 100;
var started = {};
var ctx2 = document.getElementById("chart2").getContext("2d");
var chart2 = new Chart(ctx2, {
type: "line",
data: {
datasets: [
{
backgroundColor: "transparent",
borderColor: "rgb(255, 99, 132)",
borderWidth: 1,
pointRadius: 0,
data: data,
fill: true,
animation: (context) => {
var delay = 0;
var index = context.dataIndex;
if (!started[index]) {
delay = index * delayBetweenPoints;
started[index] = true;
}
var {x,y} = index > 0 ? context.chart.getDatasetMeta(0).data[index-1].getProps(['x','y'],
true) : {x: 0, y: 100};
return {
x: {
easing: "linear",
duration: delayBetweenPoints,
from: x,
delay
},
y: {
easing: "linear",
duration: delayBetweenPoints * 500,
from: y,
delay
},
skip: {
type: 'boolean',
duration: delayBetweenPoints,
from: true,
to: false,
delay: delay
}
};
}
}
]
},
options: {
scales: {
x: {
type: 'linear'
}
}
}
});
<div class="chart">
<canvas id="chart2"></canvas>
</div>
<script src="https://www.chartjs.org/dist/master/Chart.js"></script>
Solved it! Instead of incrementing 20 seconds, it is incrementing every 5 seconds ahead of time. Definitely a better experience for the user.
Got help from Rowf Abd's post.
var myData = [];
var prev = 100;
for (var i=0;i<60;i++) {
prev += 5 - Math.random()*10;
myData.push({x: i, y: prev});
}
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data: [myData[0]],
pointRadius: 0,
fill: false,
borderColor: "black",
lineTension: 0
}]
},
options: {
legend: {
onClick: (e) => e.stopPropagation()
},
title:{
fontColor: 'Black'
},
layout: {
padding: {
right: 10
}
},
scales: {
xAxes: [{
type: 'linear',
ticks: {
}
}],
yAxes: [{
scaleLabel: {
// fontFamily: 'Lato',
fontSize: 19,
fontColor: "Black"
}
}]
}
}
});
var next = function() {
var data = chart.data.datasets[0].data;
var count = data.length;
var xabsmin = 20;
var xabsmax = 60;
var incVar = 5;
data[count] = data[count - 1];
chart.update({duration: 0});
data[count] = myData[count];
chart.update();
if (count < myData.length - 1) {
setTimeout(next, 500);
}
if (data[count].x < xabsmin) {
chart.config.options.scales.xAxes[0].ticks.min = xabsmin - xabsmin;
chart.config.options.scales.xAxes[0].ticks.max = xabsmin;
chart.update();
}
if(data[count].x >= xabsmin && data[count].x < (xabsmax)){
var currentT = parseFloat(data[count].x);
var modDiv = (currentT % incVar);
var tempXMax = (currentT) + (incVar - modDiv);
chart.config.options.scales.xAxes[0].ticks.max = tempXMax;
chart.config.options.scales.xAxes[0].ticks.min = tempXMax - xabsmin;
chart.update();
}
}
setTimeout(next, 500);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart"></canvas>
There are multiple charts on one page.
Each chart line is common.
I want to display a legend that is common to multiple charts like the figure.It shows and hides all chart lines with OnClick like the default legend.
THIS PICT IS FAKE
Is that possible? how?
I had tried Chart.js sync legend toggle on multiple charts, One legend, multiple charts Chart JS and etc.
But, those solutions have one chart with legend, and that legend affects other charts.
Should I hide the chart and show only the legend?
Should I draw a chart with no data?
I would be grad if you could tell me
HTML
<script src="https://rawgit.com/nnnick/Chart.js/v1.0.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0-beta/Chart.js"></script>
<div>
<canvas id="myChartA"></canvas>
</div>
<div>
<canvas id="myChartB"></canvas>
</div>
JS
var ctxA = document.getElementById("myChartA").getContext("2d");
var ctxB = document.getElementById("myChartB").getContext("2d");
let data_A1 = [{
x: "2019-01-01 00:01:38",
y: "13.0"
},
{
x: "2019-01-01 01:01:39",
y: "11.0"
},
{
x: "2019-01-01 02:01:40",
y: "16.0"
},
{
x: "2019-01-01 03:01:41",
y: "15.0"
},
{
x: "2019-01-01 04:01:42",
y: "14.0"
}
];
var data_A2 = [{
x: "2019-01-01 00:01:42",
y: 14.671
}, {
x: "2019-01-01 01:01:42",
y: 13.691
}, {
x: "2019-01-01 02:01:42",
y: 16.691
}, {
x: "2019-01-01 03:01:42",
y: 17.691
}, {
x: "2019-01-01 04:01:42",
y: 18.691
}];
let data_B1 = [{
x: "2019-01-02 00:01:38",
y: "12.0"
},
{
x: "2019-01-02 01:01:39",
y: "11.0"
},
{
x: "2019-01-02 02:01:40",
y: "13.0"
},
{
x: "2019-01-02 03:01:41",
y: "14.0"
},
{
x: "2019-01-02 04:01:42",
y: "16.0"
}
];
var data_B2 = [{
x: "2019-01-02 00:00:00",
y: 14.671
}, {
x: "2019-01-02 01:01:42",
y: 13.691
}, {
x: "2019-01-02 02:01:42",
y: 16.691
}, {
x: "2019-01-02 03:01:42",
y: 15.691
}, {
x: "2019-01-02 04:01:42",
y: 14.691
}];
var myChartA = new Chart(ctxA, {
type: 'line',
data: {
datasets: [{
label: '1st Data',
data: data_A1,
borderColor: '#0f0',
showLine: true
}, {
label: '2nd Data',
data: data_A2,
borderColor: '#f00',
showLine: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
displayFormat: 'h:mm',
}
}]
}
}
});
var myChartB = new Chart(ctxB, {
type: 'line',
data: {
datasets: [{
label: '1st Data',
data: data_B1,
borderColor: '#0f0',
showLine: true
}, {
label: '2nd Data',
data: data_B2,
borderColor: '#f00',
showLine: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
displayFormat: 'h:mm',
}
}]
}
}
});
You can create a common legend and through generateLegend api, if both the datasets are similar.
First disable the default legends though the options
legend: {
display: false
}
Then use generateLegend() api to get the data labels and set it to a common element.
<ul class="legend">
</ul>
Then add event listeners to the generated elements and target all the charts
document.querySelector('.legend').innerHTML = myChartA.generateLegend();
var legendItems = document.querySelector('.legend').getElementsByTagName('li');
for (var i = 0; i < legendItems.length; i++) {
legendItems[i].addEventListener("click", legendClickCallback.bind(this,i), false);
}
function legendClickCallback(legendItemIndex){
document.querySelectorAll('.myChart').forEach((chartItem,index)=>{
var chart = Chart.instances[index];
var dataItem = chart.data.datasets[legendItemIndex]
if(dataItem.hidden == true || dataItem.hidden == null){
dataItem.hidden = false;
} else {
dataItem.hidden = true;
}
chart.update();
})
}
A sample pen is present here
https://codepen.io/srajagop/pen/yLBJOOo
Note I am using chartjs 2.8
If anyone using React version of Chartjs particularly react-chartjs-2, I have done it using React Hooks with react-chartjs-2, see the sandbox demo
i have one external JavaScript file which returns PHP response as JSON and i tried and check and its output is as desired i want to pass this data to datapoints of canvasjs
here is my json output checked and tested returning as desired by JavaScript
{ x: 1, y: 59 },{ x: 2, y: 93 },{ x: 3, y: 477 },{ x: 4, y: 506 }
being stored in sthtml variable.
here is that part of code
var html = '';
var sthtml = '';
var ndhtml = '', downlo;
for (var i = 0; i < data.length; i++) {
z = i + 1;
downlo = data[i];
html += '<tr id="' + i + '"><td>' + z + '</td></tr>';
sthtml += '' + downlo.stchart + '';
ndhtml += '' + downlo.ndchart + '';
}
$('#down-btn').html(html);
now i am passing this sthtml and ndhtml variable value to in page javascript with this ndhtml and sthtml. i tried ''+sthtml+'' and all other probable values but data is not returning and my graph is blank
<script type="text/javascript">
window.onload = function() {
var sthtml = [];
var ndhtml = [];
var chart = new CanvasJS.Chart("chartContainer", {
axisY: {},
data: [{
dataPoints: [sthtml]
}, {
dataPoints: [ndhtml]
}],
legend: {
cursor: "pointer",
itemclick: function(e) {
chart.render();
}
}
});
chart.render();
}
</script>
i think when i load the page that time its empty and later these values are populated when users submit form so we need to update these again through some push
any help will be useful how can i pass sthtml and ndhtml values in above JavaScript datapoints
Use this
<script type="text/javascript">
window.onload = function() {
var sthtml = '';
var ndhtml = '';
var chart = new CanvasJS.Chart("chartContainer", {
axisY: {},
data: [{
dataPoints: [sthtml]
}, {
dataPoints: [ndhtml]
}],
legend: {
cursor: "pointer",
itemclick: function(e) {
chart.render();
}
}
});
chart.render();
}
</script>
Inside onload function you are setting your variable as array but if you read the documentation on it requires a Joson data
data: [
{
type: "column", //change type to bar, line, area, pie, etc
dataPoints: [
{ x: 10, y: 71 },
{ x: 20, y: 55 },
{ x: 30, y: 50 },
{ x: 40, y: 65 },
{ x: 50, y: 95 },
{ x: 60, y: 68 },
{ x: 70, y: 28 },
{ x: 80, y: 34 },
{ x: 90, y: 14 }
]
}
]
Inside the data object you need an dataPoints object, Inside the dataPoints object your json data should go. Try this I hope it will work.