HTML5 画布撕裂与请求动画帧



UPDATE:整个问题最终是系统图形驱动程序的问题,而不是(似乎)浏览器/API问题。 撕裂的帧归结为实际的显示器更新。 再次感谢那些参与讨论并试图提供帮助的人。


我的页面使用画布和 2d 上下文以 720p 显示预渲染的帧。 我正在单独渲染帧并使用新的图像数据更新变量。 然后,在请求动画帧中,我只需执行context.putImageData(cached_image_data);。 尽管提前完全渲染了帧并有效地进行了双重缓冲,但我仍然经常流泪。 我在SO上发现了一些其他问题,但它们都以"使用RAF"结尾。 代码归结为:

var canvas = document.getElementById("canvas");
var cached_frame = new ImageData(new Uint8ClampedArray(canvas.width * canvas.height * 4), canvas.width, canvas.height);
var context = canvas.getContext("2d");
var framerate = 30;
function draw() {
    if (cached_frame)
        context.putImageData(cached_frame, 0, 0);
    requestAnimationFrame(draw);
}
setInterval(function() {
    var frame = context.getImageData(0, 0, canvas.width, canvas.height);
    // Do things to manipulate frame.data.
    // Save the resultant pixel data for the cached_frame.
    cached_frame.data = frame.data;
}, 1000 / framerate);
draw();

在不求助于webgl的情况下,我还能做些什么吗?

任何建议表示赞赏。 TY 所有:D

我不认为代码正在做你认为它正在做的事情

首先,据

我所知,您无法将新数据分配给ImageData,因此此行

cached_frame.data = frame.data;

什么都不做。我们可以测试显示它不起作用的内容

var ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
var imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
var data = new Uint8ClampedArray(imageData.length);
// fill imageData.data with red
fillWithColor(255, 0, 0, 255, imageData.data);
// fill data with green
fillWithColor(0, 255, 0, 255, data);
// assign imageData.data to data
imageData.data = data;
// Draw. If assigning imageData.data works result will
// be green, if not result will be red
ctx.putImageData(imageData, 0, 0);
function fillWithColor(r, g, b, a, dst) {
  for (ii = 0; ii < dst.length; ii += 4) {
    dst[ii + 0] = r;
    dst[ii + 1] = g;
    dst[ii + 2] = b;
    dst[ii + 3] = a;
  }
}
  

其次,你的draw函数是连续绘制的,至少从你发布的代码中cached_frame在第 2 行设置的代码中,所以它总是正确的,并且总是在绘制。如果您以某种方式部分更新了cached_frame中的实际数据,那么当只有部分结果时,它将绘制。

我想你想要这样的东西

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var frame;
var framerate = 30;
function draw() {
   context.putImageData(frame, 0, 0);
}
setInterval(function() {
    frame = context.getImageData(0, 0, canvas.width, canvas.height);
    // Do things to manipulate frame.data
    // frame is ready, draw it at next rAF
    requestAnimationFrame(draw);
}, 1000 / framerate);

如果您认为解码比 raf 发生得更快,您可能需要检查抽奖是否已经排队。不过,我认为在这种情况下您实际上不需要 rAF。我很确定你可以在 setInterval 结束时绘制,它会显示下一帧,没有撕裂。

这是一个测试,它不是为我流泪。

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var frame;
var framerate = 30;
var frameCount = 0;
setInterval(function() {
  ++frameCount;
  frame = context.getImageData(0, 0, canvas.width, canvas.height);
  var data = frame.data;
  var width = frame.width;
  var height = frame.height;
  // Do things to manipulate frame.data
  for (var yy = 0; yy < height; ++yy) {
    for (var xx = 0; xx < width; ++xx) {
      var offset = (yy * width + xx) * 4;
      data[offset + 0] = ((xx >> 2 & 0x1) ^ frameCount & 0x1) ? 255 : 0;
      data[offset + 3] = 255;
    }
  }
  // frame is ready, draw it at next rAF
  context.putImageData(frame, 0, 0);
}, 1000 / framerate);
<canvas id="canvas" width="1280" height="720"></canvas>

最新更新