A while back webkit (and thus Safari) began to support CSS canvas-backgrounds for elements (Source: http://www.webkit.org/blog/176/css-canvas-drawing/).
This could greatly simplify the creation of games and multimedia, in that you dont need to inject a canvas-tag into a DIV (for instance), but simply hook into the background of the DIV directly. Something like this perhaps:
<div id="gameview"
style="background: -webkit-canvas(myscreen); width: 320px; height: 480px;">
</div>
<script>
var target = document.getElementById("gameview");
var wd = target.clientWidth;
var hd = target.clientHeight;
var context = document.getCSSCanvasContext("2d", "myscreen", wd, hd);
/* draw stuff here */
</script>
I was wondering, are there any speed penalties involved in this? In theory i think drawing to a background canvas should be faster than drawing to a canvas tag, especially if the target element is empty.
Have anyone tested this for high-speed demos or games?
According to my tests (also run in reversed order), original canvas element is slightly but consistently slower than the background canvas.
Chromium 17 draws a chess-board 10000 times in:
~470 ms on the background canvas
~520 ms on a canvas element
Safari 5 shows similar dynamics.
Try setting the number of iterations to 100000, results should be consistent with the above.
Update half a year later
We tried the background canvas approach in one project (as an attempt for a minor optimization), and the results were dramatically opposite to our expectations. The whole thing (two layers: one – a div with background canvas, the other – a regular canvas) became marginally slower. In fact, when we introduced the background canvas, the app became slow as hell. Tested in Chrome 21.
I definitely would not vouch for the background canvas to be faster in all situations.
test.php:11Regular Canvas 606
test.php:20Background Canvas 449
test.php:11Regular Canvas 516
test.php:20Background Canvas 483
Regular seems to underperform compared to background canvas in my tests on chrome in linux debian, heres the code used ( also added to http://jsfiddle.net/hDPVr/ )
<div style="width:300; height:200; background: -webkit-canvas(test_canvas); "></div>
<canvas id="canvas" style="width:300; height:200;"></div>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var regular_timer = new Date().getTime() ;
for( var i = 0; i<100000; i++ ){
context.fillRect( 0,0,10,10);
}
console.log( 'Regular Canvas', regular_timer - (new Date().getTime()) )
var context = document.getCSSCanvasContext('2d', 'test_canvas', 300, 200);
var background_timer = new Date().getTime() ;
for( var i = 0; i<100000; i++ ){
context.fillRect( 0,0,10,10);
}
console.log( 'Background Canvas', background_timer - (new Date().getTime()) )
</script>
So the only thing that I did for testing is fillRect, but it's still at least 10% better in some cases
Related
I need to build a kind of map in canvas, which must be able to hold more than 10.000 elements and thus has quiet big dimensions in some cases (> 8000px width, >4000 px height). Also I need to pan and zoom the map.
After some fiddeling around with existing libraries (Paper.js) and possible other solutions (Leaflet Map) I eventually wrote an own library from scratch, because the main requirement is, that is should be really really fast (loading, mouseovers, ...) and none of the libraries I tried could offer all of the aspects.
The structure is as follows:
I have one map object with an associated Control object, which registers events and has resize methods etc.
A map is divided in mutliple even sized tiles (1024px x 1024px - customizable) because using the map with only one canvas at a size over 8000px width made it incredibly slow
Each tile is associated with a canvas
The elements (just circles) are added to one or multiple tiles (If it's on the edge) - more specifically to the tiles' canvas.
The tiles are placed within an container div which has the dimensions of the map area (when not zoomed out)
The container div is placed within a viewport div to enable the map being displayed as a "widget"
Zooming scales every tile/canvas and the container. For sake of performance I sacrificed smooth zoom and implemented a customizable amount of zoom steps, which still feels okay.
Panning set's the topand left style of the container.
Events used are window.resize, mousewheel, DOMMouseScrol, mousedown, mouseup, mousemove, touchstart,touchend,touchmove and Hammertime pinch
This alltogether runs satisfying on Desktop Browsers, and iPhones (tested with SE, 6S) but on every Android device I tested it (Samsung S4, One Plus One and another 1 year old device, and android studio emulator) it runs extremly slow. Drawing of the Map is fine in speed, but zooming and panning is near to impossible.
The code is too comprehensive to post it here, so I'm asking you if there are any known problems with canvas on android, that could explain this problem, or maybe some issues with the way I built the structure that could produce issues with android. I'm really clueless here, since it works on desktop and iPhone.
The real problem you're hitting is you're overloading the GPU. Loading that much data all and once then moving it around is going to put a toll on the GPU and likely force the browser into software rendering mode, which is a big performance hit.
Instead, I'd suggest changing your approach. Rather than having various large canvases, you should have one canvas that is, at most, the size of the users screen. Then, utilize methods of the canvas API such as scale and translate to render what you need. For an added bonus, avoid trying to render things which are off screen.
It may seem like having to redraw the scene every time you move around would be slow but it's not. The reality is that either you specify exactly what needs to be drawn or the browser has to attempt to draw all of it again when you shift it around. Here's a brief example of how you can render and move large images.
var ctx = document.querySelector('canvas').getContext('2d');
var img = new Image();
img.src = 'https://placeimg.com/1000/1000/nature';
img.onload = start;
function start() {
var xDirection = -1;
var yDirection = -1;
var xPosition = 0;
var yPosition = 0;
var prev = Date.now();
(function render() {
var now = Date.now();
var delta = (now - prev) / 1000;
xPosition += xDirection * delta * 20;
yPosition += yDirection * delta * 40;
if (xPosition > 0) {
xPosition = 0;
xDirection *= -1;
} else if (xPosition < -320) {
xPosition = -320;
xDirection *= -1;
}
if (yPosition > 0) {
yPosition = 0;
yDirection *= -1;
} else if (yPosition < -240) {
yPosition = -240;
yDirection *= -1;
}
prev = now;
ctx.save();
ctx.translate(xPosition, yPosition);
ctx.drawImage(img, 0, 0);
ctx.restore();
requestAnimationFrame(render);
})();
}
body {
background: #111;
}
canvas {
background: #FFF;
}
<canvas width="320" height="240"></canvas>
I created a basic 2D game using pure JavaScript. My problem is when the sprite stops moving it is sometimes blurred sometimes not. I use requestAnimationFrame to move the sprite and keydown/keyup events trigger moving. When keyup is triggered the sprite stops moving and the default tile is set however sometimes it is showing blurred.
https://arpadvas.github.io/untitled_game_project/
Since you didn't posted an minimal code example, and that I don't want to go through the raw code you linked to, this will stay as a guess (an educated one).
Generally, this happens when you draw your sprites on floating coordinates.
Since there is no way to draw on half a pixel, the pixel being the smallest unit in canvas, to smoothen drawings, the browser will by default create antialias artifacts, turning some pixels less opaque so that your eyes think it is on half a pixel.
While this usually works well with realistic photographs, it doesn't at all with pixel-art.
The solution then is either to round all your coordinates, or if you are lazy, to set the imageSmoothingEnabled property of your context.
var img = new Image();
img.onload = draw;
function draw(){
i = .316252;
blurred.width = round.width = noAntiAlias.width = img.width +20;
blurred.height = round.height = noAntiAlias.height = img.height +20;
blurred.getContext('2d').drawImage(img, 10+i, 20+i);
round.getContext('2d').drawImage(img, 10, 20);
var nA = noAntiAlias.getContext('2d');
nA.mozImageSmoothingEnabled = nA.msImageSmoothingEnabled = nA.webkitImageSmoothingEnabled = nA.imageSmoothingEnabled = false;
noAntiAlias.getContext('2d').drawImage(img, 10+i, 20+i);
};
img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
<canvas id="blurred"></canvas>
<canvas id="round"></canvas>
<canvas id="noAntiAlias"></canvas>
Ps : this made me realize that somehow my FF doesn't smooth this particular image I used... If someone can confirm in comments, I'd be glad to dig into this further if needed.
Using a sprite sheet for at least each individual animation or even a single big one for all your sprites is generally a good idea because there will be less overhead, compression will be better, and the server won't be bombarded with as many requests. However these benefits all only affect the downloading of ones sprites from the webserver, what about rendering efficiency?
In short: Is there any significant benefit in rendering individual frames directly off of the sprite sheet, rather than putting each frame on its individual canvas beforehand and rendering from there?
What I'm planning to do is create an array of frames for each animation, so frame[0] would be an off-screen canvas element representing the first frame. I'm not super concerned about memory overhead (though it'd also be interesting to know), I'm mostly concerned about the efficiency of the rendering. If it's a significant performance hit I could consider instead making each "frame" something like an object with a reference to the respective sprite sheet, and x/y/w/h values for where to find the frame on the sheet.
PS: I'm asking this with canvas in mind, would it be different for webGL?
I suspect #Bergi has it just right.
I did the following quick perf test drawing a sprite 10000 times using canvas-frames and then using clips from a spritesheet.
On my machine (win8/2GHz/4Core), clipping from a spritesheet was always faster, but both canvas-frames and spritesheet-clipping occurred so quickly that there was no significant rendering speed benefit using one method or the other.
Given no speed improvement between the 2 methods, we're left with Bergi's good conclusion that the extra setup plus memory overhead with canvas would tilt a decision towards clipping a spritesheet. Also, the canvas-frame solution would certainly lag on mobile--mobile doesn't do canvas well.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var cframes=[];
var iw,ih,iw10;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/avatars.png";
function start(){
var iw=img.width;
var ih=img.height;
var iw10=parseInt(iw/10);
var iterations=10000;
for(var i=0;i<10;i++){
var c=document.createElement('canvas');
var cx=c.getContext('2d');
c.width=iw10;
c.height=ih;
cx.drawImage(img, iw10*i,0,iw10,ih, 0,0,iw10,ih);
cframes.push(c);
}
var t1=performance.now();
for(var i=0;i<iterations;i++){
ctx.drawImage(cframes[i%10],10,0);
}
var t2=performance.now();
var t3=performance.now();
for(var i=0;i<iterations;i++){
ctx.drawImage(img, (i%10)*iw10,0,iw10,ih, 10,ih,iw10,ih);
}
var t4=performance.now();
alert(iterations+' draws. Canvas-frames:'+parseInt(t2-t1)+'ms, spritesheet-clips:'+parseInt(t4-t3)+'ms');
}
body{ background-color: black; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
I have got a canvas and I display image using it via the putImageData() method after updating bytes in clamped array got by getImageData.data.
That works nicely, but I need to scale *2 in Y-direction.
Of course I can do it without a question here too by repeating every lines twice, but it requires too much time to render a frame then (I display 25 frames per second). I've read about the ability to scale image with drawImage() method. The only problem that as far as I know, using drawImage() is slower than using putImageData(), and it was required in old browser versions like Firefox/2 (or such).
How can I upscale the image in Y direction twice as fast as possible?
By the way, is it possible to get similar solution to gain full screen resolution somehow (a flash - not JS - example: like what youtube does when you go to fullscreen)?
If you want the whole canvas to scale, you can do this without any extra javascript. Just set a size in CSS that has twice the width and height specified in the canvas.
<canvas width="200" height="100" style="width:200; height: 200">
See the W3 spec.
Edit:
Looking at the question linked by Alnitak below: If you want nearest-neighbor scaling rather than antialiasing you can do this:
canvas {
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
You can achieve vertical scaling (without anti-aliasing) by just grabbing one image row at a time with getImageData and then replicating that row multiple times with putImageData, e.g.:
var oy = 0;
for (var y = 0; y < sh; ++y) {
var data = src_ctx.getImageData(0, y, sw, 1);
for (var n = 0; n < scale; ++n) {
dst_ctx.putImageData(data, 0, oy++);
}
}
See http://jsfiddle.net/alnitak/hYZ3U/ for a worked example that completes in 3ms on my machine.
I am faced with a problem that slows down animation on a canvas, as various pictures move left, right, up and down. I need advice on how to optimize the animation.
It is important that the animation works on all main browsers, in particular: Chrome, Firefox and Internet Explorer.
Is it possible to optimize the animation? Maybe put a delay on the drawing? Thank you in advance.
In javascript you can use the setInterval and setTimeout functions to create delays and throttle the frame rate.
for instance if you wanted to make your drawing loop approximately 30 FPS you could have some code that looks like this:
function draw(){
var canvas = document.getElementById('myCanvas');
//create the image object
var img = new Image();
//set the image object's image file path
var img.src = "images/myImage.png"
//check to see that our canvas exists
if( canvas.getContext )
{
//grab the context to draw to.
var ctx = cvs.getContext('2d');
//clear the screen to a white background first
//NOTE to make this faster you can clear just the parts
//of the canvas that are moving instead of the whole thing
//every time. Check out 'Improving HTML5 Canvas Performance'
//link mention in other post
ctx.fillStyle="rgb(255,255,255)";
ctx.fillRect (0, 0,512, 512);
//DO ALL YOUR DRAWING HERE....
//draw animation
ctx.drawImage(img, 0, 0);
}
//Recalls this draw update 30 times per second
setTimeout(draw,1000/30);
}
This will help you control how much processing time the animation is taking. Thus if you find that your animation is going too slow you can increase the frame rate by changing the denominator '30' to something like '60' fps or anything that really works well for your program.
As far as optimizing goes in addition to setTimeout() the way you draw your animations is critical too. Try to load all your images before you render them this is called "Preloading". That is if you have a bunch of different images for each animated cell then before you call your draw function load all the images into an array of images like so:
var loadedImages = new Array();
loadedImages[0] = new Image();
loadedImages[0].src = "images/animationFrame1.png";
loadedImages[1] = new Image();
loadedImages[1].src = "images/animationFrame2.png";
loadedImages[2] = new Image();
loadedImages[2].src = "images/animationFrame3.png";
loadedImages[3] = new Image();
loadedImages[3].src = "images/animationFrame4.png";
you could even put them in a hash if it makes sense for you app where instead of
loadedImages[0] = new Image();
loadedImages[0].src = "images/animationFrame1.png";
you do this
loadedImages['frame1'] = new Image();
loadedImages['frame1'].src = "images/animationFrame1.png";
once you have all your images loaded you references them for drawing by doing calling them like so:
//Using the integer array
ctx.drawImage(loadedImages[0], 0, 0);
//OR
//Using the stringed hash
ctx.drawImage(loadedImages['frame1'], 0, 0);
You want to separate your loading process from your rendering process because loading images is process intensive thus will slow your animations down if you are loading things while rendering.
That is not to say that you can't ever load anything while rendering, but instead just be conscience that this will slow animation speed down.
Here is an article on preloading images.
There is another post on here which talks about consistent animation speed on all browsers here
Note the link posted by the green checked answer
Other things to be noted are making sure to only clearing and redrawing bounding boxes as mentioned in the post with HTML 5 canvas optimization. That link has some really good techniques to be conscience of while developing canvas animations.
Here are some frame works as well which might come in handy to cross compare what you are doing to what other engines are doing.
Hope some of this helps. I am new to javascript (only started coding with it about 2 weeks ago) and so there could be better ways to do things but these are the things I have found thus far.
window.requestAnimationFrame() is one sure way to make your animation run smoother.
https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame
(cross browser http://paulirish.com/2011/requestanimationframe-for-smart-animating/ )
However it doesn't fix the possible problems with your drawing code which was missing from the question.