【问题标题】:Small Delay on Drawing Large Images for the First Time in Chrome首次在 Chrome 中绘制大图像时出现小延迟
【发布时间】:2012-03-21 09:51:38
【问题描述】:

我正在使用 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 中的这种延迟吗?

编辑:我尝试按照 Peter Wishart 的建议在加载后立即绘制整个下一帧,但效果甚微。我也试过修改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 中变得陈旧。

有没有比这种策略更少浪费和更少hacky的替代方案?

【问题讨论】:

    标签: javascript google-chrome canvas


    【解决方案1】:

    考虑到 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;
        }
    };
    

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

    【讨论】:

      【解决方案2】:

      似乎 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

      【讨论】:

        【解决方案3】:

        嗯.. 虽然 onLoad 已触发,但 Chrome 可能需要做一些事情来准备图像,例如解压缩、加载到显卡。

        你可以强制第一帧画出屏幕或预先隐藏,以便在用户按下空格之前将延迟移动到?

        【讨论】:

          猜你喜欢
          • 2015-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-05-03
          • 1970-01-01
          • 2021-04-03
          • 1970-01-01
          • 2023-03-31
          相关资源
          最近更新 更多