如何在JS中强制重绘?

How to force repaint in JS?

我想要达到的目标:

  1. 用户点击了一个元素
  2. 屏幕显示"calculation in progress"屏幕
  3. 系统进行耗时的数学计算
  4. 屏幕显示结果("done")

这是剥离的代码:

<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>

当我 运行 它并单击 div 时,需要一段时间然后将文本更改为 "done",因此用户看不到 "calculation in progress" 消息 - 这是我的问题。

要在计算开始前强制屏幕重绘以显示消息,其他线程建议修改 CSS、隐藏并立即取消隐藏元素或使用 setTimeout,但没有任何效果。

这将是一个绘制复杂数学对象(分形)的程序,我将使用 canvas 而不是 div,但我简化了上面的示例。由于未来的图形界面,使用 "alert()" 不是一个选项 - "calculation in progress" 屏幕应在计算完成后立即变为 "done"。

您需要等待一毫秒或使用 Worker 进行计算。

第一个例子可能是最简单的,不是直接调用calc,而是创建一个新函数

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

然后调用caller.

IMO 处理这个问题的一个简单方法是通过定时器函数在 "small" 块中执行计算,例如:

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();

这允许您显示进度(包括例如分形的低分辨率、计算的百分比)并允许中断(例如在停止按钮上使用 onclick 事件)。

如果图块不是很小,开销将是可以接受的,仍然保持页面对重绘和用户交互的合理响应。

由于现代浏览器可能会延迟重绘以获得更好的帧速率,因此带有 setTimeout 的版本可能无法在太短的超时时间下工作。

如果可能,您需要使用 requestAnimationFrame。如果它不可行,那么@Bálint 的回答应该有效,但超时时间要长得多(在我在 Firefox 中的测试中,它开始工作时超时时间接近 20-30)。实际超时值取决于浏览器(也可能取决于系统)

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.
}