I am replacing an image on a page on a timer using setInterval in javascript (it’s a stats graph that needs to be up to date).
I am using the following code:
var tid = setInterval(mycode, 20000);
function mycode() {
var theDate = new Date();
var mili = theDate.getUTCDate() + theDate.toLocaleTimeString() + theDate.getMilliseconds();
var img = $('<img />')
.attr('src', '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})')
.attr('id', 'GraphImage')
.load(function ()
{
if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
{
alert('broken image!');
}
else
{
$('#GraphImage').replaceWith(img);
}
});
img.attr('src', img.attr('src').replace("theDate", mili));
}
The image is currently just sitting in a div on the page like this:
<div style="float:left;margin-right:20px">
<img id="GraphImage" alt="Graph of Results" src="@Url.Action("ChartImage", "Home")" />
</div>
This code works and replaces the image every 20 seconds – however, even though I’m using the .load function and not replacing the image until it is fully loaded, I still get an annoying little flicker as the browser swaps the image over.
How would I go about using a jQuery fade transition/animation to smoothly swap over the two images? Ideally I’d like a way to do this without needing too much in the way of additional markup gumph or css limitations on how the image can be styled and positioned in the page.
Similar to this question: Javascript Image Reloading; flickers
However I am already using the accepted answer on this question, and still getting flicker.
Every time
mocode()fires, you appear to create a new<img />and replace the previous one. You will get a much smoother effect by reusing the same<img>– just change its src.I’m not sure exactly how your code works so it’s difficult to be certain but I think you need something like this :
You may need to build the url differently, and with a little thought the error message could be made more specific.
EDIT
Tyco, your solution looks good and if it works maybe you will want to stick with it. Meanwhile I’ve been playing with a similar idea but dramatically different code centered around jQuery’s Deferreds. These are a little mind-blowing if you’ve not used them but are very useful when you have asynchronous tasks.
In your case, you have three sequential asynchronous tasks, fetching the graph URL, loading the graph image, and fading out the previous graph to reveal the new version.
Coding this is fairly straightforward but the code needs to be error-tolerant – in particular it needs to cater for server responses (URL and the image itself) arriving back in the wrong order or after the next firing of the
setInterval. Deferreds help enormously by providing the means to cancel function chains established at the previous iteration.With a 20 second interval, there shouldn’t be a problem, but one day the internet/server may be running exceptionally slowly or you may decide to reduce the interval.
Unless you have used Deferreds before, the code below will look very alien but, barring errors on my part, it should do the job.
Javascript:
CSS:
HTML:
As you will see, everything is organised into a bunch of fairly concise functions, each a variation on a common theme, namely :-
The last task,
graph_swap(), is straightforward and the error handlergraph_error()is slightly different.See comments in code for further details.
In addition to handling errors and tardy server responses, a MAJOR advantage of this approach is that the main iterator function,
fetchGraph()becomes VERY simple. The really clever line is a set of chainedpipe()commands which sequence the tasks and route any errors to the error handler.I have tested this as much as I can and think that, like your own solution, it will give a smooth transition.