Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my CSS3/jQuery animation's performance so slow?

Why oh why is this so slow? I have made an isometric grid of cubes with css3 that bobs up and down on mouseover. It works great on Firefox with a grid size less than about 12 and with Chrome works pretty good under about 18. I have a decent video card and CPU and the thing that is bugging me is why it is so slow to animate just one cube's animations if I make sure I only mouseover one cube. Does my JavaScript need optimisation or is this just to be expected from the current implementation of browser CSS3 and JavaScript engines? Full test case below includes slider to change grid size on the fly, can either download for yourself or visit this jsfiddle version graciously provided by Doug.

<html>
    <head>
        <style type="text/css">
            body
            {
                background: black;
            }

            .cube
            {
                position: relative;
            }

            .cube .rightFace, .cube .leftFace
            {
                height: 25px; width: 10px; padding: 5px;
            }

            .leftFace
            {
                position: absolute;

                -webkit-transform: skew(0deg, 30deg);
                -moz-transform: skew(0deg, 30deg);
                -o-transform: skew(0deg, 30deg);

                -moz-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                -webkit-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                -o-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;

                border: 1px solid black;
            }

            .rightFace
            {
                -webkit-transform: skew(0deg, -30deg);
                -moz-transform: skew(0deg, -30deg);
                -o-transform: skew(0deg, -30deg);

                position: absolute;
                left: 19.5px;

                border: 1px solid black;
            }

            .topFace div
            {    
                width: 19px;
                height: 19px;


                border: 1px solid black;

                -webkit-transform: skew(0deg, -30deg) scale(1, 1.16);
                -moz-transform: skew(0deg, -30deg) scale(1, 1.16);
                -o-transform: skew(0deg, -30deg) scale(1, 1.16);
            }

            .topFace
            {
                position: absolute;

                left: 10.25px;
                top: -16.5px;

                -webkit-transform: rotate(60deg);
                -moz-transform: rotate(60deg);
                -o-transform: rotate(60deg);
            }

            #slider
            {
                width: 200px;
                margin: 0 auto;
            }
        </style>
        <link type="text/css" rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" />
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
        <script type="text/javascript">
            function refreshCubes()
            {
                $('.box').empty();
                var x = $("#slider").slider("value");
                var initialTop = 50;
                var initialLeft = 450;

                for(var i = 1; i < x; i++)
                {
                    for(var j = 1; j < x; j++)
                    {
                        var cube = $('<div class="cube"><div class="topFace"><div></div></div><div class="leftFace"></div><div class="rightFace"></div></div>');

                        cube.css(
                        {
                            left : initialLeft + (20 * i) + (-19 * j) + 'px',
                            top : initialTop + (11 * i) + (11 * j) + 'px'
                        });

                        cube.find('.topFace div').css('background', 'rgb(100,' + Math.ceil(255 - 16 * i) + ',' + Math.ceil(255 - 16 * j) + ')');
                        cube.find('.rightFace').css('background', 'rgb(35,' + Math.ceil(190 - 16 * i) + ',' + Math.ceil(190 - 16 * j) + ')');
                        cube.find('.leftFace').css('background', 'rgb(35,' + Math.ceil(190 - 16 * i) + ',' + Math.ceil(190 - 16 * j) + ')');
                        cube.children('div').css('opacity', '.9');

                        cube.hover(function()
                        {
                            $(this).animate({top: '-=25px'}, 400, 'easeInCubic');

                        }, function()
                        {
                            $(this).animate({top: '+=25px'}, 400, 'easeOutBounce');
                        });

                        $('.box').append(cube);
                    }
                }
            }

            $(document).ready(function()
            {    
                $('#slider').slider(
                {
                    value: 9,
                    max: 30,
                    min: 2,
                    slide: refreshCubes,
                    change: refreshCubes
                });

                refreshCubes();
            });
        </script>
    </head>
    <body>
        <div id="slider"></div>
        <div class="box"></div>
    </body>
</html>
like image 218
SimonDever Avatar asked Nov 05 '11 02:11

SimonDever


People also ask

Do CSS animations affect performance?

Compared with animating elements using JavaScript, CSS animations can be easier to create. They can also give better performance, as they give the browser more control over when to render frames, and to drop frames if necessary.

Does CSS animation slow down website?

It slows down development because it can confuse people reading the stylesheet. It can also mean slower rendering times, because it's just more CSS for the browser to look through while it matches all of the CSS to its appropriate HTML elements.

Why would someone use CSS animations instead of JavaScript animations?

TL;DR # Use CSS animations for simpler "one-shot" transitions, like toggling UI element states. Use JavaScript animations when you want to have advanced effects like bouncing, stop, pause, rewind, or slow down.


2 Answers

This one is faster: http://jsfiddle.net/JycdN/1/

I optimized the jQuery code itself. Also, its a bad idea to animate a "+=25px" style CSS value because each time you do that you are forcing the browser to make extra CSS calculations on top of the animation calculations. Instead of doing that, you might as well just use plain CSS3 animations. Check this out: http://matthewlein.com/ceaser/

Its better for animate() to have a static value (in this case, the original and raised positions which i've stored for each cube in the data- attributes) so the only calculations being done are the ones by the javascript interpreter itself.

The live() method automatically adds the events to the cubes whenever they are recreated, so there's no need to set the events inside the refreshcubes function. Also, James Montagne pointed out that using the live() method makes the whole thing behave like Daniel described in his answer (which is faster). EDIT: I've made it even faster by using the on() method instead of live(). The event context for hovering is now on each cube instead of on the whole document. See this question for details: How does jQuery's new on() method compare to the live() method in performance?.

Wherever possible, make one single jquery object and reference to it with a variable, that way you don't recreate a new object every time.

Overall, I noticed quite an increase in performance, but its still slower than I'd imagine capable. Maybe the CSS transforms are re-calculated every time the cubes are moved (even by a pixel).

EDIT: As addedlovely suggested, modifying the jQuery.fx.interval helped make it a little faster.

I think you'll have more luck drawing the cubes in 2D canvas or with 3D webGL canvas.

Try using three.js or processing.js to draw the cubes and animate them.

EDIT: I've made it even faster using hardware-accelerated CSS3 animations in Google Chrome (other browsers are still developing these CSS3 features so it only works in CHrome for now): http://jsfiddle.net/trusktr/JycdN/35/

like image 50
trusktr Avatar answered Oct 20 '22 17:10

trusktr


You have n^2 listeners, honestly surprised things work as well as they do. Jquery does not have enough information to optimize the algorithm, but we do. Instead of caring about where the mouse is n^2 times we actually only need to check once.

Add a listener to the entire board. On call calculate which cell is being hovered over and animate that one. Keep a list of cells in the 'on' state and calculate if the mouse is still hovering over them, if not animate them to their original state and remove them from the list.

Now instead of n^2 operations a frame you have 1. More, but much faster, code and the same functionality. Yes jquery is awesome and having to do this yourself is a pity. Keep in mind though that jquery is designed for those 10s of listeners which an average site will use, not for 100s or 1000s.

@trusktr: It is indeed faster but still many times slower than necessary.

like image 22
Daniel Avatar answered Oct 20 '22 18:10

Daniel