即使我已经尝试了很多强制重绘的方法,进度条也不会在繁重的工作中重新绘制

时间:2017-02-08 12:38:25

标签: javascript jquery html css repaint

我有一个JavaScript方法,迭代大约200个输入,插入值,并触发onchange事件。在Chrome中大约需要2000毫秒,在IE中大约需要10000毫秒。

我想制作一个可以显示进度的进度条。

现在,问题是浏览器不会重新绘制进度条,直到流程结束时,它会立即达到100%。

在这里研究之后,我发现了三种方法并尝试了所有这些方法,但它们都不起作用:

  • 使用零或非零(例如200毫秒)超时的setTimeout运行进度条更新
  • 访问元素或相邻元素的height属性,这将强制重绘
  • 显示/隐藏元素并同时访问offsetHeight属性。

这是我的代码:

    <div class="cg-progress-bar">
        <div class="cg-progress-bar-completed">
            &nbsp;
        </div>
        <div class="cg-inline-block cg-progress-bar-force-repaint">
            &nbsp;
        </div>
    </div>

JavaScript的:

var progressBarCurrent = 0;
var progressBarTotal = 236;

$.each(sqlData, function(column, value){
        //doing some work here to update values in inputs

        //update progress bar
        progressBarCurrent++;
        if (progressBarCurrent % 22 === 0) { //don't call too often
            var percentageComplete = Math.floor( (progressBarCurrent/progressBarTotal)*100 ) + "%";    

            var bar = $(".cg-progress-bar-completed").width(percentageComplete)[0];

            //hack 1
            bar.style.display = 'none';
            bar.offsetHeight;
            bar.style.display = '';

            //hack 2
            setTimeout(function() { bar.style.display = 'block'}, 0);

            //hack 3
            $(".cg-progress-bar-completed").animate({ width: percentageComplete }, 100).height();

            //hack 4 - insert empty text node
            bar.appendChild(document.createTextNode(' '));
            $(bar).hide().show(0);
            bar.appendChild(document.createTextNode(' '));

            //hack 5 - nuclear option
            $(window).trigger("resize");
        }
    }
});

如何强制浏览器重新绘制?

3 个答案:

答案 0 :(得分:2)

可能你的进程太重了,正在崩溃执行线程。这意味着虽然它计算浏览器会略微冻结,但在处理计算时可能无法与UI进行其他交互。

为了避免这种情况,HTML5提供了Web worker技术。这是一种在浏览器环境中模拟多任务处理的方法,允许您执行繁重的任务,避免浏览器冻结。

这里有一篇关于这项技术的优秀介绍性文章,我已成功用于创建一个文件上传器,可以上传Gb长度文件,维护一个实时进度条:

https://www.html5rocks.com/en/tutorials/workers/basics/

我希望它有所帮助。

答案 1 :(得分:1)

好的,所以我之前的anwser并没有像我希望的那样工作,所以这是一个有效的例子:

的index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>-</title>
    <style type="text/css" media="screen">
        div{
            margin: 0px;
            padding: 0px;
            height: 30px;
        }
        #wrapper{
            width: 300px;
            border: solid 1px black;
        }
        #progressBar{
            width: 0px;
            background-color: black;
            color: grey;
        }
    </style>
</head>
<body>
    <div id="wrapper"><div id="progressBar"></div></div>
    <script type="text/javascript">

        var progressBar = document.getElementById("progressBar")    
        var workerThread = new Worker('worker.js');

        function drawProgress(progress){
            var percentageComplete = Math.floor( (progress.current/progress.maximum)*100 ) + "%";
            progressBar.style.setProperty("width",percentageComplete)
        }

        workerThread.onmessage = function(e){
            drawProgress(e.data)
        }
    </script>
</body>
</html>

worker.js

var sqlData = new Array( 236 );
var i = 0;
for ( let entry of sqlData ) {
    console.log( 'iteration of each' );
    postMessage( {
        current: i,
        maximum: sqlData.length
    } )
    for ( var n = 0; n < 200000; n++ ) {
        for ( let entry of sqlData ) {
            entry = Math.random()
        }
    }
    i++;
}

我认为这可能是最好的选择,因为没有涉及黑客只是这种任务的方式。唯一的问题是在工作者中很难获得JQuery。最好的方法是在主线程中完全检索SQL数据集,然后传输到worker中。

答案 2 :(得分:0)

@ K48补充它应该与IE9 +兼容,所以这里是IE8 +支持的解决方案:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>IE8 compatible</title>
    <style type="text/css">
        div{
            margin: 0px;
            padding: 0px;
            height: 30px;
        }
        #wrapper{
            width: 300px;
            border: solid 1px black;
        }
        #progressBar{
            width: 0px;
            background-color: black;
            color: grey;
        }
    </style>
</head>
<body>
    <div id="wrapper"><div id="progressBar"></div></div>
    <script type="text/javascript">
        var continueExecution = false;
        var progressBar = document.getElementById( "progressBar" );
        var i = 0;
        var sqlData = new Array( 236 );
        var lastDraw = (new Date).valueOf();

        function heavyloadFunction() {
            var entry = sqlData[ i ]

            // do something with the entry
            for ( var n = 0; n < 2000; n++ ) {
                for ( var h = 0; h < sqlData.length; h++ ) {
                    var cEntry = sqlData[h]
                    cEntry = Math.random()
                }
            }

            // finish entry work

            if(i < sqlData.length){
                i++;
                continueExecution = true;
            }else{
                console.log("finished")
                continueExecution = false;
            }
        }

        function drawProgress( progress ) {
            var percentageComplete = Math.floor( ( progress.current / progress.maximum ) * 100 ) + "%";
            progressBar.style.width = percentageComplete
        }

        function shouldReDraw(){
            var dNow = (new Date).valueOf();
            if(dNow - lastDraw > 16){
                // around 60fps
                lastDraw = dNow;
                return true;
            }else{
                return false;
            }
        }

        function excutionLoop() {
            heavyloadFunction();

            if ( continueExecution ) {
                if(shouldReDraw()){
                    drawProgress({
                        current: i,
                        maximum: sqlData.length
                    })

                    window.setTimeout( excutionLoop, 0 )
                }else{
                    excutionLoop()
                }
            }else{
                drawProgress({
                    current: i,
                    maximum: sqlData.length
                })
            }           
        }

        excutionLoop();
    </script>
</body>
</html>

这个想法非常简单,你可以在每次循环迭代时中断执行,这样就可以进行重绘。

编辑:做了一点改进,防止绘图成为瓶颈。