将图像循环绘制到画布上 - 如何优化此代码?

时间:2014-03-02 13:52:36

标签: javascript optimization canvas drawing

我在画布元素上绘制背景图像。我用requestAnimationFrame创建一个循环。在这个循环中,我用适当的坐标在画布上绘制一个图像。

动画似乎很平滑,在Chrome 60 fps中,但我偶尔会有一些小问题。在Firefox中比在Chrome中更糟糕。当我用干净的轮廓查看它时没有打开标签会更好 - 但它仍然不完美。

以下是完整来源:http://jsbin.com/vopiw/1/edit?html,output

每个帧都会调用此函数:

function draw(delta) {
    totalSeconds += delta;

    var vx = 100; // the background scrolls with a speed of 100 pixels/sec
    var numImages = Math.ceil(canvas.width / img.width) + 1;
    var xpos = totalSeconds * vx % img.width;

    context.save();
    context.translate(-xpos, 0);
    for (var i = 0; i < numImages; i++) {
        context.drawImage(img, i * img.width, 0);
    }
    context.restore();
}

你能发现什么,这可能是一个真正的性能缺点吗?

到目前为止我发现了什么:

  • 内存消耗略有增长但不断增长
  • 但是没有垃圾收集发生,这可能归咎于毛刺

你有没有任何线索?

1 个答案:

答案 0 :(得分:1)

将图像用作元素本身的背景图像,并使用背景位置滚动它。

而不是img onload只需直接进入代码:

(function imageLoaded() {

  canvas.style.backgroundImage = 'url(...)';
  canvas.style.backgroundRepeat = 'repeat-x';
  draw(0);
  ...

然后只需使用以下内容更新draw()方法:

// cache these
var iw = 400,
    cw = canvas.width;

function draw(delta) {

    totalSeconds += delta;

    var vx = 100;  // if always 100 just insert the value directly below
    var numImages = ((cw / iw)|0) + 1;  // use logic OR to remove fractions
    var xpos = totalSeconds * vx % iw;

    // update background position
    canvas.style.backgroundPosition = (-xpos + iw) + 'px 0';
}

第二个问题是计算时间增量的方式。使用低分辨率计时器可以增加生涩感。

尝试使用内置的高分辨率计时器。幸运的是,rAF提供了一个高分辨率的时间戳,你可以使用它:

function loop(now) {  // use argument from rAF (hi-res timestamp)
    if (!looping) {
        return;
    }

    requestAnimationFrame(loop);

    var deltaSeconds = (now - lastFrameTime) * 0.001; //mul is faster than div
    lastFrameTime = now;
    draw(deltaSeconds);
}

<强> Modified jsbin

这会将绘图操作交给浏览器,但请记住,增益不是全部。原因是drawImage()方法本身相当快,但你在JavaScript中保存了一些步骤,这是真正的瓶颈(尽管有神话,画布本身非常快)并且重复这些绘制操作留给浏览器中的内部编译代码。

影响平滑度的其他因素通常是硬件时钟和硬件功能以及浏览器中正在发生的其他事情。

我还会将canvas元素置于绝对或固定位置,因为浏览器会为元素提供一个单独的位图(与画布位图无关),这可以提高CSS背景性能(未在修改后的jsbin中显示。)