3
\$\begingroup\$

I'm moving fewer than 10 position: absolute elements around the screen. An enormous amount of CPU time (profiled in Chrome as (program) i.e. native code) is used where I do the following:

style.left = monster.x + 'px';
style.top  = monster.y + 'px';

... where style belongs to the DOM element representing the monster in question.

This is a string concatenation cost since as soon as I remove that + 'px', the problem disappears. Of course, then absolute positioning doesn't work onscreen since the px is required by the browser.

I'm running this logic via requestAnimationFrame which runs about 60FPS... am currently amending such that position updates only occur every 2-3 frames as this will certainly help. Am also modifying so that only when the numeric value changes, is the style property set anew, rather than setting the style property on every active frame.

Even so, is this how CSS sprite updates are done? Is it always this slow? The only alternatives I can see are to use canvas (usually also slow, for different reasons) or WebGL. I'd prefer CSS for simplicity.

\$\endgroup\$
  • 1
    \$\begingroup\$ Are you sure the perfomance problem lies within string concatenation? If you only perform string concateation without assigning the value to a style-property, do you get the same performance drop? \$\endgroup\$ – bummzack Sep 8 '14 at 9:19
  • \$\begingroup\$ @bummzack I commented out just the + 'px' at the end of each of these 2 lines to see the result: CPU immediately goes to 98% idle. Note also that because the CPU time is noted under category (program), there is no further information on what the cause is, but it does indicate that as soon as the browser is able to parse the value with the px part, a lot of time is lost in native code. The only other possible cause for slowdown that I can see is that I'm moving several individual mini-canvases (each element is a uniquely-modified sprite), since it can only do that once px is present... \$\endgroup\$ – Engineer Sep 8 '14 at 9:24
  • \$\begingroup\$ Commenting out the + 'px' is something different though. I suggest you test the following too: A) Perform concatenation without assigning the value to the CSS style. B) create a concatenated string at initialisation and assign it in your game-loop. Check what ends up using more resources. \$\endgroup\$ – bummzack Sep 8 '14 at 9:30
2
\$\begingroup\$

I doubt the string concatenation is the culprit here, but you could always use the transform CSS property, which is supported in almost every recent browser.

Since most CSS transforms also use px values (for example: translate), you should use raw matrix coordinates.

So to offset an element by say x: 10px, y: 20px you would use:

transform: matrix(1, 0, 0, 1, 10, 20)

But since you're dealing with CSS properties, there's always going to be a string (the property value) that will be interpreted by the browser at some point. So I'm not sure if that's going to speed up things considerably.

Internally you could work with matrices as arrays, say:

var transformMatrix = [1, 0, 0, 1, 10, 20];

Then when it comes to rendering, assign it like so:

style.transform = "matrix(" + transformMatrix.join() + ")";

But as I mentioned previously, there's no way to avoid string concatenation/conversion at some point if you're dealing with CSS properties. Another thing to note is that the transform property requires a vendor-prefix in some browsers.

Libraries like jQuery will automatically generate the prefix for you, if you're not using such a library, you should consider these vendor-prefixes: -moz-, -ms-, -webkit-, -o-. A CSS declaration would then look like this:

.myElement {
    -moz-transform: matrix(1, 0, 0, 1, 10, 20);
    -ms-transform: matrix(1, 0, 0, 1, 10, 20);
    -webkit-transform: matrix(1, 0, 0, 1, 10, 20);
    -o-transform: matrix(1, 0, 0, 1, 10, 20);
    transform: matrix(1, 0, 0, 1, 10, 20);
}

Which translates to the following JavaScript code:

style.MozTransform = 
style.msTransform = 
style.WebkitTransform = 
style.OTransform = 
style.transform = 'matrix(1, 0, 0, 1, 10, 20)';

But instead of doing that, you better figure out the required prefix of the browser beforehand and then just use the property that is supported by the browser. Here's a short explanation how to figure out vendor prefixes using JS.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This had nothing to do with View update code. It was something else entirely -- inefficient object creation and math code -- in the associated Controller update. Accepting your answer as it may be useful for others and I appreciate the assistance which led to a solution. \$\endgroup\$ – Engineer Sep 8 '14 at 12:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.