在Chrome中首次绘制大图像的延迟很小

时间:2012-03-02 22:04:04

标签: javascript google-chrome canvas

我正在使用canvas标签开发一个简单的基于JavaScript的游戏。作为游戏的一部分,我有几个大的精灵表(例如2816x768和4096x4864),每个精灵表都包含屏幕角色的相关动画。当游戏开始时,游戏只会播放角色的空闲动画。当用户按下空格时,我开始用完全不同的恶搞表格播放另一个动画。

以下是绘制精灵的代码:

Sprite.prototype.drawFrame = function(x, y)
{
    ctx.drawImage(this.image,
        x*this.width, y*this.height,
        this.width, this.height,

        this.position[0], this.position[1],
        this.width, this.height);
};

以下是加载图片的代码:

Stage.prototype.loadImage = function(src)
{
    var image = new Image();
    this.incAssets();
    var stage = this;
    image.onload = function()
    {
        stage.decAssets();
    }
    image.src = src;
    return image;
}

问题在于,当用户按下空格时以及实际绘制新精灵表中的帧时,有1.5秒的延迟。这只是一次,不会影响以下动画的平滑度。我已经使用new Image预加载了精灵表,并且游戏甚至不会启动,直到所有相应的image.onload事件都被触发,所以我知道浏览器没有等待它们加载。我已经使用Chrome 17.0中的调试器逐步完成了JavaScript,并缩短了对上下文drawImage调用的延迟。最令人费解的是Firefox 10.0.2中没有这种延迟,因此这是Chrome特有的问题。这对游戏来说非常不利。

我在这里做错了什么?无论如何我可以减少Chrome中的延迟吗?

编辑:我按照彼得威斯哈特的建议尽快绘制整个下一帧,但影响很小。我还尝试修改loadImage如下:

Stage.prototype.loadImage = function(src)
{
    var image = new Image();
    this.incAssets();
    var stage = this;
    image.onload = function()
    {
        ctx.drawImage(image, 0, 0);
        stage.decAssets();
    }
    image.src = src;
    return image;
};

这也没有效果。

事实上,我确实找到了解决方案,但效率非常低。在我看来,Chrome可能会在解码后尝试使用图像内存进行智能操作。如果图像未使用足够长时间,并且这只是一个猜测,Chrome将从内存中删除已解码的数据,如果再次需要则将其拉回。通常情况下,解码过程需要花费不明显的时间,但是我使用的大图像会导致性能上非常大的昙花一现。使用这个我将绘制循环更改为:

function draw()
{
    var currentTime = new Date().getTime();
    var deltaTime = currentTime - lastTime;
    lastTime = currentTime;

    var dt = deltaTime / 1000.0;

    // The hack that keeps all decoded image data in memory is as following.
    if (this.stage.nextStage != undefined) 
        this.stage.nextStage.draw(0); // The 0 here means the animations advance by 0 milliseconds, thereby keeping the state static.

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
    if (stage != undefined && stage.loaded)
    {
        stage.draw(dt);
    }
}

这个解决方案确实有效,但就像我说的那样,这似乎是一种可怕的浪费。我必须绘制下一组动画的整个帧,只是为了防止解码后的数据在Chrome中变得陈旧。

这种策略是否有更少的浪费和更少的黑客替代方案?

3 个答案:

答案 0 :(得分:3)

由于认为Chrome只是在一段时间后丢弃了解码后的图像数据,我尝试将图像复制到屏幕外的画布,假设Chrome不会因将画布从内存中删除而烦恼。这样做的代码非常适合loadImage函数。

Stage.prototype.loadImage = function(src)
{
    var useCanvasCache = Prototype.Browser.WebKit; // If we are in a WebKit browser (e.g. Chrome)
    var decodeCanvas;
    var dectodeCtx;
    if (useCanvasCache)
    {
        // Creates a canvas to store the decoded image.
        decodeCanvas = document.createElement('canvas');
        dectodeCtx = decodeCanvas.getContext('2d');
    }
    var image = new Image();
    this.incAssets();
    var stage = this;
    image.onload = function()
    {
        stage.decAssets();
        // Simply transfer the image to the canvas to keep the data unencoded in memory.
        if (useCanvasCache)
        {
            decodeCanvas.width = image.width;
            decodeCanvas.height = image.height;
            dectodeCtx.drawImage(image, 0, 0);
        }

    }
    image.src = src;
    // Canvas works the same as an image in a drawImage call, so we can decide which to return.
    if (useCanvasCache)
    {
        return decodeCanvas;
    }
    else
    {
        return image;
    }
};

它也有效。当页面加载时会有一个小的初始惩罚,它可能会占用更多内存,但这是一个可行的解决方案,因为速度比此应用程序中的内存更重要。

答案 1 :(得分:1)

为了达到这个目的,似乎chrome已添加到decode函数中,从而防止首次添加到DOM时出现解码延迟。

似乎没有在Firefox或IE中实现。

const img = new Image();
img.src = "bigImage.jpg";
img.decode().then(() => {
     document.body.appendChild(img);
}).catch(() => {
    throw new Error('Could not load/decode big image.');
});

更多信息:https://medium.com/dailyjs/image-loading-with-image-decode-b03652e7d2d2

答案 2 :(得分:0)

嗯..虽然onLoad已经解雇了,但Chrome可能需要做的事情就是准备像解压缩这样的图像,加载到显卡。

您是否可以强制第一帧预先绘制屏幕或隐藏,以便在用户按空格之前将延迟移至?