This little project was the result of an email request, which I'm sharing here because I thought it was quite interesting. The question was, how could you make a starfield animation (like the old screensaver) with SVG.
Drawing
The first thing we need is a star. I drew one in Inkscape. Since we want lots of the same shape, I put it a def element, which means we define the shape for later use.
<defs>
<path id="star" fill="#ee2" d="M-4 -17.2L-8.7 -3.6 -23.1 -3.3 -11.6 5.4 -15.8 19.2 -4 11 7.9 19.2 3.7 5.4 15.2 -3.3 0.8 -3.6z"/>
</defs>
Then I created a background and a group which will contain the stars:
<rect width="450" height="450" fill="#228" />
<g id="star-group"></g>
Scripting
Everything else is done with Javascript. First we add the script element and define some variables:
<script type="text/javascript"><![CDATA[
var svgDocument = document.getElementById('starfield');
var svgNS = "http://www.w3.org/2000/svg";
var xlinkNS = "http://www.w3.org/1999/xlink";
var angles = [];
var distances = [];
var nStars = 32;
]]></script>
The first three variables are required so we can work with the SVG (which has an id of "starfield"). nStars is the number of stars we'll have on screen at any time, angles and distances will contain the angle and distance of each star from the center of the screen.
Next we can find the center coordinates of the screen and the maximum distance a star can be from the screen before we remove it. I've used a value equal to 40 plus the diagonal length from the center of the screen.
var centerX = svgDocument.getAttributeNS(null, 'width') / 2;
var centerY = svgDocument.getAttributeNS(null, 'height') / 2;
var maxD = Math.sqrt(centerX * centerX + centerY * centerY) + 40;
Creating and drawing stars
Now we need a function to create stars. The createStars function creates n use elements, each of which refers to the star def element and has a different id so we can manipulate them separately. The use elements are all added to the group with the id "star-group". A random angle and random distance is added to the respective arrays. These are used as polar coordinates to position the star elements.
function createStars(n) {
for (var i=1; i<=n; i++) {
var star = document.createElementNS(svgNS, "use");
star.setAttributeNS(null, "id", "star" + i);
star.setAttributeNS(xlinkNS, "href", "#star");
document.getElementById("star-group").appendChild(star);
angles.push(Math.PI * 2 * Math.random());
distances.push(maxD * Math.random());
}
}
The drawField function adds a transform attribute to each star. It uses the angles and distances array to find the position the star should be and translates it to that position. It also scales the star based on its distance from the center of the screen. The scaling isn't quite right, but it's close enough.
function drawField() {
for (var i = 0; i < nStars; i++) {
var star = svgDocument.getElementById('star' + (i + 1));
var d = distances[i];
var x = centerX + d * Math.sin(angles[i]);
var y = centerY + d * Math.cos(angles[i]);
var scale = 2 * (1 - (maxD / (d + maxD)));
star.setAttributeNS(null, 'transform', 'translate(' + x + ',' + y + ') scale(' + scale * scale + ')');
}
}
Animating the stars
Every tick of the animation we increase the distance of the each star from the center of the screen. Again, it's probably not right to increase the distance linearly, but you get the idea. If the distance is so great that the star is off the screen, we reset the distance to 0. We could also pick a new random angle.
function updateImage() {
for (var i = 0; i < nStars; i++) {
distances[i]++;
if (distances[i] > maxD) {
distances[i] = 0;
}
}
drawField();
}
Then we need to repeatedly call this function and for this we can use setInterval, which allows us to call it once every 10 ms.
function beginAnimation() {
var timeout = setInterval(updateImage, 10);
}
Finally, we create the stars and begin the animation.
createStars(nStars);
beginAnimation();
Comments (1)
Sam Hakimi on 2 Feb 2018, 3:46 a.m.
Clean! Thanks