【问题标题】:HTML Canvas in for loop only displaying after loop completesfor循环中的HTML Canvas仅在循环完成后显示
【发布时间】:2018-07-04 03:59:02
【问题描述】:

我在制作进度图时遇到了一些麻烦。这是我第一次使用画布,所以我对这个概念有点陌生。这个页面将成为学校作业的一个小素数基准。我还没有完成算法,所以现在算起来了。我想让一个图表向用户显示基准测试的进度,这样它看起来不像页面刚刚冻结。我将基准分解为“冲刺”,设备将计算一段时间内的数字,然后更新图表。问题是,图表似乎直到“基准”结束时才更新。有什么建议吗?

javascript 如下(execBench 可能是最相关的函数):

function startBench() {
    // move to benchmark display
    //showPage("bench");
    jQuery.mobile.changePage("#bench");
    setTimeout(
        function () {
            // run benchmark
            var score = execBench(10);
            //set score and move page
            $(".result").text(score);
            setTimeout(function () {
                showPage("result");
            }, 4000);

        }, 2000);
}

function debugmsg(message) {
    console.log(message);
}

function execBench(time) {
    var graphUpdateRate = 2; // horizontal "resolution" of graph/sprint length in s
    var sprintCount = Math.floor(time / graphUpdateRate);
    debugmsg("Running " + sprintCount + " " + graphUpdateRate + "-second sprints");
    var currentTime = new Date();
    var sprintDeadline = currentTime;
    var counter = 0; // "score" for the end, # of primes generated
    var lastPrime = 0;
    var record = []; // datapoints for graph

    for (var i = 0; i < sprintCount; i++) {
        
        // perform calculations
        sprintDeadline = incrementDate(new Date(), graphUpdateRate);
        while (currentTime < sprintDeadline) {
            currentTime = Date.now();
            lastPrime = generatePrime(lastPrime);
            counter++;
        }

        // report progress
        record.push(counter);
        drawGraph(document.getElementById('progGraph'), record, sprintCount);
    }
    return counter;
}

function generatePrime(min) {
    //placeholder for algorithm
    min++;
    return min;
}

function drawGraph(canvas, dataPoints, maxPoints) {
    var context = canvas.getContext('2d');
    var width = canvas.width;
    var height = canvas.height;
    var xIncrement = width / maxPoints;
    var xBegin = 0;
    var prevPoint = 0;
    var yScale = -1 * height / Math.max(...dataPoints);

    //reset canvas
    canvas.width = canvas.width;
    context.clearRect(0, 0, canvas.width, canvas.height);

    //move context to bottom right and set scale
    context.translate(0, height);
    context.scale(1, 1);

    context.strokeStyle = "#ed1e79";

    for (dataPoint in dataPoints) {
        currentPoint = (dataPoints[dataPoint] * yScale);

        context.beginPath();
        context.moveTo(xBegin, prevPoint);
        context.lineTo(xBegin + xIncrement, currentPoint);
        context.lineWidth = 3;
        context.lineCap = 'round';
        context.stroke();

        prevPoint = currentPoint;
        xBegin += xIncrement;
    }

    debugmsg(Math.max(...dataPoints));
    return;
}

function incrementDate(date, seconds) {
    return new Date(date.getTime() + (seconds * 1000));
}

【问题讨论】:

  • 你可能正在寻找 requestAnimationFrame()
  • 您需要使用诸如 requestAnimationFrame() 或 setTimeout() 之类的计划更新来执行 drawGraph() 的每次迭代,以免阻塞线程。

标签: javascript jquery canvas html5-canvas


【解决方案1】:

您的while 循环处于“阻塞”状态。它会占用 CPU,不允许 javascript 和计算机上的许多其他功能执行任何操作。

改为使用setTimeout(fn, t) 安排下一次更新。

setTimeout() 是非阻塞的。它的fn 将在 t 毫秒时间(或此后不久)在一个新的事件线程中执行。

在 setTimouts 之间,您的计算机处理器将有能力指示显卡渲染画布。

【讨论】:

  • 投反对票的人,有你的信念的勇气。解释你认为错的地方!
【解决方案2】:

作为使用 requestAnimationFrame() 的示例,您可以尝试这样的操作。

function execBench(time) {
    var graphUpdateRate = 2; // horizontal "resolution" of graph/sprint length in s
    var sprintCount = Math.floor(time / graphUpdateRate);
    debugmsg("Running " + sprintCount + " " + graphUpdateRate + "-second sprints");
    var currentTime = new Date();
    var sprintDeadline = currentTime;
    var counter = 0; // "score" for the end, # of primes generated
    var lastPrime = 0;
    var record = []; // datapoints for graph

    var i = 0;
    (function drawSprint() {
    
        // perform calculations
        sprintDeadline = incrementDate(new Date(), graphUpdateRate);
        while (currentTime < sprintDeadline) {
            currentTime = Date.now();
            lastPrime = generatePrime(lastPrime);
            counter++;
        }

        // report progress
        record.push(counter);
        drawGraph(document.getElementById('progGraph'), record, sprintCount);
        
        i++;
        if (i < sprintCount) {
          requestAnimationFrame(drawSprint);
        }
    })();
    
    return counter;
}

【讨论】:

  • 这非常适合让图表实时更新,谢谢。它的副作用是让它在基准测试中途移动到结果页面,但这是我可以解决的问题。只是为了确保我理解正确,requestAnimationFrame 是否递归地使用该本地“绘图”函数?
  • 没错。 drawSprint() 执行一次,然后在下一次重绘之前使用 requestAnimationFrame 请求浏览器调用自身,这会递归地继续直到 sprint 完成。
猜你喜欢
  • 2015-01-21
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 2015-03-04
  • 2014-07-19
  • 1970-01-01
  • 2022-06-13
  • 1970-01-01
相关资源
最近更新 更多