DEV Community

Cover image for 🍦 Vanilla JS Starry Night
Yossi Abramov
Yossi Abramov

Posted on • Originally published at yossiabramov.com

🍦 Vanilla JS Starry Night

In this post I would like to share with you a Vanilla JS demo of twinkling stars on a smooth black canvas. Basically, this kind of challenge involves dealing with random parameters like a star’s width, height, coordinates and twinkling duration. I think it is probably better to start with the relevant portion of the CSS for this small demo and work our way to the JS part.

Here are some links for this demo:

👉 GitHub repo: https://github.com/yossi-abramov/starry-night

👉 GitHub page: https://yossi-abramov.github.io/starry-night/

CSS

.star {
  --twinkle-duration: "";
  --twinkle-delay: "";
  --star-size: "";
  position: absolute;
  width: var(--star-size);
  height: var(--star-size);
  background: #fff;
  border-radius: 50%;
  animation: twinkle infinite alternate;
  animation-duration: var(--twinkle-duration);
  animation-delay: var(--twinkle-delay);
}

@keyframes twinkle {
    from {
        transform: scale(1);
    }

    to {
        transform: scale(1.5);
        box-shadow: 0 0 5px 0.5px rgba(150, 150, 150, 0.6);
    }
}
Enter fullscreen mode Exit fullscreen mode

I’ve created a .star class for my stars and initialised CSS variables in it. By scoping these variables to the .star class we can make these variables behave like “arguments”. That means we can set a value to our scoped CSS property on a given star element (Just think of all the cool things you can build with CSS variables!). Next, I’ve set a keyframe animation for the “twinkling” effect. As you can see, both animation-duration and animation-delay have a CSS variable assigned to them. As for the twinkle keyframe, it’s pretty straight forward: alternate infinitely between a scale of 1 and 1.5 of the star’s size and add a box-shadow for a soft glow effect.

JS

Now, let’s go over the JS portion of the code. As mentioned above, we need to deal with some random “star” properties. For that reason the first thing we need is a function that will generate a random number between a min and a max. For that purpose, we can use the mighty JS Math Object:

const genRandomNumber = (min, max) => {
    return Math.random() * (max - min) + min;
}
Enter fullscreen mode Exit fullscreen mode

After setting up our genRandomNumber function we can move on to defining the boundaries of our microcosmos/canvas/night sky. Remember, we need to spread our stars randomly across a space. So first we need to select our parent and give our stars coordinates relative to the selected parent. In this case, I’ve selected the body element:

const $el = document.body;
Enter fullscreen mode Exit fullscreen mode

Now, all we need to do is create an element, append a .star class to it and pass the required random CSS properties that we defined in our CSS. After our star element is created, we will simply append it to the parent element - body in this case. We will need to repeat this process x amount of times – so a simple for loop will do!

for (let index = 0; index < 1000; index++) {
    const star = document.createElement("div");
    star.classList.add("star");

    // Star coordinates
    let x = genRandomNumber(1, $el.offsetWidth);
    let y = genRandomNumber(1, $el.offsetHeight);

    star.style.left = Math.floor(x) + "px";
    star.style.top = Math.floor(y) + "px";

    star.style.setProperty(
        "--star-size",
        genRandomNumber(1, 3) + "px"
    );

    star.style.setProperty(
        "--twinkle-duration",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    star.style.setProperty(
        "--twinkle-delay",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    $el.append(star);
}
Enter fullscreen mode Exit fullscreen mode

Let's refactor this code a bit, here is another suggestion:

// Night Sky element

const $el = document.body;

// Generate a random number between min and max values

const genRandomNumber = (min, max) => {
    return Math.random() * (max - min) + min;
}

// Generate a star <div>

const genStar = () => {
    const star = document.createElement("div");
    star.classList.add("star");

    // Gen star coordinates relative to $el size
    let x = genRandomNumber(1, $el.offsetWidth);
    let y = genRandomNumber(1, $el.offsetHeight);

    const { style } = star;

    style.left = Math.floor(x) + "px";
    style.top = Math.floor(y) + "px";

    style.setProperty(
        "--star-size",
        genRandomNumber(1, 3) + "px"
    );

    style.setProperty(
        "--twinkle-duration",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    style.setProperty(
        "--twinkle-delay",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    return star;
}

for (let index = 0; index < 800; index++) {
    $el.append(genStar());
}
Enter fullscreen mode Exit fullscreen mode

✍ For more posts:
https://yossiabramov.com/

Top comments (2)

Collapse
 
casiimir profile image
casiimir • Edited

Nice done!
(only a little advice, try to do it with canvas, it reduces very well performance requests :D, Merry Xmas 🎁 )

Collapse
 
yossiabramov profile image
Yossi Abramov

Thanks for the advice - will try that👌