我正在开发基于画布的图表平台实现。可以拖动画布以左右平移图表。当按住鼠标按钮时,文档上的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。
有谁知道造成这种延迟的原因是什么?
我还尝试过transferControlToOffscreen和一个工作线程,但由于某种原因,在callng commit()之前处理worker中的上下文绘制命令需要比主线程长3-4倍。