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.
removeChild
is slow -> re-use a container when 404 is out of view, do the same foraddLayer
. Also that coloring is slow, write your own HSL withoutsqrt
using formulas in easyrgb.com (PS. not a JS dev). \$\endgroup\$ – aybe Jun 2 '16 at 23:54