Reputation: 20099
I have a small hobby project in which I try to build a 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 div
s 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
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.
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.
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+.
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.
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