Why is this function causing the browser refresh button to grey out? - javascript

I am testing a real time graph with chart js, and wrote the simple code below. Every second, I want to redraw the graph with some new data (shift and push in the datas and labels array). Despite how ugly it is, it works, and updates the graph every second. However, the refresh button greys out (for some milliseconds) every time the function executes, even if there is no interaction with the network. Why?
setInterval(function() {
var ctx = document.getElementById("line-chart-1").getContext("2d");
var chart1 = window.chart1;
var d1 = chart1.data.datasets[0].data;
var lab1 = chart1.data.labels;
var newData = dataPointGenerator(70, 80);
d1.shift();
d1.push(newData.data);
lab1.shift();
lab1.push(newData.time);
window.chart1 = new Chart(ctx, {
type: 'line',
data: {
labels : lab1,
datasets : [
{
label: 'names[0]',
fill: false,
borderColor: "rgb(75,192,192)",
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: "rgb(75,192,192)",
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgb(75,192,192)",
pointHoverBorderColor: "rgb(75,192,192)",
pointHoverBorderWidth: 2,
pointRadius: 1,
pointHitRadius: 10,
data : d1
}
]
},
options: {
animation : false,
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: 'kW'
}
}]
}
}
});
function dataPointGenerator(start, end) {
var rand = Math.floor(Math.random() * (end - start + 1) + start);
var date = new Date();
return {
data : rand,
time : date
}
};

Related

Is there a way to change the react-chart-js tooltip only on a graph?

I have the following:
const labels = historicosData.map(historico => {
const localDate = new Date(historico.fechaIngreso);
return localDate.toLocaleString('es-ES', { month: 'long' })
});
//const labels = historicosData.map(({ anio }) => anio);
// const labels = [123, 321, 456, 123, 4568]
const data = {
labels,
datasets: [
{
label: 'Valores históricos',
fill: false,
backgroundColor: 'rgba(75,192,192,0.4)',
borderColor: 'rgba(75,192,192,1)',
pointBorderColor: 'rgba(75,192,192,1)',
pointBackgroundColor: '#fff',
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: 'rgba(75,192,192,1)',
pointHoverBorderColor: 'rgba(220,220,220,1)',
pointHoverBorderWidth: 10,
pointRadius: 10,
pointHitRadius: 10,
data: historicosData.map(({ valor }) => valor),
},
],
};
And it displays the following graph:
Is there a way where I can change the default message of the tooltip (the one saying 'mayo') for a personalized message?
My main purpose is to make the X bottom axis to display only the months' name and display the whole date when the user's hover over one specific point
You can use the options prop as follows to customize the tooltip title:
const options = {
responsive: true,
plugins:
{
tooltip:
{
callbacks:
{
title: (context) =>
{
return context[0].label
},
}
},
},
}
Then pass options as a prop to your Line component:
<Line options = {options} data = {data} />
In the example above, I'm returning context[0].label which is the label you already see on the x-axis. You can change the return value to anything you want, and you can put other necessary logic within the callback as well.
For example, you may choose to return context[0].parsed.x rather than the label. Or you may choose to use this value to put together the final title to return.
Read more about tooltip configuration in the docs.

How to maintain chartjs / ng2-charts gradient on window resize?

I had applied some gradient rule to my chartjs chart. And it looks great as you can see on the below
However, when the browser window is resized (i.e. width of window is smaller), the gradient is ruined (bottom blue colors disappeared). Screenshot:
I want to maintain the graph's gradient with all values and fit the different widths (responsive). Is there any way to do that? Here is what I had tried but didn't work:
.TS File
ngAfterViewInit() {
const ctx = (<HTMLCanvasElement>this.myChart.nativeElement).getContext('2d');
const purple_orange_gradient = ctx.createLinearGradient(0, 200, 0, 20);
purple_orange_gradient.addColorStop(0.1, "#000279");
purple_orange_gradient.addColorStop(0.2, "#0000F2");
purple_orange_gradient.addColorStop(0.3, "#0362FD");
purple_orange_gradient.addColorStop(0.4, "#04D3FD");
purple_orange_gradient.addColorStop(0.5, "#45FFB7");
purple_orange_gradient.addColorStop(0.6, "#B7FF46");
purple_orange_gradient.addColorStop(0.7, "#FFD401");
purple_orange_gradient.addColorStop(0.8, "#FE6500");
purple_orange_gradient.addColorStop(0.9, "#F30004");
purple_orange_gradient.addColorStop(1, "#7E0100");
const bar_chart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: []=this.histogramLabels.reverse(),
datasets: [{
borderColor: purple_orange_gradient,
pointBorderColor: purple_orange_gradient,
pointBackgroundColor: purple_orange_gradient,
pointHoverBackgroundColor: purple_orange_gradient,
pointHoverBorderColor: purple_orange_gradient,
pointBorderWidth: 10,
pointHoverRadius: 10,
pointHoverBorderWidth: 1,
pointRadius: 3,
fill: true,
backgroundColor: purple_orange_gradient,
borderWidth: 4,
data: []=this.histogramGraphData
}]
},
options: {
legend: {
display:false,
position: "bottom"
},
scales: {
yAxes: [{
ticks: {
display: false,
fontColor: "rgba(0,0,0,0.5)",
fontStyle: "bold",
beginAtZero: true,
maxTicksLimit: 1,
padding: 20,
},
gridLines: {
drawTicks: false,
display: false
}
}],
xAxes: [{
gridLines: {
zeroLineColor: "transparent",
},
ticks: {
padding: 20,
beginAtZero: true,
fontColor: "rgba(0,0,0,0.5)",
fontStyle: "bold"
}
}]
}
}
}
)
}
.HTML
<div class="row my-2">
<div class="col-md-6">
<canvas id=”myChart” #myChart height="130"></canvas>
</div>
</div>
HTML Canvas' createLinearGradient() depends on the y axis coordinates that you pass in as argument. You had passed in a static 200 every time (i.e. ctx.createLinearGradient(0, 200, 0, 20);).
That's why the gradient's steps remains the same everytime. For the gradient to update, you have to recalculate the height of the <canvas> element on window resize and pass it in to createLinearGradient() again.
You can accomplish this by:
Separating the block where you create the gradient into a separate function. eleHeight retrieves the height of the canvas element.
generateGradient(){
let eleHeight = this.myChart.nativeElement.offsetHeight;
// console.log(eleHeight)
let purple_orange_gradient: CanvasGradient = this.myChart.nativeElement.getContext('2d').createLinearGradient(0, eleHeight, 0, 20);
purple_orange_gradient.addColorStop(0.1, "#000279");
purple_orange_gradient.addColorStop(0.2, "#0000F2");
purple_orange_gradient.addColorStop(0.3, "#0362FD");
purple_orange_gradient.addColorStop(0.4, "#04D3FD");
purple_orange_gradient.addColorStop(0.5, "#45FFB7");
purple_orange_gradient.addColorStop(0.6, "#B7FF46");
purple_orange_gradient.addColorStop(0.7, "#FFD401");
purple_orange_gradient.addColorStop(0.8, "#FE6500");
purple_orange_gradient.addColorStop(0.9, "#F30004");
purple_orange_gradient.addColorStop(1, "#7E0100");
return purple_orange_gradient;
}
Add a onresize event handler to your containing <div> and generate the gradient again. You also need to programatically update the chart every time you make a change to re-render it.
<div style="display: block; max-height: 100%" (window:resize)="onResize($event)" >
...
</div>
onResize(event?){
// console.log("onResize");
this.barChartData.forEach((d, i) => {
d.backgroundColor = this.generateGradient();
})
this.chart.chart.update(); //update the chart to re-render it
}
Update the barchartData's properties (that uses gradient) in ngAfterViewInit. We need to do this here because we only want the height of the <canvas> element with data populated. Without data populated, the element is much smaller.
ngAfterViewInit(){
this.barChartData.forEach((d, i) => {
d.backgroundColor = this.generateGradient();
});
this.chart.chart.update(); //update the chart to re-render it
}
Have a look at this Stackblitz example⚡⚡ I have created.
You have to change the gradient whenever your canvas is resizing. Took me a while to figure out a good structure to minimize lines of code and optimize performance. This is the best I could achieve.
There are exeptions when the chart.js onResize() fires though but I couldn't solve this issue completly bulletproof. But for simple resizes it should work.
Complete code (same code in JSBin with live preview):
let sData = {}
sData.labels = []
sData.data = []
const count = 50
for (let x = 0; x < count; x++) {
sData.data.push(Math.floor(Math.random()*100))
sData.labels.push(x)
}
const canvas = document.getElementById('chart')
const ctx = canvas.getContext("2d")
let purple_orange_gradient
function updateGradient() {
let bottom = bar_chart.chartArea.bottom
let top = bar_chart.chartArea.top
purple_orange_gradient = ctx.createLinearGradient(0, bottom+top, 0, top)
purple_orange_gradient.addColorStop(0.1, "#000279")
purple_orange_gradient.addColorStop(0.2, "#0000F2")
purple_orange_gradient.addColorStop(0.3, "#0362FD")
purple_orange_gradient.addColorStop(0.4, "#04D3FD")
purple_orange_gradient.addColorStop(0.5, "#45FFB7")
purple_orange_gradient.addColorStop(0.6, "#B7FF46")
purple_orange_gradient.addColorStop(0.7, "#FFD401")
purple_orange_gradient.addColorStop(0.8, "#FE6500")
purple_orange_gradient.addColorStop(0.9, "#F30004")
purple_orange_gradient.addColorStop(1.0, "#7E0100")
return purple_orange_gradient
}
const bar_chart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: sData.labels,
datasets: [{
borderColor: purple_orange_gradient,
pointBorderColor: purple_orange_gradient,
pointBackgroundColor: purple_orange_gradient,
pointHoverBackgroundColor: purple_orange_gradient,
pointHoverBorderColor: purple_orange_gradient,
pointBorderWidth: 10,
pointHoverRadius: 10,
pointHoverBorderWidth: 1,
pointRadius: 3,
fill: true,
backgroundColor: purple_orange_gradient,
borderWidth: 4,
data: sData.data
}]
},
options: {
legend: {
display: false,
position: "bottom"
},
scales: {
yAxes: [{
ticks: {
display: false,
fontColor: "rgba(0,0,0,0.5)",
fontStyle: "bold",
beginAtZero: true,
maxTicksLimit: 1,
padding: 20,
},
gridLines: {
drawTicks: false,
display: false
}
}],
xAxes: [{
gridLines: {
zeroLineColor: "transparent",
},
ticks: {
padding: 20,
beginAtZero: true,
fontColor: "rgba(0,0,0,0.5)",
fontStyle: "bold"
}
}]
},
onResize: function(chart, size) {
// onResize gradient change
changeGradient()
}
}
});
// Initial gradient change
changeGradient()
function changeGradient() {
let newGradient = updateGradient()
bar_chart.data.datasets[0].borderColor = newGradient
bar_chart.data.datasets[0].pointBorderColor = newGradient
bar_chart.data.datasets[0].pointBackgroundColor = newGradient
bar_chart.data.datasets[0].pointHoverBackgroundColor = newGradient
bar_chart.data.datasets[0].pointHoverBorderColor = newGradient
bar_chart.data.datasets[0].backgroundColor = newGradient
bar_chart.update()
}

Update ChartJs chart using $scope in AngularJS

I'm having some issues when trying to update a chart's data using $scope.
I know there's a function to update charts myChart.update(); but I can't get to update the char when I put it in a $scope.
The following code gets the chart's data and then tries to update the chart. The problem comes at $scope.lineChart.update();. It looks like chartjs can't detect any changes.
The following code is executed after triggering a select, so the chart has an initial data and the following code just tries to update it.
This does not work: $scope.lineChart.update();
$scope.getLineChartMaxData().then(function () {
$scope.getLineChartMinData().then(function () {
$scope.lineChart.update();
});
});
The chart function:
$scope.fillLineChart = function () {
console.log("FILLING LINE CHART");
const brandProduct = 'rgba(0,181,233,0.5)'
const brandService = 'rgba(0,173,95,0.5)'
var data1 = $scope.lineChartMaxWeekData;
var data2 = $scope.lineChartMinWeekData;
var maxValue1 = Math.max.apply(null, data1)
var maxValue2 = Math.max.apply(null, data2)
var minValue1 = Math.min.apply(null, data1)
var minValue2 = Math.min.apply(null, data2)
var maxValue;
var minValue;
if (maxValue1 >= maxValue2) {
maxValue = maxValue1;
} else {
maxValue = maxValue2;
}
if (minValue1 >= minValue2) {
minValue = minValue2;
} else {
minValue = minValue1;
}
$scope.minValue = minValue;
$scope.maxValue = maxValue;
var ctx = document.getElementById("recent-rep-chart");
if (ctx) {
ctx.height = 250;
$scope.lineChart = new Chart(ctx, {
type: 'line',
data: {
labels: $scope.lineChartMaxWeekLabels,
datasets: [{
label: 'Valor',
backgroundColor: brandService,
borderColor: 'transparent',
pointHoverBackgroundColor: '#fff',
borderWidth: 0,
data: data1
},
{
label: 'My Second dataset',
backgroundColor: brandProduct,
borderColor: 'transparent',
pointHoverBackgroundColor: '#fff',
borderWidth: 0,
data: data2
}
]
},
options: {
maintainAspectRatio: true,
legend: {
display: false
},
responsive: true,
scales: {
xAxes: [{
gridLines: {
drawOnChartArea: true,
color: '#f2f2f2'
},
ticks: {
fontFamily: "Poppins",
fontSize: 12
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
maxTicksLimit: 5,
stepSize: 50,
max: maxValue,
fontFamily: "Poppins",
fontSize: 12
},
gridLines: {
display: true,
color: '#f2f2f2'
}
}]
},
elements: {
point: {
radius: 0,
hitRadius: 10,
hoverRadius: 4,
hoverBorderWidth: 3
}
}
}
});
}
};
UPDATE: $scope.lineChart.destroy(); works well, but I don't want to destroy the chart and build it again because it is built with another sizes.

How can I have different values for the chart and the tooltip in chart.js?

I want to use a radar chart from chart.js to display several attributes compared to average values.
For example, I might want to display the size, weight and ipd (interpupillary distance) of a specific human compared to the average.
Now, if I simply put in the raw numbers into the chart, that would look pretty weird, because the values of each of the attributes can't be compared with each other and would stretch the radar diagram in a weird way. So what I do instead is take a ratio from every attribute and put it in as data. For example this could mean that I have a size of 1.10 if someone is 10% taller than average, or a weight of 0.95, if someone is 5% lighter than average.
But now when hovering over the data point, the tooltip shows the ratio that I put in as data value, so the tooltip would tell me Size: 1.10. I would like to have the real value in the tooltip instead, like Size: 1.85m.
How can I have a 'tooltip value' that is different from the actual data that is used for drawing the chart? My current code is below.
HTML:
<canvas id="chart-human"></canvas>
JS:
var ctx = document.getElementById('chart-human');
var data = {
labels: ['Size', 'Weight', 'IPD'],
datasets: [
{
label: 'Sam Smith',
data: [1.10, 0.95, 1.23]
},
{
label: 'Average',
data: [1, 1, 1]
}
]
};
var options = {};
var chart = new Chart(ctx, {
type: 'radar',
data: data,
options: options
});
You could accomplish that using tooltip's callbacks function ...
var ctx = document.getElementById('chart-human');
var real_data = [
['1.85m', '100lbs', '120%'],
['1.95m', '90lbs', '150%']
];
var data = {
labels: ['Size', 'Weight', 'IPD'],
datasets: [{
label: 'Sam Smith',
data: [1.10, 0.95, 1.23],
backgroundColor: 'rgba(0,119,204,0.2)',
borderColor: 'rgba(0,119,204, 0.5)',
borderWidth: 1,
pointBackgroundColor: 'rgba(0, 0, 0, 0.4)'
}, {
label: 'John Doe',
data: [1.20, 0.85, 1.43],
backgroundColor: 'rgba(255, 0, 0, 0.15)',
borderColor: 'rgba(255, 0, 0, 0.45)',
borderWidth: 1,
pointBackgroundColor: 'rgba(0, 0, 0, 0.4)'
}, {
label: 'Average',
data: [1, 1, 1],
backgroundColor: 'rgba(0, 255, 0, 0.15)',
borderColor: 'rgba(0, 255, 0, 0.45)',
borderWidth: 1,
pointBackgroundColor: 'rgba(0, 0, 0, 0.4)'
}]
};
var options = {
tooltips: {
callbacks: {
title: function(t, d) {
let title = d.datasets[t[0].datasetIndex].label;
return title;
},
label: function(t, d) {
let title = d.datasets[t.datasetIndex].label;
let label = d.labels[t.index];
let value = (title != 'Average') ? real_data[t.datasetIndex][t.index] : d.datasets[t.datasetIndex].data[t.index];
return label + ': ' + value;
}
}
}
};
var chart = new Chart(ctx, {
type: 'radar',
data: data,
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<canvas id="chart-human"></canvas>

chart.js 2, animate right to left (not top-down)

the jsfiddle below shows the problem.
The first data inserts are fine, but when the length of the data set is capped at 10 you see the undesired behaviour where data points are animated top-down instead of moving left. It's extremely distracting.
http://jsfiddle.net/kLg5ntou/32/
setInterval(function () {
data.labels.push(Math.floor(Date.now() / 1000));
data.datasets[0].data.push(Math.floor(10 + Math.random() * 80));
// limit to 10
data.labels = data.labels.splice(-10);
data.datasets[0].data = data.datasets[0].data.splice(-10);
chart.update(); // addData/removeData replaced with update in v2
}, 1000);
Is there a way to have the line chart move left having the newly inserted data point appear on the right? As opposed to the wavy distracting animation?
thanks
This code uses streaming plugin and works as expected.
http://jsfiddle.net/nagix/kvu0r6j2/
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming#1.5.0/dist/chartjs-plugin-streaming.min.js"></script>
var ctx = document.getElementById("chart").getContext("2d");
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: "My First dataset",
backgroundColor: "rgba(95,186,88,0.7)",
borderColor: "rgba(95,186,88,1)",
pointBackgroundColor: "rgba(0,0,0,0)",
pointBorderColor: "rgba(0,0,0,0)",
pointHoverBackgroundColor: "rgba(95,186,88,1)",
pointHoverBorderColor: "rgba(95,186,88,1)",
data: []
}]
},
options: {
scales: {
xAxes: [{
type: 'realtime'
}]
},
plugins: {
streaming: {
onRefresh: function(chart) {
chart.data.labels.push(Date.now());
chart.data.datasets[0].data.push(
Math.floor(10 + Math.random() * 80)
);
},
delay: 2000
}
}
}
});
You should use 2.5.0 chartsjs
here it works :
http://jsfiddle.net/kLg5ntou/93
var data = {
labels: ["0", "1", "2", "3", "4", "5", "6"],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(95,186,88,0.7)",
strokeColor: "rgba(95,186,88,1)",
pointColor: "rgba(0,0,0,0)",
pointStrokeColor: "rgba(0,0,0,0)",
pointHighlightFill: "rgba(95,186,88,1)",
pointHighlightStroke: "rgba(95,186,88,1)",
data: [65, 59, 80, 81, 56, 55, 40]
}
]
};
var ctx = document.getElementById("chart").getContext("2d");
var chart = new Chart(ctx, {type: 'line', data: data});
setInterval(function () {
chart.config.data.labels.push(Math.floor(Date.now() / 1000));
chart.config.data.datasets[0].data.push(Math.floor(10 + Math.random() * 80));
// limit to 10
chart.config.data.labels.shift();
chart.config.data.datasets[0].data.shift();

Categories