Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Every time <code>mocode()</code> fires, you appear to create a new <code>&lt;img /&gt;</code> and replace the previous one. You will get a much smoother effect by reusing the same <code>&lt;img&gt;</code> - just change its src. </p> <p>I'm not sure exactly how your code works so it's difficult to be certain but I think you need something like this :</p> <pre><code>function graphError() { alert('Graph: reload failed'); } var $graphImg = $("#GraphImage").on('error', graphError); var tid = setInterval(mycode, 20000); function mycode() { var theDate = new Date(); var mili = theDate.getUTCDate() + theDate.toLocaleTimeString() + theDate.getMilliseconds(); $.ajax({ url: '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})', }).done(function(srcURL) { $graphImg.attr('src', srcURL.replace("theDate", mili)); }).error(graphError); } </code></pre> <p>You may need to build the url differently, and with a little thought the error message could be made more specific.</p> <h2>EDIT</h2> <p>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. </p> <p>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. </p> <p>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 <code>setInterval</code>. Deferreds help enormously by providing the means to cancel function chains established at the previous iteration. </p> <p>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.</p> <p>Unless you have used Deferreds before, the code below will look very alien but, barring errors on my part, it should do the job. </p> <p>Javascript:</p> <pre><code>$(function() { var $graphImg1 = $("#GraphImage1"); var $graphImg2 = $("#GraphImage2"); var promises = { fetchGraphUrl: null, loadImg: null, fadeOut: null }; var graph_errors = { threshold: 5,//set low for testing, higher in production environment. count: 0 }; var interval = 5;//seconds $graphImg2.on('error', function() { if(promises.loadImg) { promises.loadImg.reject(); } }).on('load', function() { if(promises.loadImg) { promises.loadImg.resolve(); } }); function graph_fetchURL(milli) { if(promises.fetchGraph) { promises.fetchGraph.reject(milli, 'fetchURL', 'timeout'); } var dfrd = promises.fetchGraph = $.Deferred().fail(function() { jqXHR.abort(); }); var jqXHR = $.ajax({ url: '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})' }).done(function(srcURL) { dfrd.resolve(milli, srcURL); }).fail(function(jqXHR, textStatus, errorThrown) { dfrd.reject(milli, 'ajax', textStatus); }); return dfrd.promise(); } function graph_loadImg(milli, srcURL) { if(promises.loadImg) { promises.loadImg.reject(milli, 'loadImg', 'timeout'); } //An extra deferred is needed here because $graphImg2's handlers don't know about milli. var dfrd = $.Deferred(); promises.loadImg = $.Deferred().done(function() { dfrd.resolve(milli); }).fail(function() { dfrd.reject(milli, 'loadGraph', 'load error'); }); $graphImg2.attr('src', srcURL.replace("theDate", milli)); return dfrd.promise(); } function graph_fade(milli) { if(promises.fadeOut) { promises.fadeOut.reject(milli, 'fade', 'timeout'); } promises.fadeOut = $.Deferred(); $graphImg2.show(); $graphImg1.fadeOut('fast', function() { promises.fadeOut.resolve(milli); }); return promises.fadeOut.promise(); } function graph_swap() { $graphImg1.attr('src', $graphImg2.attr('src')).show(); $graphImg2.hide(); } function graph_error(timestamp, phase, txt) { var txt = txt ? (' (' + txt + ')') : ''; console.log(timestamp + ': fetchGraph failed in the ' + phase + ' phase' + txt); if(++graph_errors.count &gt;= graph_errors.threshold) { clearInterval(tid); console.log('fetchGraph errors exceeded threshold (' + graph_errors.threshold + ')'); } return $.Deferred().promise();//an unresolved, unrejected promise prevents the original promise propagating down the pipe. } function fetchGraph() { var now = new Date(); var milli = now.getUTCDate() + now.toLocaleTimeString() + now.getMilliseconds(); graph_fetchURL(milli) .pipe(graph_loadImg, graph_error) .pipe(graph_fade, graph_error) .pipe(graph_swap, graph_error);//this .pipe() chain is the glue that puts everything together. } fetchGraph(); var tid = setInterval(fetchGraph, interval * 1000); }); </code></pre> <p>CSS:</p> <pre><code>#graphWrapper { position: relative; float: left; margin-right: 20px; } #GraphImage, #GraphImage2 { position: absolute; left: 0; top: 0; width: XXpx; height: YYpx; } #GraphImage2 { display: none; } </code></pre> <p>HTML:</p> <pre><code>&lt;div id="graphWrapper"&gt; &lt;img id="GraphImage1" alt="Graph of Results" src="" /&gt; &lt;img id="GraphImage2" alt="" src="" /&gt; &lt;/div&gt; </code></pre> <p>As you will see, everything is organised into a bunch of fairly concise functions, each a variation on a common theme, namely :-</p> <ul> <li>to reject any outstanding task of the same kind from a previous iteration</li> <li>to create a deferred that can be rejected at the next iteration</li> <li>to perform the task itself</li> <li>to return a promise derived from the Deferred</li> </ul> <p>The last task, <code>graph_swap()</code>, is straightforward and the error handler <code>graph_error()</code> is slightly different.</p> <p>See comments in code for further details. </p> <p>In addition to handling errors and tardy server responses, a MAJOR advantage of this approach is that the main iterator function, <code>fetchGraph()</code> becomes VERY simple. The really clever line is a set of chained <code>pipe()</code> commands which sequence the tasks and route any errors to the error handler.</p> <p>I have tested this as much as I can and think that, like your own solution, it will give a smooth transition.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    1. COInteresting idea. I've tried this out and sadly it's not noticably different from the version which replaces the whole img tag. I think the issue relates to the actual rendering of the new image by the browser (the flicker becomes far more obvious as the image dimensions increase). I still have a feeling that the solution is related to rendering the image transparent and then fading it in rather than trying to speed up the swapping.
      singulars
    2. COTyco, something else has just occurred to me. Using my approach, and assuming the graph image always to have the same dimensions, ensure the `<img>` has `height` and `width` properties set. This will prevent the document needing to reflow (or thinking it needs to reflow) when the image is replaced, hence much less work for the browser's rendering engine.
      singulars
    3. CONo joy :( This seems like a reasonable plan for helping minimize the browser's workload, but I'm still not sure that's the route to go - as a side note, I've noticed this kind of re-draw flicker often happens in default Win Forms controls on resize events. With the jQuery animation effects I'm sure it should be possible to load both the existing and the updated images and then fade between them before discarding the original. I'm just not experienced enough with jQeury to get the best technique - I'd probably end up with a bunch of ugly html markup that wasn't actually needed.
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload