4

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?

1
  • 1
    DOM access is a lot slower than you are giving it credit for. You are creating new elements every 40 milliseconds and adding to them DOM... ouch. Try looking into Canvas and/or requestAnimationFrame for what you're trying to do. Commented Oct 7, 2015 at 15:57

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this very insightful answer. I know the canvas is more efficient, but I wanted to see how far I could push it using DOM, CSS and JS only.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.