1
\$\begingroup\$

I built a 404 page which involves parallax scrolling text. The program updates the position of 100-200 text nodes at each animation frame called with window.requestAnimationFrame. It runs decently well on my MacBook (although it occupies significant CPU), but on weaker computers like my phone, it runs very sluggishly.

How can I optimize my code to achieve better performance?

var scene;

function randint(low, high) { // Return a random integer between low and high
  return Math.floor(( Math.random()*(high-low) ))+low;
}

// Add a layer to the scene

function addLayer() {
  // Make  a "layer" in the parallax animation
  var layer = document.createElement("div");
  // Give it a random Z value (0 to 1)
  layer.setAttribute("class", "layer");
  layer.setAttribute("data-z", Math.random().toFixed(2));

  // Make a div inside that says "404"
  layer.appendChild(document.createTextNode("404"));
  // Random X position
  layer.style.left = randint(-10, window.innerWidth) + "px";
  // Below bottom of screen
  layer.style.top = window.innerHeight + "px";
  // Font size and weight based on Z value
  var z = parseFloat(layer.getAttribute("data-z"));
  layer.style.fontSize = Math.floor(z * 150) + "px";
  layer.style.fontWeight = Math.ceil(z * 4) * 100;
  // Random opacity
  layer.style.opacity = (Math.random() / 10).toFixed(2);

  // Add nodes to appropriate parents
  scene.appendChild(layer);
}

function updatePositions() {
  var layers = scene.getElementsByTagName("div");
  var layer, div, z;
  for (var i = 0; i < layers.length; i++) {
    layer = layers[i];
    z = parseFloat(layer.getAttribute("data-z"));
    y = parseInt(layer.style.top.slice(0, layer.style.top.length - 2));
    layer.style.top = (y - Math.ceil(z * 10)).toFixed(2) + "px";

    if (y < (-150)) { // Max fontsize is 150px
      scene.removeChild(layer);
    }
  }
}

function updateColor() {
  var hue = (parseFloat(document.body.getAttribute("data-hue")) + 0.25) % 360;
  document.body.setAttribute("data-hue", hue.toString());
  var hex = chroma(hue, 1, 1, "hsv").brighten().hex();
  document.body.style.color = hex;
  document.getElementsByTagName("a")[0].style.backgroundColor = hex;
}

function update() {
  addLayer();
  updatePositions();
  updateColor();
  window.requestAnimationFrame(update); // Recur
}

scene = document.getElementById("scene");
window.requestAnimationFrame(update);
body {
  background-color: #222;
  margin: 0;
  width: 100vw;
  height: 100vh;

  /* Global font settings */
  color: #fff;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#scene {
  position: fixed;
  top: 0;
  left: 0;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.layer {
  position: absolute;
}

.main {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;

  text-align: center;

  opacity: 0.5;
}

h1 {
  font-size: 200px;
  margin-bottom: 0;
}

.main p {
  font-size: 150%;
  margin-top: 0;
}

.main a {

  color: #222;
  background-color: #fff;
  padding: 10px;

  text-decoration: none;
  font-size: 150%;
  font-weight: 600;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.1.1/chroma.min.js"></script>
<body data-hue="0">
  <!-- Background -->
  <div id="scene"></div>
  <!-- Foreground  -->
  <div class="main">
    <h1>404</h1>
    <p>That page couldn't be found.</p>
    <a href="http://luke.deentaylor.com/">Home</a>
  </div>

</body>

Running this snippet in full window will more accurately emulate how it will appear on my website.

Here's a JSFiddle if it's preferred.

\$\endgroup\$
  • 1
    \$\begingroup\$ Use Chrome profiler, it shows that removeChild is slow -> re-use a container when 404 is out of view, do the same for addLayer. Also that coloring is slow, write your own HSL without sqrt using formulas in easyrgb.com (PS. not a JS dev). \$\endgroup\$ – Aybe Jun 2 '16 at 23:54
1
\$\begingroup\$

According to my experience, web animations don't use CPU as such, but rather prefer the GPU at times for rendering. On phones, custom animations like yours will run without any moderation of fps, and so a lag is kinda expected. On top of it, js doesn't use parallel threads of course, so it won't even use the remaining core if they are available.

What I'd suggest is use some good library to run these. I remember using Three.js and it ran on my Lumia 520 at 8FPS but still without lag, as the animation was optimized for low FPS. The same ran on my PC at 60FPS smoothly just the same.

Hope this helps.

\$\endgroup\$

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.