Nic
This week I learnt...

This week I learnt...

Creating stars using canvas

Canvas is an HTML element that is used to draw graphics. You can do all sorts of cool stuff, but we're going to use it to make stars. It's pretty simple, so it's easy to understand what's going on.

Although this is an HTML element, it just sits there on the page. We need to use JavaScript to do anything with it.

Set up - HTML and CSS

First we need to set up the HTML and CSS, which won't take long.

HTML:

<canvas class="canvas"></canvas>

Here we're just adding the canvas to the page and giving it a class of canvas, so we can refer to it by the class.

CSS:

html, body {
  overflow: hidden;
  background-color: black;
}

We're going to make the stars fill the screen, whatever size that is. That does tend to introduce scrollbars, so overflow: hidden will deal with that. And the black background will simulate a night sky.

Set up - Canvas

Now we can set the canvas up:

const canvas = document.querySelector('.canvas');
const ctx = canvas.getContext("2d");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

Let's go through this. The first line is just creating a variable for the canvas, so we can easily refer to it later.

The second line creates a canvas context. ctx is a pretty standard variable name for this - if you look at other things people have done with canvas you'll likely see a variable called ctx. If you console.log ctx it'll give you all sorts of information about the canvas default properties. It's these properties that we're going to manipulate. We don't do anything with the canvas directly, we do it via the canvas context.

Then we set the canvas to be the same width and height as the window.

Random helper function

Since we're going to want to position the stars randomly, I find a random function to be useful. JavaScript has Math.random(), but it returns a random number between 0 and 1, excluding 1. We need it to return a random number between min and max, including max. There is a way to do this, but rather than writing it out each time, a function makes it easier:

function random(min, max) {
  return min + Math.random() * (max + 1 - min);
}

Add the stars

Now we can add the stars. I'm going to do this in a function, for reasons that will become clear later.

function stars() {
  const canvasSize = canvas.width * canvas.height;
}

We're finding out how many pixels the canvas has. Then we can loop through every pixel to add a star:

  for(let i = 0; i < stars; i++) {
    //Set up random elements
    let xPos = random(2, canvas.width - 2);
    let yPos = random(2, canvas.height - 2);
    let alpha = random(0.5, 1);
    let size = random(1, 2);    
  }

This is where our random function comes in handy. We can use it to pick the x and y position of each star. Since the biggest a star can be is 2px, we'll make sure it's not within 2px of the edge of the canvas. Stars aren't all uniform, so we're picking a random opacity (alpha) between 0.5 and 1 and a random size between 1px and 2px.

And now we've set them up, we can add them to the canvas:

    ctx.fillStyle = '#ffffff';
    ctx.globalAlpha = alpha;
    ctx.fillRect(xPos, yPos, size, size);

These are all properties the canvas context has that you can manipulate. fillStyle tells it what colour you want it to fill up with. In this case, we're going to make all our stars white. You can pick another colour if you want.

globalAlpha is how transparent it is - where 0 is completely transparent and 1 is completely opaque. We're setting it to the random opacity we came up with earlier.

fillRect draws a rectangle. It takes four properties, x position, y position, width, height. Since our stars will be square, width and height are the same thing. Don't worry, they won't look square - 2px is so small that you can't tell the shape that well.

If you call the stars() function and everything's gone right at this point you'll have a white canvas with black bits. What? Why did that happen?

Fixing the stars

What we did was to loop through every pixel and add a star. But actually, we just want a few stars, not fill the space with stars!

To do this we decide how big a fraction of the pixels we want to be stars, then loop through that. The whole stars function now looks like this:

function stars() {
  //Add stars to a small fraction of the canvas
  const canvasSize = canvas.width * canvas.height;
  const starsFraction = canvasSize / 2000;

  for(let i = 0; i < starsFraction; i++) {
    //Set up random elements
    let xPos = random(2, canvas.width - 2);
    let yPos = random(2, canvas.height - 2);
    let alpha = random(0.5, 1);
    let size = random(1, 2);

    //Add stars
    ctx.fillStyle = '#ffffff';
    ctx.globalAlpha = alpha;
    ctx.fillRect(xPos, yPos, size, size);
  }
}

The number you use here is up to you - try changing it to 20000 or 200 and see how the number of stars changes.

And so we're done! Except we're not.

The resizing problem

Try making your browser window smaller, then reload the page. The canvas still fills the screen. Now make your browser bigger and notice how the stars now fill up a space in the top left. That's not great. Imagine someone's loaded up your stars, then maximises their browser window to see it in full glory. They're going to be disappointed.

But it's ok, we can fix that.

The fix

We can add a function that runs every time the window is resized:

window.addEventListener('resize', function() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
});

So now the canvas once again fills the whole window, the stars will too right? Wrong. Resizing the canvas removes the stars. But it's simple to add them again - all we have to do is to re-run the function.

window.addEventListener('resize', function() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  //Re-add the stars
  stars();
});

You might want to add a timer to that, so it doesn't do it instantly, otherwise it'll be constantly redrawing if someone is sizing their browser gradually.

Why not just use CSS for resizing?

You might be wondering why you don't just set the canvas to be width and height 100%, so it will always scale with the browser. The trouble with that is that it scales the stars as well. So if you go from small to big your stars will look stretched. And if you go from big to small your stars will look tiny. It just doesn't look as good.

The whole thing

Here's the whole thing in CodePen.

 
Share this