My web page loads and automatically creates a chart with data it pulls from an API I wrote.
Ive also got a HTML input that allows me to select the month. I have added an event listener to that input that triggers a function to draw a new chart based on the month i have selected (it recalls the api too with these new parameters).
It looked like it worked, but on further inspection, I realised that the previous chart was behind the new chart.
Is there a way i can remove the old chart?
<div class="chart_div" style="max-height: 400px; max-width: 800px; margin: 5px">
<label for="monthSelector">Start month:</label>
<input
type="month"
id="monthSelector"
name="start"
min="{{min_date}}"
max="{{today_date}}"
value="{{today_date}}"
/>
<canvas id="myChart" width="400" height="400"> </canvas>
</div>
<script>
var canvas = document.getElementById("myChart");
const context = canvas.getContext("2d");
var monthSelector = document.getElementById("monthSelector");
// event listener for month slider
monthSelector.addEventListener("input", function () {
selected_date = monthSelector.value + "-01";
drawChart(selected_date);
});
var today = monthSelector.value + "-01";
// Draw chart upon loading page
drawChart(today);
function drawChart(date) {
x_labels = [];
data_set_scratches = [];
data_set_medical_scores = [];
context.clearRect(0, 0, canvas.width, canvas.height);
var url_scratches =
"http://127.0.0.1:8000/api/get-daily-scratch-count/" + date + "/";
var url_medical_scores =
"http://127.0.0.1:8000/api/get-daily-medical-score/" + date + "/";
// get x label based on dates of selected month
var date_vals = date.split("-");
var num_days = getDaysInMonth(date_vals[1], date_vals[0]);
console.log(num_days);
for (var i = 1; i <= num_days; i++) {
var num = minTwoDigits(i);
x_labels.push(num);
}
// call api to fetch the data
Promise.all([
fetch(url_scratches)
.then((res) => res.json())
.then(function (data) {
var scratches = data;
var dateIndex = 0;
var scratchesIndex = 0;
while (scratchesIndex < scratches.length) {
var scratchDates = scratches[scratchesIndex].date.split("-"); // Splits date into list ["YYYY", "MM", "DD"]
// if dates are equal, push total and increase both index
if (scratchDates[2] == x_labels[dateIndex]) {
data_set_scratches.push(scratches[scratchesIndex].total);
dateIndex += 1;
scratchesIndex += 1;
// if dates are not equal, push 0 and increase only date index
} else {
data_set_scratches.push(0);
dateIndex += 1;
}
}
console.log(data_set_scratches);
}),
fetch(url_medical_scores)
.then((res) => res.json())
.then(function (data) {
var medicalScores = data;
var dateIndex = 0;
var scoreIndex = 0;
while (scoreIndex < medicalScores.length) {
var scoreDates = medicalScores[scoreIndex].date.split("-"); // Splits date into list ["YYYY", "MM", "DD"]
// if dates are equal, push score then increase both index
if (scoreDates[2] == x_labels[dateIndex]) {
data_set_medical_scores.push(medicalScores[scoreIndex].score);
dateIndex += 1;
scoreIndex += 1;
// if dates are not equal, push 0 and increase only date index
} else {
data_set_medical_scores.push(0);
dateIndex += 1;
}
}
console.log(data_set_medical_scores);
}),
]).then(function () {
// Creat chart from api Data
let chartTest = new Chart(myChart, {
type: "line",
data: {
labels: x_labels,
datasets: [
{
label: "Scratch Total",
fill: false,
data: data_set_scratches,
borderColor: "green",
borderWidth: 1,
lineTension: 0,
backgroundColor: "red",
pointBackgroundColor: "red",
pointBorderColor: "red",
pointHoverBackgroundColor: "red",
pointHoverBorderColor: "red",
},
{
data: data_set_medical_scores,
label: "Medical Score",
fill: false,
borderColor: "orange",
borderWidth: 1,
lineTension: 0,
backgroundColor: "#e755ba",
pointBackgroundColor: "#55bae7",
pointBorderColor: "#55bae7",
pointHoverBackgroundColor: "#55bae7",
pointHoverBorderColor: "#55bae7",
},
],
},
options: {
title: {
display: true,
text: "Daily Scratches/Medical Scores",
},
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
xAxis: [
{
ticks: {
stepSize: 1,
autoSkip: false,
},
},
],
},
},
});
});
}
// function to get num of days in month
function getDaysInMonth(month, year) {
return new Date(year, month, 0).getDate();
}
function minTwoDigits(n) {
return (n < 10 ? "0" : "") + n;
}
</script>
What I would really like to do is delete the existing chart before the api is called again? Any help would be greatly appreciated.
call the destroy method of the chart object
.destroy()
Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js. This must be called before the canvas is reused for a new chart.
// Destroys a specific chart instance
myLineChart.destroy();
https://www.chartjs.org/docs/latest/developers/api.html?h=destroy
Related
EDIT: I have narrowed it down to something like this:
for (i = 0; i < data.length; i++) {
const newCanvas = document.createElement("canvas");
newCanvas.id = data[i].design_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
createChart([data[i].design_name], [data[i].design_start, data[i].design_end]);
}
With the create chart making the chart id = to the array 'labels':
const myChart = new Chart(
document.getElementById(labels),
config
);
I am attempting to create a tool that creates an 'n' number of charts in ChartJS and save each of them as images. Currently, designButtonClick() sends the 'event_fky' value to
getDesigns(event_fky) in my controller. This method returns all designs with that foreign key. In turn, the chart plots each design on the chart. I need to evolve this into
something that can make a group individual charts for each design based on how many designs there are. My current solution, still conceptual, is to have methods in my controller
create chart variables 'chartData [data here]' and 'labels[datahere]' while looping through the designs returned from getDesigns, and sending those back to the JS script createChart
'n' number of times for each design. It would also send html chart/html element ids based on the design_name attribute to send back to createChart. This way, it is create a unique
chart 'n' number of times.
To save the charts as images, I would use the same set of element ids generated by getDesigns to send the charts to images using JS' toBase64Image() function and saving them to the
user's system.
Is this the best way of solving this problem? Or is this spaghetti, and is there a better method for this? My attempts to find better online answers have only resulted in docs on
updating one chart dynamically, not creating a dynamic number of charts. Much help is appreciated, code is below as well as a screenshot of the current chart output.
JavaScript:
var labels = [];
var cData = [];
function designButtonClick() {
var event_fky = 3;
$.ajax({
url: 'Tree/getDesigns',
type: 'POST',
data: { event_fky }
}).done(function (data) {
for (i = 0; i < data.length; i++) {
labels.push(data[i].design_name);
cData.push([data[i].design_start, data[i].design_end])
}
createChart(labels, cData);
});
}
function createChart(labels, cData) {
const data = {
labels: labels,
datasets: [{
barThickness: 2,
categoryPercentage: .5,
label: 'Design Time',
data: cData,
backgroundColor: [
'rgba(255, 26, 104, 0.2)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 1,
borderSkipped: false,
borderRadius: 20
}]
};
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: 6000,
ticks: {
stepSize: 1000
}
}
}
}
};
const myChart = new Chart(
document.getElementById('myChart'),
config
);
}
C# Controller:
public ActionResult getDesigns(int? event_fky)
{
var designs = from e in _context.designs
where (event_fky.HasValue ? e.event_fky == event_fky : e.event_fky == null)
select new
{
design_pky = e.design_pky,
design_name = e.design_name,
design_start = e.design_start,
design_end = e.design_end
};
return this.Json(designs, JsonRequestBehavior.AllowGet);
}
Designs Table:
--------Design--------
design_pky |int
event_fky |int
design_name |varchar
design_start |number
design_end |number
Screenshot of Chart
This is a working answer for the javascript:
var eventList = function () {
var tmp = null;
$.ajax({
'async': false,
url: 'Tree/getEventIDs',
type: 'POST',
data: {},
'success': function (data) {
tmp = data;
}
});
return tmp;
}();
for (var i = 0; i < eventList.length; i++) {
event_fky = eventList[i].event_pky;
event_name = eventList[i].event_name;
event_length = eventList[i].event_end;
var designList = function () {
var tmpi = null;
$.ajax({
'async': false,
url: 'Tree/getDesigns',
type: 'POST',
data: {event_fky},
'success': function (data1) {
tmpi = data1;
}
});
console.log(event_fky);
console.log(tmpi);
return tmpi;
}();
var dLabels = [];
var dLengths = [];
for (var j = 0; j < designList.length; j++) {
dLabels.push(designList[j].design_name);
dLengths.push([designList[j].design_start, designList[j].design_end]);
}
const newCanvas = document.createElement("canvas");
newCanvas.id = event_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
if (dLabels.length != 0) {
createChart(dLabels, dLengths, event_name, event_length);
}
}
}
function createChart(labels, cData, evName, evLen) {
// setup
const data = {
labels: labels,
datasets: [{
barThickness: 4,
categoryPercentage: .5,
label: evName,
data: cData,
backgroundColor: [
'rgba(' + Math.random() * 85 + ', ' + Math.random() * 170 + ', ' + Math.random() * 255 + ', 1)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 0,
borderSkipped: false,
borderRadius: 20
}]
};
// config
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: evLen,
ticks: {
stepSize: 100
}
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById(evName),
config
);
return myChart;
}
I created a dynamic line chart based on some input data. The intention is that the customer can indicate with a dropdown on which month the "Investment" should start.
So, for example, if the "Investment" does not start until month 6, then that line should only start at 6 on the x-axis. But the other lines "Case" and "ROI" should still just start at 1.
I've tried several things but to no avail.
I tried changing the x-axis "min ticks" based on the selection the user made, but that makes all lines start at another point instead of the "Investment" line only. Another problem is that every number before the selection then dissapears from the x-axis. But I really want to keep every number from 1-60, even if the user chooses to start the "Investment" on month 10, for example.
I would really appreciate some help! Thanks.
Here's my fiddle: https://jsfiddle.net/js5pha24/
var options = {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Case',
data: [],
backgroundColor: 'rgba(152,164,135, 0.5)',
borderColor: 'rgb(152,164,135)',
fill: false
}, {
label: 'Case',
data: [],
backgroundColor: 'rgba(145,139,167, 0.5)',
borderColor: 'rgb(145,139,167)',
fill: false
}, {
label: 'Case',
data: [],
backgroundColor: 'rgba(206,157,206, 0.5)',
borderColor: 'rgb(206,157,206)',
fill: false
}]
},
options: {
legend: {
display: true,
position: "top"
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
autoSkip: true,
maxRotation: 0,
minRotation: 0
}
}],
yAxes: [{
ticks: {
callback: value => {
return "€ " + value;
}
}
}]
}
}
}
for (let i = 1; i <= 60; i++) {
options.data.labels.push(i);
const caseMonth = 118187 * i;
options.data.datasets.find(set => set.label === "Case").data.push(caseMonth);
const investMonth = 500000 + (20000 * i);
options.data.datasets.find(set => set.label === "Investment").data.push(investMonth);
const roiMonth = caseMonth - investMonth;
options.data.datasets.find(set => set.label === "ROI").data.push(roiMonth);
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
canvas { background-color : #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.js"></script>
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>
You can put null values on the chart data so one line can start after the others. For example if you want the investment line start at month 10, you can replace the the first ten investMonth values with null.
If understood correctly you still want to use the investMonth value in the roiMonth calculation so I created "investMonthValue" so only investment will get null if it is less than investmentStartMonth.
let investmentStartMonth = 10
for (let i = 1; i <= 60; i++) {
options.data.labels.push(i);
const caseMonth = 118187 * i;
options.data.datasets.find(set => set.label === "Case").data.push(caseMonth);
let investMonth = 500000 + (20000 * i);
let investMonthValue = i<investmentStartMonth?null:investMonth
options.data.datasets.find(set => set.label === "Investment").data.push(investMonthValue);
const roiMonth = caseMonth - investMonth;
options.data.datasets.find(set => set.label === "ROI").data.push(roiMonth);
}
I am trying to develop a Crash Game, where a multiplier (Y) increases exponentially and dynamically over time (X), causing the chart to re-render at each tick.
You can see an example of the chart game here
TL;DR: I am trying to achieve a "zoom-out" effect of the chart as my ticks increase in values (x,y).
Where my code fails is when ticks data values (x,y, respectively time and multiplier) surpass suggestedMax tick values. The only reason I am using suggestedMax is to have some labels on the chart at the beginning.
I have tried to achieve this by using both line and scatter chart type, but the final outcome is simply unacceptable from a performance point of view.
Here is my code:
const HomePlaygroundView = () => {
var chart = undefined;
const chartText = useRef(null);
let last_tick_received = 0;
const incrementChart = () => {
last_tick_received += 100;
};
const onServerTickReceived = (multiplier, msLapsed) => {
// Update chart multiplied
if (chart.data.datasets[0].data.length >= 100) {
// Halve the array to save performance (lol)
for (let i = 1; i < 100; i += 2) {
console.log("Reducing chart data");
chart.data.datasets[0].data.splice(i, 1);
}
}
chart.data.datasets[0].data.push({
x: msLapsed,
y: multiplier,
});
// This is basically my zoom out effect implementation...
if (multiplier >= 2.5) { // Increase suggestedMax only if bigger data needs to be fit
chart.options.scales.yAxes[0].ticks.suggestedMax = multiplier;
}
if (msLapsed > 9000) { // Same logic as above
chart.options.scales.xAxes[0].ticks.suggestedMax = msLapsed;
}
if (msLapsed < 10000) {
// Fit msLapsed in the pre-existing 10 seconds labels of x axis (this is a hell of a workaround)
let willInsertAtIndex = undefined;
for (let i = 0; i < chart.data.labels.length; i++) {
let current = chart.data.labels;
if (current < msLapsed) {
// Insert at i + 1? Check the next index if it's bigger than msLapsed
let nextVal = chart.data.labels[i + 1];
if (nextVal) {
if (nextVal > msLapsed) {
willInsertAtIndex = i + 1;
break;
}
} else {
willInsertAtIndex = i + 1;
break;
}
}
}
if (willInsertAtIndex) {
chart.data.labels.splice(willInsertAtIndex, 0, msLapsed);
}
} else {
chart.data.labels.push(msLapsed);
}
// Decimate data every so and so
chartText.current.innerText = `${multiplier}x`;
// Re-render canvas
chart.update();
};
useEffect(() => {
console.log("rendered chart");
var ctx = document.getElementById("myChart").getContext("2d");
ctx.height = "350px";
chart = new Chart(ctx, {
// The type of chart we want to create
type: "scatter",
// The data for our dataset
data: {
labels: [...Array(11).keys()].map((s) => s * 1000),
datasets: [
{
label: "testt",
backgroundColor: "transparent",
borderColor: "rgb(255, 99, 132)",
borderWidth: 10,
showLine: true,
borderJoinStyle: "round",
borderCapStyle: "round",
data: [
{
y: 1,
x: 0,
},
],
},
],
animation: {
duration: 0,
},
responsiveAnimationDuration: 100, // animation duration after a resize
},
// Configuration options go here
options: {
spanGaps: true,
events: [],
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
elements: {
point: {
radius: 0,
},
},
scales: {
xAxes: [
{
type: "linear",
ticks: {
callback: function (value, index, values) {
let s = Math.round(value / 1000);
return s.toString() + "s";
//return value;
},
autoSkipPadding: 100,
autoSkip: true,
suggestedMax: 10000,
stepSize: 100,
min: 0,
},
},
],
yAxes: [
{
ticks: {
// Include a dollar sign in the ticks
callback: function (value, index, values) {
return Math.round(value).toString() + "x"; // Display steps by 0,5
},
min: 1,
suggestedMax: 2.5,
stepSize: 0.01,
autoSkip: true,
autoSkipPadding: 150,
},
},
],
},
},
});
let lastTick = 1.0;
let dateStart = new Date().getTime();
setTimeout(() => {
chartText.current.innerText = "Go!";
setTimeout(() => {
setInterval(() => {
let timePassed = new Date().getTime() - dateStart;
//console.log(timePassed);
let calculateTick = Math.pow(
1.01,
0.00530133800509 * timePassed
).toFixed(2);
console.log(timePassed);
onServerTickReceived(calculateTick, timePassed);
}, 50);
}, 1000);
}, 2000);
});
const classes = useStyles();
return (
<div className={classes.canvasContainer}>
<span ref={chartText} className={classes.canvasText}>
Ready...?
</span>
<canvas id="myChart"></canvas>
</div>
);
};
export default HomePlaygroundView;
I have an experimental page where is a chart.js canvas.
Right now I add data points one by one from a webwocket data packet.
In that packet I got 200 data, which I want to add to my canvas, but not one by one, but all of it at once.
Here is my code:
<body>
<div id="lineGraph" class="chart-container">
<canvas id="line-chart"></canvas>
</div>
</body>
<script>
function myFunction1() {
maxDataPoints = document.getElementById("adatszelesseg").value;
if(maxDataPoints >= 200){
animateDur = 0;
animationisokay = false;
}else if (maxDataPoints < 200){
animateDur = 1000;
animationisokay = true;
}
init();
}
function removeData(){
dataPlot.data.labels.shift();
dataPlot.data.datasets[0].data.shift();
}
function addData(label, data) {
if(dataPlot.data.labels.length > maxDataPoints) removeData();
dataPlot.data.labels.push(label);
dataPlot.data.datasets[0].data.push(data);
dataPlot.update();
}
function init() {
dataPlot = new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: [],
datasets: [{
backgroundColor: "rgba(159,170,174,0.2)",
borderWidth: 1.3,
hoverBackgroundColor: "rgba(232,105,90,0.8)",
hoverBorderColor: "orange",
data: [],
label: "Analog.Sign.(%)",
borderColor: "#f7f7f7",
fill: true
}]
},
options: {
legend: {
fontColor: "white",
labels: {
fontColor: "white",
fontSize: 18
}
},
animation: animationisokay,
scales: {
xAxes: [{
scaleLabel: {
fontColor: "white",
fontSize: 18,
display: true,
labelString: 'Sec / MicroSec'
}
}],
yAxes: [{
scaleLabel: {
fontColor: "white",
fontSize: 18,
display: true,
}
}]
},
responsiveAnimationDuration: animateDur,
maintainAspectRatio: true,
animation: {
animationEasing: 'linear',
duration: animateDur
}
}
})
};
webSocket1 = new WebSocket('ws://' + window.location.hostname + ':81/');
webSocket1.onmessage=function(a){
var t = a.data;
if(t.indexOf('}')>-1){
var j = t.substring(0, t.length - 1);
var today = new Date();
var m = today.getSeconds() + ":" + today.getMilliseconds();
console.log(j);
addData(m, j);
}};
</script>
So as you can see, I have a websocket event, where my data comes in as a.data.
I put it into a variable called t for further modification.
After that I have to look at it if it is have the string "}" than this is my data.
After that I add the j data to the canvas. And that is repeating while I got my 200 data to the canvas.
The data comes on the websocket almost each microsec.
The problem is I can see on the canvas that there is a delay in the display in each data, like if it is scrolling itself from right to the left.
I tried to collect my 200 data to an array, and pass that array to the canvas but it did not worked.
On the background I have a c++ server code.
I send the data like this:
for (int i = 0; i < datawidth; i++) {
String json = "";
json += String(vRealBuff[i] / (float)40.95);
json += "}";
webSocket.broadcastTXT(json.c_str(), json.length());
}
It is 200 data one by one, but it doesn't matter if I send all of it at once from the server, or collect it on the JavaScript code and add all of it at once.
The data does not display if I add an array of data to it.
Solved by collecting the data on an array and passing that array to the chart.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I want to show only the last point. Can somebody help with this?
Expected
Actual
Well, hello!
You can set the pointRadius to zero, eg:
var myChart = new Chart(
ctx, {
type: 'line',
data: {
labels: [...]
datasets: [
{
data: [...],
pointRadius: 0, # <<< Here.
}
]
},
options: {}
})
For many points (like your question) change pointRadius to array:
pointRadius: [0, 0, 0, 0, 0, 0, 5]
Follow my full example in Fiddle
If this helped you, check it out
Update
I just saw Edi's answer as I submitted mine and he mentioned the pointRadius. I noticed this too messing around and as he stated, you can pass in an array to simplify this.
I wrote a method below to "grow" the array (scaling with the data) in order to ensure that the last data point has a radius > 0.
The custom Line chart appears to render much smoother because the native draw function has been modified.
Chart.js 2.0 introduces the concept of controllers for each dataset. Like scales, new controllers can be written as needed.
I copied the source for controller.line.js and modified the loop for the draw() method to only draw the last point. This is the easiest solution without having to figure out how to set an array of radius values.
/** Initialize array with radius of n and queue values ahead of it */
function pointRadiusLast(radius, length, initialArray) {
var result = initialArray || [ radius ];
while (result.length < length) result.unshift(0); // Place zeros in front
return result;
}
// https://www.chartjs.org/docs/latest/developers/charts.html
var valueOrDefault = Chart.helpers.valueOrDefault;
const lineEnabled = (dataset, options) => valueOrDefault(dataset.showLine, options.showLines)
Chart.defaults.derivedLine = Chart.defaults.line;
var customController = Chart.controllers.line.extend({
/** If you want to EXTEND the draw function.
draw: function(ease) {
Chart.controllers.line.prototype.draw.call(this, ease); // Override the super method
var meta = this.getMeta();
var pt0 = meta.data[0];
var radius = pt0._view.radius;
var ctx = this.chart.chart.ctx;
ctx.save();
// Custom drawing...
ctx.restore();
}
If you want to OVERRIDE the draw function...
*/
draw: function(ease) {
var me = this;
var chart = me.chart;
var meta = me.getMeta();
var points = meta.data || [];
var area = chart.chartArea;
var ilen = points.length;
var halfBorderWidth;
var i = 0;
if (lineEnabled(me.getDataset(), chart.options)) {
halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
Chart.helpers.canvas.clipArea(chart.ctx, {
left: area.left,
right: area.right,
top: area.top - halfBorderWidth,
bottom: area.bottom + halfBorderWidth
});
meta.dataset.draw();
Chart.helpers.canvas.unclipArea(chart.ctx);
}
// Draw the points
for (; i < ilen; ++i) {
if (i === ilen - 1) { // Only the last or simply... points[ilen - 1].draw(area)
points[i].draw(area);
}
}
}
});
Chart.controllers.derivedLine = customController; // Specify type on chart below
var data = [
{ month : 'January' , low : 25, high : 43 },
{ month : 'February' , low : 27, high : 47 },
{ month : 'March' , low : 35, high : 56 },
{ month : 'April' , low : 44, high : 67 },
{ month : 'May' , low : 54, high : 76 },
{ month : 'June' , low : 63, high : 85 }
];
var additionalData = [
{ month : 'July' , low : 68, high : 89 },
{ month : 'August' , low : 66, high : 87 },
{ month : 'September' , low : 59, high : 81 },
{ month : 'October' , low : 46, high : 69 },
{ month : 'November' , low : 37, high : 59 },
{ month : 'December' , low : 30, high : 48 }
];
var defaults = {
fill: false,
lineTension: 0.1,
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBorderWidth: 2,
//pointRadius: 0, <-- Do not specify this, we will supply it below
pointHitRadius: 10
};
/** Initialize array with radius of n and queue values ahead of it */
function pointRadiusLast(radius, length, initialArray) {
var result = initialArray || [ radius ];
while (result.length < length) result.unshift(0);
return result;
}
var dataSets = [
Object.assign({
label: "Low",
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
pointBorderColor: "rgba(75,192,192,1)",
pointHoverBackgroundColor: "rgba(75,192,192,1)",
pointHoverBorderColor: "rgba(220,220,220,1)",
data: data.map(item => item.low)
}, defaults),
Object.assign({
label: "High",
backgroundColor: "rgba(192,75,75,0.4)",
borderColor: "rgba(192,75,75,1)",
pointBorderColor: "rgba(192,75,75,1)",
pointHoverBackgroundColor: "rgba(192,75,75,1)",
pointHoverBorderColor: "rgba(192,75,75,1)",
data: data.map(item => item.high)
}, defaults)
];
var chartOptions = {
title : {
text : 'Weather Averages for Washington, DC',
display : true
},
showLines: true
};
var defaultChart = Chart.Line(document.getElementById('default-line-chart'), {
data: {
labels: data.map(item => item.month),
datasets: dataSets.map(dataset => $.extend(true, {}, dataset, {
pointRadius : pointRadiusLast(5, data.length)
}))
},
options : $.extend(true, {}, chartOptions, {
title : {
text : chartOptions.title.text + ' (Default)'
}
})
});
var customChart = new Chart(document.getElementById('custom-line-chart'), {
type: 'derivedLine',
data: {
labels: data.map(item => item.month),
datasets: dataSets.map(dataset => $.extend(true, {}, dataset, {
pointRadius : 5
}))
},
options : $.extend(true, {}, chartOptions, {
title : {
text : chartOptions.title.text + ' (Custom)'
}
})
});
setTimeout(function() {
var category = 'month', fields = [ 'low', 'high' ];
var counter = additionalData.length;
var intervalId = setInterval(function() {
if (counter --> 0) {
var record = additionalData[additionalData.length - counter - 1];
addData(defaultChart, record, category, fields);
addData(customChart, record, category, fields);
} else {
clearInterval(intervalId);
}
}, 1000); // Update every second
}, 1000); // 1 second delay
function addData(chart, data, xField, yFields) {
chart.data.labels.push(data[xField]);
chart.data.datasets.forEach((dataset, i) => {
dataset.data.push(data[yFields[i]]);
if (chart.config.type === 'line') { // Only the normal Line chart
dataset.pointRadius = pointRadiusLast(5, dataset.data.length);
}
});
chart.update();
}
<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.3/Chart.min.js"></script>
<canvas id="default-line-chart" width="400" height="120"></canvas>
<canvas id="custom-line-chart" width="400" height="120"></canvas>