Reputation: 2533
I need to refresh an HTML5 canvas every two or three seconds.
setInterval(writeCanvas, 2000);
This canvas is filled with points and lines. Each abscissa and ordinate is stored in an XML file. So before updating the canvas I do an async request to the file on the server.
The problem is that the canvas blinks. I guess it disappears while the async request is running.
How could I get around this issue?
Here is the code of writeCanvas:
function drawLines(ctx, back, front, width, xArray, yArray) {
ctx.strokeStyle = back;
ctx.fillStyle = front;
ctx.lineWidth = width;
ctx.beginPath();
ctx.moveTo(xArray[0], yArray[0]);
for (var i=1; i<xArray.length; i++) {
ctx.lineTo(xArray[i],yArray[i]);
}
ctx.fill();
ctx.stroke();
ctx.closePath();
}
function drawPoint(ctx, back, front, x, y, radius, startAngle, endAngle) {
ctx.strokeStyle = back;
ctx.fillStyle = front;
ctx.beginPath();
ctx.arc(x,y,radius,startAngle,endAngle,endAngle);
ctx.fill();
ctx.stroke();
ctx.closePath();
}
function writeLabel(ctx, color, font, x, y, text) {
ctx.fillStyle = color;
ctx.font = font;
ctx.beginPath();
if(x < 0) {
x = 0;
}
ctx.fillText(text, x, y);
ctx.fill();
ctx.closePath();
}
function writeCanvas()
{
var elem = document.getElementById('profileCanvas');
if (!elem || !elem.getContext) {
return;
}
var ctx = elem.getContext('2d');
if (!ctx) {
return;
}
// apply the final size to the canvas
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
$.get('profileStatus.xml', function(xml) {
if(xml) {
var testPoints = new Array();
$(xml).find('TP').each(function() {
var selected = $(this).find('SELECTED:first').text();
if(selected == "YES") {
var name = $(this).find('MODULE_NAME:first').text();
var state = $(this).find('STATE:first').text();
var tp = new ProfileTp(name, state, selected);
testPoints.push(tp);
}
});
$.get('profile.xml', function(data) {
if(data) {
profileWidth = parseFloat($(data).find('MAIN > PROFILE > DIM_W').first().text());
profileHeight = parseFloat($(data).find('MAIN > PROFILE > DIM_H').first().text());
var backgroundColor = '#ddd';
var color = '#323232';
ctx.translate(0,canvasHeight);
var xArray = new Array();
var yArray = new Array();
$(data).find('PROFILE > POINT > X').each(function(){
var x=parseFloat($(this).text());
xArray.push(x);
});
$(data).find('PROFILE > POINT > Y').each(function(){
var y=parseFloat($(this).text());
yArray.push(y);
});
drawLines(ctx, backgroundColor, color, 2, xArray, yArray);
var finalArray = new Array();
$(data).find('TESTPOINTS > TP').each(function() {
var labelName = $(this).find('MODULE_NAME:first').text();
var tp = $.grep(testPoints, function(obj){ return obj.NAME == labelName; });
if(tp.length == 1) {
$(this).find('IHM').each(function(){
tp[0].LABEL_X = parseFloat($(this).find('LABEL > X:first').text());
tp[0].LABEL_Y = parseFloat($(this).find('LABEL > Y:first').text());
tp[0].MARKER_X = parseFloat($(this).find('MARKER > X:first').text());
tp[0].MARKER_Y = parseFloat($(this).find('MARKER >Y:first').text());
});
finalArray.push(tp[0]);
}
});
for(var i=0; i<finalArray.length; i++) {
writeLabel(ctx, color, fontSize+"px Arial",(finalArray[i].MARKER_X+finalArray[i].LABEL_X),(finalArray[i].MARKER_Y+finalArray[i].LABEL_Y), finalArray[i].NAME);
drawPoint(ctx, backgroundColor, color, finalArray[i].MARKER_X, finalArray[i].MARKER_Y, 8, 0, 2*Math.PI);
}
} else {
console.error('No XML test points returned');
}
});
}
});
}
There are two XML files. One contains all the points, lines and labels. The second contains only the points and labels that have to be displayed.
Upvotes: 0
Views: 1241
Reputation:
Crogo already mention the probable cause in the answer, but as a work around you could do:
if (elem.width !== canvasWidth || elem.height !== canvasHeight) {
// apply the final size to the canvas
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
}
The canvas size is only set if the size change.
You should also try to avoid using setInterval
(what if client is on a slow/unstable connection that makes it take longer than 2 second to load data..). If the download is still in progress and setInterval
triggers you will initiate another download while the first one is still downloading. You will also risk get "double drawings" to the canvas as these calls stack up in the event queue:
Rather trigger an setTimeout
from inside your writeCanvas()
:
function writeCanvas()
{
//... load and draw
setTimeout(writeCanvas, 1500); //compensate for time
}
Of course, if the data MUST be loaded every two seconds this would be inaccurate (not that setInterval
is.. they both give an estimate only).
Upvotes: 0
Reputation: 493
Setting a canvas' dimensions clears it entirely, so the lines :
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
are likely to make your canvas 'blink'. GET requests are asynchronous so the canvas is cleared way before points data are computed and drawn.
To fix this, change the dimensions inside your requests callbacks, right before drawing.
Upvotes: 3