Slight delay in drawing large images in Chrome

I am working on a simple JavaScript based game using the canvas tag. As part of the game, I have several large sprite sheets (for example, 2816x768 and 4096x4864), each of which contains related animations for the character on the screen. When the game begins, the game simply has a game of idle animation. When the user presses the spacebar, I start playing another animation from a completely different page with the caption.

Here is the code that draws sprites:

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); }; 

And here is the code that loads the images:

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

The problem is that there is a delay of 1.5 seconds when the user presses the spacebar and when the frame from the new sprite sheet is really drawn. This is one time and does not affect the smoothness of the next animation. I already have sprite sheets preloaded with new Image and the game will not even start until all the relevant image.onload events have been fired, so I know that the browser does not wait for them to load. I went through JavaScript using the debugger in Chrome 17.0, and narrowed the delay for calling drawImage in context. The hardest part is that this delay is missing in Firefox 10.0.2, so this is a Chrome issue. It really breaks the game.

What am I doing wrong here? Is there a way to reduce this delay in Chrome?

Change I tried to draw the whole next frame as soon as it loaded, as suggested by Peter Wishart, but this had minimal effect. I also tried changing loadImage as follows:

 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; }; 

This also showed no effect.

In fact, I found a solution, but it is terribly inefficient. It occurred to me that Chrome might try to do smart things with image memory after decoding it. If the image remains unused for a long time, and again this is just an assumption, Chrome will delete the decoded data from the memory and return it if necessary again. Typically, the decoding process takes an inconspicuous amount of time, but the large images that I use cause a very sharp drop in performance. Using this, I changed the drawing cycle to:

 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); } } 

This solution really works, but, as I said, it seems like a terrible waste. I need to draw the entire frame of the next set of animations, just to save the decoded data in Chrome.

Are there any less wasteful and less hacker alternatives to this strategy?

+4
source share
3 answers

With the idea that Chrome just dropped the decoded image data after a while, I tried just copying the images to a screen canvas, on the assumption that Chrome would not bother to pull this canvas out of memory. Code that works well for the loadImage function.

 Stage.prototype.loadImage = function(src) { var useCanvasCache = Prototype.Browser.WebKit; // If we are in a WebKit browser (eg 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; } }; 

He works too. There is a small initial penalty when the page loads, and most likely it uses more memory, but this is a workable solution, because speed is more important than memory in this application.

+2
source

Hmm .. although onLoad apparently released something there that Chrome needs to do in order to prepare images, such as uncompress, to load onto a graphics card.

Can you force the first frame to be displayed on the screen or hide in advance to move the delay before the user presses the spacebar?

0
source

It seems that chrome has been added to the decode function just for this purpose, preventing decoding delays when first added to the DOM.

Not implemented in firefox or 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.'); }); 

Additional information: https://medium.com/dailyjs/image-loading-with-image-decode-b03652e7d2d2

0
source

Source: https://habr.com/ru/post/1399470/


All Articles