I'm rendering images using HTML5 canvas and I'm facing serious performance issues when resizing multiple (hundreds) canvas at once.
Is there any tricks to get resizing as smooth as in mobileme gallery ?
Thanks
We don't have much information about your procedures, but the slowness either resides in the fact that you have hundreds(!) of canvases, versus mobileme's 20-something.
You'll notice as you scroll down in mobileme that the number of canvases does not increase. There are only ever as many canvases as the page needs. Or rather, when you scroll down the canvases you can no longer see are no longer there (so to speak).
The only other place for optimization is in your redrawing code, since when you do a canvas resize you need to be redrawing as well. But first try to optimize the number of canvases you are using to be fewer.
Related
So I am making this horizontal scroll site which has a ton of images. I planned on using svgs for the entire site, but with only 20-30 svg images of medium to high complexity used in the page, and chrome already seems to be showing som jank and high paint times for scroll (and firefox is even worse, though safari seems to do a lot better).
Scroll timeline
View the site (scroll works on mac only, windows users can use arrow keys)
My question is, if I were to use pngs instead of svgs, would it reduce the paint times and hence the jank? Why is the browser struggling with only around 20 odd svg images?
As was my doubt, the problem turned out to be something completely different. Browsers are more than capable of handling multiple vector images. But what they aren't good at (and understandably so) is at redrawing those images very often.
Problem
My long horizontal scroll site was quite wide (30,000px). I had a background-color property applied to one of lower z-index'ed div's to represent the sky throughout the scroll site. I didn't want the sky to stretch the entire 30,000px since it essentially didn't change much, and hence gave it viewport width and height, with:
position:fixed;
Not a very smart move. Turns out that this property was causing my document layer to be repainted on every frame. Initially I though it was normal for browsers to do so on scroll, since Robby Leonardi's site, which I used as reference also repainted every frame.
Solution
Thanks to this article by one of the chrome dev tools developers, I set aside conventional wisdom, and make the sky layer
position:absolute;
and stretched it the entire site width, and boom! The paint rectangles were gone. And the scroll performance was smoother than butter.
Other solutions I tried
Hiding elements not near the viewport to make painting lighter, as suggested by #philipp, but didn't yeild any appreciable difference. Also, it felt super-hacky, and it wasn't targeting the root cause of the problem.
I tried modularizing my site into scenes and using translateZ(0) hack on each scene so that only the smaller scenes get repainted instead of the document. This actually helped quite a bit, and scroll was decent. Then,
I gave all the svg images their own layer by using translateZ(0). I started getting FPS of around 60 now, but again, this wasn't the right way of doing things.
I once had a similar thing. The SVG was 10 or more times as wide as the one shown above, it contained ~20k elements and was about 3MB in size. The only Thing what brought back performance (since it was a jump and run game) was an algorithm which was able to find all elements whose bounding box overlapped the viewport. With this I could use display: none; to hide everything what is invisible.
That reduced the amout of visible elements to ~150 per frame and the game ran again fluently.
I used a balanced binary tree (avl tree) and a one dimensional range query, since the height of the viewport was always the same as the image.
Good luck!
[EDIT]
Forgot to leave something like an anwer. From my Experience are large/huge SVG Graphics a bottleneck in rendering, especially if there is a lot of scripting happening. If you do not need any Interactivity with the elements from the Graphic, so nothing than a large Background Image, I would recommend to use a Tile map, based on PNG images, that is the standard way in Jump'n'Run games with huges »worlds«, so you can gain perfomance in two Points:
Rendering is faster,
You can »lazy ajax load« tiles, depending on visibility, to prevent users to download the »whole world« at startup.
Additinally you could use something like PIXI.js to render with WebGL, what will push the performance drastically and with it comes support for Tilemaps and Spritesheets.
If you insist on the advantages of Vector Grafics (Scaling, Interactivity) than you need to find a way to hide as much elements as possible to keep the frame rate high.
I am working on a game that uses HTML5 canvas 2D context drawing on a Chromecast device (which doesn't have hardware acceleration). I've noticed that drawing two objects in one frame will trigger a repaint of the entire region that contains both of them. As a "worst-case", imagine that I want to only change the color of the top-left and bottom-right pixels of a large canvas element. If I use two one-pixel fillRect calls to do this, it (WebKit/Blink, at least) will mark the entire canvas dirty, triggering a very expensive paint operation. I believe this should link to the code that performs this logic in Chromium:
https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp&l=218
Is there any way to convince the browser to actually perform two small paint operations instead of one (excessively) large one? Or would this always be slower, despite the fact that it's re-drawing significantly less? I have tried putting the elements on different canvas elements layered on top of each other, but the browser still seems to catch it and batch them together (at least as shown by the repaint regions in DevTools).
As far as I know, currently you can't do much better than that; one main drawback is that double buffering is not implemented either, which would have improved the performance. We will be seeing improvements in these areas moving forward.
I've already read all the stuff around scrolling:
Structuring an HTML5 Canvas/JS Game
and so on:
HTML5 Canvas tutorial
The secret to silky smooth JavaScript animation
Google search "HTML5 Scrolling"
Canvas Games
Build a vertical scrolling shooter game with HTML5 canvas
Math mayem
CAAT JavaScript framework
(The latest one is impressive, but even though almost everything is done there's nothing about scrolling).
Here's what I'm thinking about, and I didn't found something valueable about that. An idea just came to my mind and I'm wondering if I should take a lot of time thinking about that and trying, or not (that's why I'm asking here actually).
I'm planning to do a game with a scrolling "à la" Mario.
The big drawback about scrolling is that you have to redraw the whole background.
I've already avoided two performance problems of the sprite / scroll: create two canvas one top of each other:
the background
the sprites
And just erase the sprites.
The problem is about the background: I'm making a full copy of the background to the "visible" canvas. (note: there's no problem about flickering because writing in JavaScript is a blocking operation and all modern browsers handle vertical synch, so no double buffering needed).
Here's an old version of what I'm writing, but you'll get the big picture:
Test HTML5
Now I'm wondering for the scrolling: what if I do a "background div" instead of a canvas, with the appropriate CSS (background image for the background), and write the tiles on the image directly, then change CSS to simulate the scrolling? Should it be faster? If so, why? Is there any good idea out there for this?
On a semi-modern+ computer with a semi-recent+ browser, the fastest thing to do is probably to take a super-long div with background images, set overflow to hidden and scroll by adjusting scrollLeft or scrollTop properties. This is MUCH faster than adjusting CSS properties as it shouldn't trigger any reflow calculation in the CSS engine. Basically, any time you touch a DOM property that could have CSS impact, the whole (or at least a lot of) of) the structure of the document needs to be re-checked and re-rendered.
You could load in chunks of the backgrounds as they get close to avoid the one giant massive image load. I don't believe there is any 100% surefire way to pull an image out of memory across browsers but removing references to it in the DOM or in your CSS probably doesn't hurt when you've scrolled far enough past a piece of your background. That way the browser's garbage collector at least has the option of clearing memory.
In pan-mobile solutions that rely on webviews (like Cordova/Phonegap), however, well that's why I arrived at this question.
I have no idea but I'm pretty sure mixing HTML and canvas is a lousy idea for performance purposes. Right now I've got a not-super-complicated android game choking on 50x50 100px tiles in a canvas element in an android web view that also has some basic HTML in the mix for stuff like controls and separating a couple other canvas elements from the rest. I basically have a birds-eye-view ship cruising around and doing scans with a cheesy radiating circle grahic that reveals elements on a map fog-of-war style. Works great in a browser. Complete disaster in the cordova app version.
I suspect the only way I'm going to realize my pan-mobile game dev dreams is to use one of the many openGL-wrapped-in-a-canvas-API solutions out there and completely ditch the HTML which I find damned convenient for UI implementation, given that the bulk of my experience is in web UI. Another general tip for web view HTML is to avoid scrolling within internal elements if you can (so just let the body handle overflow). Scrolling overflow in anything but the body didn't even work in Android 2's webviews and it still seemed to cause 4.1's views to choke on an earlier/simpler version of the app I'm working on.
My final conclusion: after many tries, including Eirk Reppen suggestion, I write directly "raw" into hidden parts of the canvas and use CSS: all webbrowsers handle already image flickering and all that stuff around, and they have already optimized everything.
So each time I've tried to "optimize", the results were worse.
It seems that webbrowsers are made to handle properly basic stuff made by beginnners... maybe because 99% of HTML content is made by beginners.
Here is a demo of scrolling an oversize canvas by changing the CSS margin, this one based on scrolling with time: https://jsfiddle.net/6othtm0c/
And this version with mouse dragging: https://jsfiddle.net/ax7n8944/
HTML:
<div id="canvasdiv" style="width: 500px; height: 250px; overflow: hidden">
<canvas id="canvas" width="10000px" height="250px"></canvas>
</div>
JS for the scroll-with-time version:
canvas = document.getElementById("canvas");
context = canvas.getContext('2d');
for (var i = 0; i < 1000; i++) {
context.beginPath();
context.arc(Math.random() * 10000, Math.random() * 250, 20.0, 0, 2 * Math.PI, false);
context.stroke();
}
var t0 = window.performance.now();
setInterval(function(){
canvas.style.marginLeft = -(window.performance.now() - t0)/5 + "px";
}, 5);
fastest scrolling is to scroll using CSS. So you draw all background once, not only visible part, but all, and hide that is not visible, and use css to scroll it (margin, or position). No redraw, only CSS changes. This work really fastest. But if all map is really huge, other custom ways can be better.
I have a page that has pretty heavy (mid-weight rather) canvas operations going on. To cater for users on mobile devices and older computers I was thinking I could implement a mechanism that will check if the canvas element is actually visible and decide if the constant calculations and canvas updates (animation running at 30fps) do have to be done or not.
This is working fine, yet when doing a performance test with the Chrome Dev Tools I noticed that even when I disable my visibility check and just let things render all the time the CPU usage of the function in question drops quite a bit when no part of the canvas element(s) is visible (although in theory it should still be performing the same tasks). So: at least on my computer running Chrome 17 it does not make a real difference if I check for the element's actual visibility.
To cut a long story short: Do I need to do this or are browsers smart enough to handle such a case without even telling them (and I can save the visibility checking)?
EDIT:
So I made some "research" on this topic and built this fiddle.
What happens is that it just generates noise at 30 frames per second. Not too pleasing to the eye but, well... The upper part is just a plain div to block the viewport. When I scroll down and have the canvas element in the viewport CPU Usage tells me it's taking up about 40%, so apparently the browser does have quite a lot to do here. When I scroll back up so that I just have the maroon colored div in my viewport and profile the CPU usage it drops to sth around 10%. When I scroll back down: usage goes up again.
So when I implement a visibility check like in this modified fiddle, I do see an increase (a tiny one to be honest) in CPU usage instead of a drop (as it has the additional task of checking if the canvas is inside the viewport).
So I am still wondering if this is some side effect of something that I am not aware of (or I am making some major mistake when profiling) or if I can expect browsers to be smart enough to handle such situations?
If anyone could shed a light on that I'd be very thankful!
I think you're confused between whether the logic is running and whether the rendering is happening. Many browsers now hardware-accelerate their canvases so all rendering happens on the GPU, so actual pixel pushing takes no CPU time anyway. However your tick function has non-trivial code to generate random noise on the CPU. So you're only really concerned over whether the tick function is running. If the canvas is offscreen, it certainly won't be rendered to the display (it's not visible). As for the canvas draw calls, it probably depends on the browser. It could render all draw calls to an off-screen canvas in case you suddenly scroll it back in to view, or it could just queue up all the draw calls and not actually do anything with them until you scroll the canvas in to view. I'm not sure what each browser does there.
However, you shouldn't use setInterval or setTimeout for animating Canvas. Use the new requestAnimationFrame API. Browsers don't know what you do in a timer call so will always call the timer. requestAnimationFrame on the other hand is designed specifically for visual things, so the browser has the opportunity to not call the tick function, or to reduce the rate it's called at, if the canvas or page is not visible.
As for how browsers actually handle it, I'm not sure. However, you should definitely prefer it since future browsers may be able to better optimise requestAnimationFrame in ways they cannot optimise setInterval or setTimeout. I think modern browsers also reduce the ordinary timers to 1 Hz if the page is not visible, but it's definitely much easier for the browser to optimise requestAnimationFrame, plus some browsers get you V-syncing and other niceness with it.
So I'm not certain requestAnimationFrame will mean your tick function is not called if the canvas is scrolled out of view. So I'd recommend using both requestAnimationFrame and the existing visibility check. That should guarantee you the most efficient rendering.
From my own experience it renders whatever you tell it to render regardless of position on screen.
An example is if you draw tiles, that exceeds the canvas size, you will still see the performance drop unless you optimize the script.
Try your function with a performance demanding animation, and see if you still get the same results.
I'm working on a site that will be showing a lot of information via divs. Its basically a chart out of divs. The way its setup is I've got an outer div with a set size that you can scroll the contents around (click and drag like google maps).
From here I can 2 ways of going, have 1 large inner div that is moved around with all the chart divs within it. This would be by far the easiest approach. The other option I see is a tiled approach where I break the large inner div up into smaller divs and dynamically add/remove them as needed.
The chart itself is potentially 1999425014 pixels square. Each point is made up of 6 divs and there are 100,000+ points.
What would be the best way to move forward?
I believe most modern browsers render sections of pages only as needed. If the entire page fits in RAM, only the section that is currently visible is being drawn. I conclude this because large documents do not slow down my computer when I use other programs, but there is a bit of lag when I scroll across one. Plus, when the window is repainted through the operating system's APIs, only the pixels that are visible should be rendered. That would only make sense when designing it, anyway.
Surely you can fit as many as you want on a page without worrying about real-time performance hits, unless you're talking about scrolling. Rendering the document should take less time than retrieving it out of system memory and determining where the scroll bars are.
Best of luck, and merry Christmas.
-Tom