Scrolling huge backdrop in canvas - javascript

My problem is the company I'm working for has started giving me art for the game I'm making for them and it has a huge world! It has given me a huge backdrop image like 12923x5356 huge!
I've now been able to get it to scroll and work like a backdrop but it's a bit slow on my old computer that's an old thinkcenter (note it can only run crunchbang and barely xp). See we want to be able to support tablets for this. It also further complicates things that they have an overlay as well just as big.
Right now I have it only drawing a canvas sized chunk of the backdrop so it only draws as much as can be seen by using
ctx.drawImage(img, x, y, width, height, dx, dy, dwidth, dheight);
with the dx/dy always drawing to the same spot and x/y scrolling with the main character.
Now realistically, is there any way to possibly do this faster? Another way I can think of is to maybe break the image down to small chunks with getImageData and drawing just so many that fills the screen with ctx.putImageData(img, x, y). I'm pretty sure from experience that the small form of drawImage is a bit faster than the long form but would this be much faster?
Anyone else have any ideas they can shoot at me?

You can make it into smaller tiles, just like how Google Maps does.
I'm not sure if you're handling some huge game map, but the technique should be the same. You can split the map into small tiles of, for example, 256px * 256px. Then you only need several 256px * 256px div to cover the viewport. If you use canvas instead, it's the same. Just render those tiles which are currently shown to the user.

Related

<canvas> element in Chrome randomly flipping vertical before toDataURL()

I present to you, dear reader, a vexing bug that I cannot reproduce locally. However, I have gotten many reports from end-users, which seem to be increasing in frequency.
If you are an advanced user of <canvas> elements in Chrome, please continue reading, as I am completely baffled.
I use <canvas> elements to render full-size iPhone and iPad screenshots. I have a method that renders the canvas, which looks something like:
set the size of the canvas to iPhone resolution (640x1136) — possibly important: this canvas might have been rendered at a smaller resolution before
fill with background color (0, 0, width, height)
draw text at the top
draw an image of a phone in the middle of the bottom
draw a screenshot image on top of that phone image
call toDataURL() on the canvas to extract the image as a JPEG
Recently, after months of nobody reporting any problems, we've had several reports from different people reporting exported images being "messed up".
The images they send us are the correct size, BUT rather than the images containing the canvas area at (0, 0, width, height), the resulting images seem to contain the image data from (0, height, width, height) — offset by the height of the canvas and then flipped vertically for some reason.
Here is what this looks like:
As shown in this image, the content of the <canvas> element itself is seemingly not in the end-result image generated by toDataURL(); rather, the end-result image seems to contain the flipped image data from below the actual area of the <canvas>.
Important debugging information:
There are no calls to setTransform(), scale() or translate() anywhere in my code
These canvas elements are not visible to the end-user
This does not seem to affect Safari
I have users reporting the same output in Chrome incognito windows, so it is probably not the result of an extension
The code seems to be rendering the images in the correct locations on the canvas — the screenshot and phone appear in the correct place, and the background color is not present (since it is only drawn to 0, 0, width, height and not beyond)
Reports seem to be increasing in frequency, but still probably only 5-10% of Chrome end-users
Any ideas as to a root cause?
UPDATE: I have fixed the issue through trial and error. In the original implementation, I borrowed a "preview" low-resolution canvas to render the full-resolution image before calling toDataURL — my fix created a new canvas at the new size for the final rendering, rather than reusing the lower-resolution original canvas.
I'd still love to know the original cause. I think it has something to do with resizing <canvas> elements in code before calling drawImage() or something, but I can't reproduce it in order to investigate further.

Optimizing HTML5 Animation

I have a HTML5 animation that uses a canvas of size 900x520. However ~60% of the canvas is not utilized in the most general case. If some of the parameters of the animation are varied, then the whole canvas is utilized which is the reason why I pegged the maximum canvas size to W:900px and H:520px.
When a user opens the page with this HTML5 canvas, a general case is considered and only 280x280 at the center of the HTML5 canvas is used. As per my knowledge though I'm not using the other 60% of the canvas is not used but is rendered every time which is causing the animation to slow down, especially on tablets and mobile phones (Most of my intended users are tablets / mobile users). So, I would like to know if there are any techniques to render only a set of pixels everytime (say 300x300 at the center of the canvas) ? Are there any workaround solutions ?
I'm already using RequestAnimationFrame (However, I want some more optimization)
First thing i would try is to use the clip() function. It slows down the drawing, but in your case it might be faster to check first against a clip since 60% of the time the clipping will quit the drawing.
Second thing to try is to handle clipping by yourself : handle a viewport object ( center x, center y, width, height ) and exclude unseen object with simple bounding box testing. Rq : Maybe the way you draw your scene allows for easy optimisation.
3) do not define a canvas bigger than the screen : rather define it at max size, and keep track of an offset, like with a viewPort object. And to avoid re-writing all your draw code, just use save/translate/restore before/after your old methods. For the clipping on this canvas, refer to 1) :-)
4) you can reduce graphic strain on mobile by using css scaling. Even if scaling reduces performances, on mobiles it is faster to draw on smaller canvas with a css scale than to draw full resolution with no scale.
So you have to set canvas.style.width and canvas.style.height to some wise values, and you can set canvas.width and canvas.height to the setting that allows for the right performance.

Rendering html canvas images at high resolution

I'm looking to create a system where users create an image in canvas on their browser (with the help of paper.js or processing.js etc) and be able to print a large (up to A3, 300dpi ideally) size representation of what they have created in canvas.
Obviously exporting images straight from the canvas element in the users browser I'm limited to screen size and resolution etc.
So I've looked into the solution of momentarily scaling up the canvas element when the users saves and capturing the image data at the larger size. This could be a solution but I'm pretty sure file size would get out of hand pretty quickly at larger sizes.
I haven't used Node.js very much but was wondering if anyone with experience would know if Node could achieve this and if it would be a better solution and briefly how I'd go about it?
I see two ways to achieve what you want :
use an oversized canvas, that you scale with css.
For instance, you can have a 1000X1000 canvas, that you show within a 200pxX200px smaller view.
<canvas width=1000 height=1000
style = 'width:200px; height:200px;' id='cv'>
</canvas>
use a small canvas for display on screen, and really draw on a backing canvas, that you reproduce on the view canvas at each change.
Both solution cannot solve the issue that mouse coordinates are integer, so to implement a 'pixel perfect' location of object you'll have to implement some kind of zooming. Second solution might be simpler for this.
To retrieve the mouse coordinates, with css scaling do not forget to multiply them by the scale, and in case 2, by the scale you decided.
// formula to get the css scale :
var cssScaleX = canvas.width / canvas.offsetWidth;
var cssScaleY = canvas.height / canvas.offsetHeight;
// then mouse coords are to be multiplied by the scale :
// mouse.x *= cssScaleX;
I tried quickly both solutions, and i was quite surprised to see that css solution is very slow (in both Ch and FF), and it seems faster to copy a back canvas than to have css doing it.
Maybe it depends on some quality settings, but it seems solution 2 is both more flexible and faster.
first css version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/kegohufu/1/
second back canvas + copy version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/qomiqoqi/1/

Google Chrome - context.canvas.width/context.canvas.width causes "Blue layer"

I upgraded from Chrome version 17 to Chrome: 18.0.1025.142 today.
I've noticed one of the web apps I am working on has a strange blue layer that appears on load and then vanishes when you start scrolling.
I've tracked this down to the following lines in my JS:
context.canvas.width = canWidth;
context.canvas.height = canHeight;
canWidth and canHeight are generated dynamically.
Commenting these lines out stops the blue layer from rendering, however this isn't a fix as I need to use the dynamically generated values to control the canvas width/height.
I also attempted hard coding the context.canvas.width and context.canvas.height to 600 and this also generated the blue layer issue.
This wasn't a problem on previous versions of Chrome, and I don't have any problems in FireFox.
Any suggestions on what the issue might be?
Edit:
Here is the block of code where the above resides (nodeLeft and nodeTop are generated else where):
context.clearRect ( 0 , 0 ,canWidth, canHeight );
context.canvas.width = canWidth;
context.canvas.height = canHeight;
context.beginPath();
context.moveTo(x, y);
context.lineTo(nodeLeft, nodeTop);
context.strokeStyle = "#000000";
context.lineCap = "round";
context.stroke();
Can you post more code than the two lines you've provided? I believe it's something more than where you've set the width/height.
I have a drawing application that I've been tinkering with on and off for a while in which I do very much the same thing. The only difference is that I call context.scale(0,0); before setting the width and height. I have no issues in any version of Google Chrome with a blue overlay. Are you possibly modifying the context's scale value before setting the width and height?
e.g. here's what I have, setting the canvas to document width/height for a full-document drawing area:
context.scale(0,0);
context.canvas.width = $(document).width();
context.canvas.height = $(document).height();
edit: my tinkering site mentioned is at http://drawcomix.com if you want to test the blue overlay to see if it's your browser or your code.
edit: based on your additional comment...
I've seen tutorials on the web that suggest the best way to clear the canvas is to set the height/width. That's not entirely true. On document load, it may be the quickest/easiest way to do so, but on mouse movements, you're probably better off doing something like this:
context.save();
context.setTransform(1,0,0,1,0,0);
context.clearRect(0,0,width,height); // width/height of canvas
context.restore();
You may not need setTransform if you're not performing any transformations on the canvas.
I believe I found this solution in Pro HTML5 Programming (Apress), but I'm not sure.
EDIT:
Ok, I got it for me. It seems to be a problem with some AMD graphic cards. I could turn off the high performance mode for chrome in the catalyst control center. That fixed most of the annoying things for me...hope that it'll be fixed in the next versions.
For mor information see: http://code.google.com/p/chromium/issues/detail?id=121658
This is not a solution, but I'm facing the same problem and it's driving me nuts. I'm trying to nail it down. I've tested differnt browser on different machines:
Problems:
Blue layers (I only see them if Chrome is NOT maximized)
Misplaced y scrollbar (sometimes only shows up if I switch through the tabs)
Distorted shapes. The size of the canvas and the drawed images / shapes vary from chrome to other browser. Means, the canvas / shapes is smaller on Chrome, it mostly shrinks the width.
Conclusion 1:
It seems that it does not happen on every machine with Chrome 18.x.x. Seems to be a hardware issue, but couldn't constrain it futher actually.
Conclusion 2:
If i set the size of the canvas to 200 height/width, the problems don't appear. If I set it higher than 200, it will deform the canvas / rectangle and other problems appear.
Conclusion 3:
The problems show only up if something is drawed to the canvas or something is set, i.e. fillStyle.
Here the test snippets I used:
<canvas id="canvas" width="300" height="300" style="background-color:orange"></canvas>
...
ctx.fillRect (10, 10, 55, 50);

iphone/ipad large canvas vs small canvas + div animation

Since I dont have either of these devices handy to test, which method would be faster on an iphone/ipad?
One large canvas (overlayed over a bg of the same size), that is cleared, and has new pixel data drawn to it each frame, at a certain position
or
A small canvas that updates each frame, contained in a div that is animated across a background
As it turns out iphone/ipad aren't even capable of running this small animation either way. I will have to look at alternative solutions for mobile/tablet devices.
Yes, I've found the iPhone too slow in all my canvas experiment.
Even moving a simple div around (CSS3 styled, no canvas) is barely accettable ( my test with the new motion API: http://www.omiod.com/i.htm )

Categories

Resources