const ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 380;
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
var lastTime;
var lastPTime;
var lastDTime;
var lastFrameRenderTime = 0;
var renderLoadMs = 0;
var pTimeErrorTotal = 0;
var totalFrameTime = 0;
var totalFrameCount = 0;
var startTime;
var clearToY = 0;
const frameRate = 1000/60;
ctx.font = "14px arial";
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
var globalTime;
ctx.clearRect(0,0,w,h);
const graph = (()=>{
var posx = 0;
const legendW = 30;
const posy = canvas.height - 266;
const w = canvas.width - legendW;
const range = 6;
const gridAt = 1;
const subGridAt = 0.2;
const graph = ctx.getImageData(0,0,1,256);
const graph32 = new Uint32Array(graph.data.buffer);
const graphClearA = new Uint32Array(ctx.getImageData(0,0,1,256).data.buffer);
const graphClearB = new Uint32Array(ctx.getImageData(0,0,1,256).data.buffer);
const graphClearGrid = new Uint32Array(ctx.getImageData(0,0,1,256).data.buffer);
const graphFrameDropped = ctx.getImageData(0,0,1,256);
const graphFrameDropped32 = new Uint32Array(graphFrameDropped.data.buffer);
graphClearA.fill(0xFF000000);
graphClearB.fill(0xFF440000);
graphClearGrid.fill(0xFF888888);
graphFrameDropped32.fill(0xFF008800);
const gridYCol = 0xFF444444;
const gridYColMaj = 0xFF888888;
const centerCol = 0xFF00AAAA;
ctx.save();
ctx.fillStyle = "black";
ctx.textAlign = "right";
ctx.textBaseline = "middle";
ctx.font = "10px arial";
for(var i = -range; i < range; i += subGridAt){
var p = (i / range) * 128 + 128 | 0;
i = Number(i.toFixed(1));
graphFrameDropped32[p] = graphClearB[p] = graphClearA[p] = graphClearGrid[p] = i === 0 ? centerCol : (i % gridAt === 0) ? gridYColMaj : gridYCol;
if(i % gridAt === 0){
ctx.fillText(i + "ms",legendW - 2, p + posy);
ctx.fillText(i + "ms",legendW - 2, p + posy);
}
}
ctx.restore();
var lastFrame;
return {
step(frame){
if(lastFrame === undefined){
lastFrame = frame;
}else{
while(frame - lastFrame > 1){
if(frame - lastFrame > w){ lastFrame = frame - w - 1 }
lastFrame ++;
ctx.putImageData(graphFrameDropped,legendW + (posx++) % w, posy);
}
lastFrame = frame;
ctx.putImageData(graph,legendW + (posx++) % w, posy);
ctx.fillStyle = "red";
ctx.fillRect(legendW + posx % w,posy,1,256);
if((frame / 60 | 0) % 2){
graph32.set(graphClearA)
}else{
graph32.set(graphClearB)
}
}
},
mark(ms,col){
const p = (ms / range) * 128 + 128 | 0;
graph32[p] = col;
graph32[p+1] = col;
graph32[p-1] = col;
}
}
})();
function loop(time){
var pTime = performance.now();
var dTime = Date.now();
var frameTime = 0;
var framePTime = 0;
var frameDTime = 0;
if(lastTime !== undefined){
frameTime = time - lastTime;
framePTime = pTime - lastPTime;
frameDTime = dTime - lastDTime;
graph.mark(frameRate - framePTime,0xFF00FFFF);
graph.mark(frameRate - frameDTime,0xFFFFFF00);
graph.mark(frameRate - frameTime,0xFF0000FF);
graph.mark(time-pTime,0xFF00FF00);
graph.mark(lastFrameRenderTime,0xFFFF00FF);
pTimeErrorTotal += Math.abs(frameTime - framePTime);
totalFrameTime += frameTime;
totalFrameCount ++;
}else{
startTime = time;
}
lastPTime = pTime;
lastDTime = dTime;
lastTime = globalTime = time;
var atFrame = Math.round((time -startTime) / frameRate);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,w,clearToY);
ctx.fillStyle = "black";
var y = 0;
var step = 16;
ctx.fillText("Frame time : " + frameTime.toFixed(3)+"ms",10,y += step);
ctx.fillText("Rendered frames : " + totalFrameCount,10,y += step);
ctx.fillText("Mean frame time : " + (totalFrameTime / totalFrameCount).toFixed(3)+"ms",10,y += step);
ctx.fillText("Frames dropped : " + Math.round(((time -startTime)- (totalFrameCount * frameRate)) / frameRate),10,y += step);
ctx.fillText("RenderLoad : " + lastFrameRenderTime.toFixed(3)+"ms Hold mouse into increase",10,y += step);
clearToY = y;
graph.step(atFrame);
requestAnimationFrame(loop);
if(mouse.button ){
renderLoadMs += 0.1;
var pt = performance.now();
while(performance.now() - pt < renderLoadMs);
}else{
renderLoadMs = 0;
}
lastFrameRenderTime = performance.now() - pTime;
}
requestAnimationFrame(loop);
canvas { border : 2px solid black; }
body { font-family : arial; font-size : 12px;}
<canvas id="canvas"></canvas>
<ul>
<li><span style="color:red">Red</span> is frame time error from the callback argument.</li>
<li><span style="color:yellow">Yellow</span> is the frame time error calculated using performance.now().</li>
<li><span style="color:cyan">Cyan</span> is the frame time error calculated using Date.now().</li>
<li><span style="color:#0F0">Green</span> dots are the difference in time between the callback time argument and the time reported by performance.now()</li>
<li><span style="color:magenta">Magenta</span> is the last frame's render time calculated using performance.now().</li>
<li><span style="color:green">Green</span> vertical lines indicate that a frame has been dropped / skipped</li>
<li>The dark blue and black background marks seconds.</li>
</ul>
frameRate = 1000/60
,因此我认为它只能在60 Hz下平稳运行。 - HankMoody1000/60
只是一个参考值,浏览器上只有一个速度60Hz..根据机器的不同,浏览器将轻松巡航或努力跟上。如果您想要超级流畅的uHD HDI 240Hz,则最好不要使用浏览器和JS。 - Blindman67