如何在JS中强制重绘?
How to force repaint in JS?
我想要达到的目标:
- 用户点击了一个元素
- 屏幕显示"calculation in progress"屏幕
- 系统进行耗时的数学计算
- 屏幕显示结果("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.
}
我想要达到的目标:
- 用户点击了一个元素
- 屏幕显示"calculation in progress"屏幕
- 系统进行耗时的数学计算
- 屏幕显示结果("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.
}