Originally posted over at HarnerDesigns.com
I like space. I grew up watching Star Wars and I'm still fascinated by the work coming out of SpaceX and NASA. It made sense to design my portfolio with the Cosmos in mind. Obviously I needed a way to have stars in the background of stuff.
My initial thought was to just create a large starry background in Illustrator, set it as the background-image
and be done with it. I've been working with SVG a lot lately so I decided to do it with code instead. The portfolio is built on WordPress and being developed from scratch so I had the power of PHP at my fingertips.
PHP Generated SVG
<?php $stars = 1000; ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewbox="0 0 600 600">
<g fill="#FFF" id="Stars">
<?php while($stars > 0){
$cx = mt_rand(0, 600);
$cy = mt_rand(0, 600);
$r = mt_rand(0,100) / 100;
$o = mt_rand(0,100) / 100;
echo '<circle cx="' . $cx . '" cy="' . $cy . '" r="' . $r .'" opacity ="'.$o.'"/>';
$stars--;
} ?>
</g>
</svg>
Just a simple While loop creating 1000 different <circle>
elements with varying sizes, positions and opacities. Worked easy enough but there were several issues with this method.
Created Too Many DOM Elements
This was stored in a components/stars.php
file that I could include with WordPress's get_template_part('components/stars')
whenever I needed stars.
I shot for simple and I ended up with slugish.
Generating 1000s of DOM elements at a time is definitely not good for performance. Google's Lighthouse Test suggests "the sweet spot is a tree depth < 32 elements and fewer than 60 children/parent elements". Definitely nowhere near 1000+.
Animation Is Impossible
One of the advantages of doing the stars with SVG was supposed to be the ability to easily add some animation. Making the stars slide around to simulate flying through space. SVG is great for that, within reason. Forcing my ancient laptop to try to redraw 1000 little circles anywhere near 30 frames per second was borderline criminal and possibly violates the Geneva Convention.
Canvas To The Rescue!
<canvas class="stars"></canvas>
var canvases = jQuery(".stars");
canvases.each(function() {
var ctx = jQuery(this)[0].getContext("2d");
var mp = 1000;
var particles = [];
for (var i = 0; i < mp; i++) {
particles.push({
x: Math.random() * W, //x-coordinate
y: Math.random() * H, //y-coordinate
r: Math.random() * 2, //radius
d: Math.random() * mp, //density
o: Math.random() - 0.5 //opacity
});
}
function draw() {
for (var i = 0; i < mp; i++) {
ctx.beginPath();
var p = particles[i];
ctx.fillStyle = "rgba(255,255,255," + p.o + ")";
ctx.moveTo(p.x, p.y);
ctx.arc(p.x, p.y, p.r + (p.x / jQuery(this).width()), 0, Math.PI * 2, true);
ctx.fill();
}
}
draw();
});
// Wordpress makes you use `jQuery` instead of `$`
Essentially the same loop as before just drawing elements on the canvas instead of in the DOM. It currently generates an array of particle information and then loops through that array to draw the stars. As I'm writing this it makes me wonder if it would be better to just call the math.random()
to generate the values as they're being drawn. Might test that out later.
In Summary
SVG is great for a lot of things. Drawing a lot of stars is not one of them. Canvas is more lightweight, doesn't clog up the DOM, and animates smoothly even on older devices (within reason). Check my new and improved optimized stars over at Harner Designs! (Still need to actually animate them though.)
Top comments (0)