简体   繁体   中英

How to force repaint in JS?

What I am trying to achieve:

  1. user clicks on an element
  2. the screen shows the "calculation in progress" screen
  3. the system performs time-consuming math calculations
  4. the screen shows the result ("done")

Here's the stripped code:

<div id ="di" onclick="calc()">initial</div>
<script>

function calc()
{
var n,a=0;
document.getElementById('di').textContent="calculation in progress";
for(n=0;n<1000000000;n++) // Here's the time consuming calculation
    {
    a=a+n; // for clarity's sake, I removed a complicated math formula here
    }
document.getElementById('di').textContent="done "+a;
}
</script>

When I run it and click on the div, it takes a while and then changes the text to "done", so the user does not see the "calculation in progress" message at all - this is my problem.

To force a screen repaint to display the message before the calculations start, other threads suggest modifying CSS, hiding and immediately unhiding the element or using setTimeout, but nothing worked.

This will be a program that draws complicated math objects (fractals) and I will use canvas instead of a div, but I simplified the example above. Because of the future graphic interface, using "alert()" is not an option - the "calculation in progress" screen should turn to "done" immediately upon completion of the calculations.

IMO an easy way to handle this is to have your computation performed in "small" chunks by a timer function, for example:

function calcFractal(x0, y0, x1, y1) {
    ... compute the fractal for the tile (x0, y0)-(x1, y1) ...
}

var x = 0, y = 0;

function nextTile() {
    calcFractal(x, y, x+tw, y+th);
    x += tw;
    if (x >= width) {
        x = 0;
        y += th;
    }
    if (y < height) setTimeout(nextTile, 0);
}

nextTile();

This allows you to show progress (including for example a low resolution of the fractal, the percentage of the computation) and to allow interruption (with onclick events on a stop button for example).

If the tiles are not tiny the overhead will be acceptable, still maintaining the page reasonably responsive to both repaints and user interaction.

You need to either wait a millisecond or do the calculations with a Worker.

The first example is probably the easiest, instead of calling calc directly, create a new function

function caller() {
     // insert "calculation in progress" in the body
    setTimeout(calc, 1);
}

Then call caller .

Because modern browsers may delay redrawing for better frame rate, versions with setTimeout may not work with too low time-outs.

If possible you need to use requestAnimationFrame . If its not posible then @Bálint answer should work, but with much bigger timeout (in my tests in Firefox its began work with timeout near 20-30). Actual timeout value is browser dependent (and probably system dependent too)

function very_long_func(){
   el= document.getElementById('di');

   requestAnimationFrame( function(){
      //edit dom for new frame;
      el.textContent = 'calculation in progress' 
      //browser will wait for this functions end, before start redraw.
      //So actual calucation must run outside of it
      setTimeout( function_with_actual_calculation, 1 ); 

   })
}
function function_with_actual_calculation(){
     //..your math here + updating textContent to "done" in the end.
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM