Rob Audenaerde
Rob Audenaerde

Reputation: 20099

CSS animation performance

I have a small hobby project in which I try to build a matrix rain: 'matrix rain'.

See demo here. Or this JSFiddle

My question is: how can I make this more efficient, as I can see it gets slow when I add a lot of columns.

I have implemented it as rendering a lot of absolute positioned divs that are animated.

Here is my CSS:

div
{
    position:absolute;
    width:1em;
    display:inline-block;
    color: black;
    animation-name: example;
    animation-duration: 2s;
    text-shadow: none; 
}

@keyframes example 
{
    0%   {color: white;  text-shadow: -1px 1px 8px white;}
    15%  {color: #5f5 ;  text-shadow: -1px 1px 8px #5f5 ;}
    100% {color: black;  text-shadow: none;}
}

In javascript I set some custom styling for each div, where I vary some settings, like font-size, animation speed etc.

Main part of the JS:

var textStrip = ['诶', '比', '西', '迪', '伊', '吉', '艾', '杰', '开', '哦', '屁', '提', '维'];

var matrixcol = function()
{
    var top =  Math.floor(Math.random() * $(window).height() * 0.5);
    var size = 10 + Math.floor(Math.random()*10);
    var col =  Math.floor(Math.random() * $(window).width() - size);
    var ms  =  500 + Math.floor(Math.random()*1500);

            var timer;
    var aap = function()
    {
        var randomNumber = Math.floor(Math.random()*textStrip.length);

        var newelem = $("<div style='font-size:"+ size+ "px;top:"+top+"px; left:"+col+"px;animation-duration:"+ 2*ms + "ms'>" + textStrip[randomNumber] +  "</div>" );
        $('body').append(newelem);
        top+=size;
        setTimeout( function() {newelem.remove();}, (1.6*ms)-(ms/40)); 
        if (top>$(window).height()-size)
        {
            size = 10 + Math.floor(Math.random()*10);
            top=0; Math.floor(Math.random() * $(window).height() * 0.5);
            col =  Math.floor(Math.random() * $(window).width() -size);
            ms = 500 + Math.floor(Math.random()*1500);
                            clearInterval(timer);
            timer = setInterval(aap, ms/40);
        }
    }
    timer = setInterval(aap, ms/40);

}


$( document ).ready(function() {
   var i;   
   for (i = 0; i < 25; i++) {
       matrixcol();
}

I have tried to use the chrome profiling, that shows my a warning:

Long frame times are an indication of jank and poor rendering performance.

The link that is provided gives some insight; however, as far a I can see I don't have much layouting going on.

tl;dr It is slow. What would be a good performance optimizations?

Upvotes: 4

Views: 1861

Answers (1)

Herrington Darkholme
Herrington Darkholme

Reputation: 6315

After several try, I think your best solution is looking to canvas, if the exact animation is desired.

The ending result I get is here. Not as exact as yours but get a 50+ fps. For every modification I have added comment, please check it out.

Cache

The easiest thing you can do is cache $(window).height(). It is usually a stable number, no need to re-query it. And resize handler can be added to adapt viewport change. Cache window size changes my fps from 9~10 to 12~15. Not big, but a low-hanging fruit.

Expensive Style

The next thing you need to do is remove text-shadow, it is a very expensive style, given the node number in your case. (Why? It requires CPU paints shadow and GPU cannot help here. read more here, and html5rocks). If you are interested in Chromium implementation, text-shadow is done in TextPainter.cpp, painted by GraphicContext, which is done primarily by CPU. And animating text-shadow is a performance nightmare. Change this boost fps to 20+.

DOM Access

The last thing is DOM access, every frame update requires a dom insertion and, correspondingly, a dom removal by yet another timer. This is painful. I try to reduce DOM removal, so I added a container for each column. And adding container does add DOM complexity, I have to wait for the animation end to update the container. After all, it saves many dom manipulations, timers and closures. Furthermore I updated setTimeout to requestAnimationFrame so that browser can orchestra DOM access better.

Combining the above three, I got a 50+ fps, not as smooth as 60fps. Maybe I can further optimize it by reducing DOM insertion, where all characters in a column is inserted once, and for each character the animation-delay is at interval.

Looking on Canvas

Still, your animation is quite harsh job for DOM based implementation. Every column is updated, and text size varies frequently. If you really want the original matrix effect, try canvas out.

Upvotes: 4

Related Questions