在重型Canvas绘制后{Mouse}在触发时发生意外延迟

时间:2018-06-07 10:48:02

标签: javascript html html5-canvas

我正在开发基于画布的图表平台实现。可以拖动画布以左右平移图表。当按住鼠标按钮时,文档上的MouseMove处理程序将触发绘图命令。

我遇到了一个奇怪的问题 - 绘图操作在画布上越复杂,绘制完成后在文档上触发下一个MouseMove事件之前的延迟时间越长。在完成绘制和等待下一个事件发生之间,代码似乎没有做任何事情。

我拼凑了一个小提琴来表明这个问题。加载时,单击并拖动画布元素右侧,您应该看到图表平移。然后检查控制台日志,希望你能看到我看到的内容。它有点粗糙和准备好,如果你走得太远,另一方面图表将消失,但它显示了这个概念。

https://jsfiddle.net/tg6w2rj1/8/

let lastStepTime;
let isDragging, dragStartX;
let rateOffset = 0;
let reqAnimId;

$(function() {
    $(document)
        .mousedown(function(e) {
            isDragging = true;
            dragStartX = e.pageX;
        })
        .mousemove(function(e) {
            if (isDragging) {
                logStep('dragStart', true);
                if (reqAnimId) window.cancelAnimationFrame(reqAnimId);
                reqAnimId = window.requestAnimationFrame(drawOnCanvas);
                rateOffset += (e.pageX - dragStartX) * 4;
                dragStartX = e.pageX;
                logStep('dragEnd');
            }
        })
        .mouseup(function(e) {
            isDragging = false;
        });
});

function drawOnCanvas() {
    logStep('drawOnCanvasStart');

    let cw = 500,
        ch = 500;
    let ctx = document.getElementById('canvas').getContext('2d');
    let colours = ['red', 'green', 'blue'];
    ctx.clearRect(0, 0, cw, ch);

    let barsInChart = 5000;
    let rateIdx = Math.max(rateOffset, 0);
    let ratesInView = rates.slice(rateOffset, rateOffset + barsInChart);
    if (ratesInView.length == 0) return;
    let xStep = cw / barsInChart;
    let yMax = ratesInView.max(x => x.h).h,
        yMin = ratesInView.min(x => x.l).l;
    let yRange = yMax - yMin;
    let thisRateIdx = 0;

    for (let x = cw - 1; x >= 0; x -= xStep) {
        let rate = ratesInView[thisRateIdx];
        let isBull = rate.c > rate.o;
        let wcol = 'grey';
        let bcol = isBull ? 'blue' : 'red';
        let wtop = ch * ((yMax - rate.h) / yRange);
        let wbot = ch * ((yMax - rate.l) / yRange);
        let btop = ch * ((yMax - (isBull ? rate.c : rate.o)) / yRange);
        let bbot = ch * ((yMax - (isBull ? rate.o : rate.c)) / yRange);

        ctx.lineWidth = 1;
        ctx.beginPath();

        ctx.moveTo(x, wtop);
        ctx.lineTo(x, btop);
        ctx.moveTo(x, bbot);
        ctx.lineTo(x, wbot);
        ctx.strokeStyle = wcol;
        ctx.stroke();

        ctx.moveTo(x, btop);
        ctx.lineTo(x, bbot);
        ctx.strokeStyle = bcol;
        ctx.stroke();

        thisRateIdx++;

        if (thisRateIdx == ratesInView.length) {
            break;
        }
    }
    logStep('drawOnCanvasEnd');
}

function logStep(message, highlight) {
    let p = performance.now();
    console.log(`${highlight ? '%c' : ''}${(lastStepTime ? p - lastStepTime :     0).toFixed(2)}ms ${message}`, highlight ? 'color: red;' : '');
    lastStepTime = p;
}

Array.prototype.min = function(selector) {
    selector = selector || (x => x);
    return this.reduce((min, x) => {
        min = (min === undefined || selector(x) < selector(min)) ? x : min;
        return min;
    });
}

Array.prototype.max = function(selector) {
    selector = selector || (x => x);
    return this.reduce((max, x) => {
        max = (max === undefined || selector(x) > selector(max)) ? x : max;
        return max;
    });
}

ms值是自上次发送日志指令以来的时间 - 正如您所看到的,在draw方法结束和下一个拖动事件之间存在~40ms的延迟。画布绘制越复杂,延迟就越大。如果它与vsync一致,我不会期望它大于20ms。

enter image description here

有谁知道造成这种延迟的原因是什么?

我还尝试过transferControlToOffscreen和一个工作线程,但由于某种原因,在callng commit()之前处理worker中的上下文绘制命令需要比主线程长3-4倍。

0 个答案:

没有答案