DEV Community

Cover image for How to animate the moon with the canvas element
Gabrielle Davidson
Gabrielle Davidson

Posted on

How to animate the moon with the canvas element

For Hacktoberfest this year, I contributed to a project using the <canvas> element. I was intrigued, as I'd come across it before while I was learning HTML but always thought "Eh, I'll get to that one day...".

What is the <canvas> element?

It's an HTML element that gives you the ability to draw with JavaScript. Pretty neat. It takes whatever id and dimensions you'd like as attributes, and wraps around a backup image which only displays if your drawing doesn't load:
The canvas element embedded in the body element

How to animate the moon

You don't have to animate the <canvas> element but I thought it'd be a nice challenge. I decided to create a waxing and waning moon animation. My approach was to write a function for each phase and loop through them using setTimeout().

Lay the foundation

Before anything else, every <canvas> element must start with two things:
The code to initialize the canvas element
First, we select the <canvas> element in the HTML with its id and save it in a variable. Second, we create a variable for the context. This is what we actually draw on. Surprise! The <canvas> element itself is really just a container. This context variable is what we will use in our functions.

Initialize

I chose a crescent moon as my starting phase. I drew it with a function called init() and added it as an attribute to the <body> element we saw before, so that it's called when the page loads.
Shows the body element with the "onload" attribute
Shows the "init" function
Crescent moon phase with corresponding code

Repeat with slight variations

I ended up with six very similar functions, so similar that I won't detail each of them here:

  1. init()
  2. quarterMoon()
  3. halfMoon()
  4. fullMoon()
  5. halfMoonWane()
  6. quarterMoonWane()

Each function calls the next one and quarterMoonWane() calls init(). That is how the continuous wax/wane effect is achieved. The only differences are the inner(bezier) and outer(arc) curves of each phase. Really it's only four functions, as quarterMoon() and halfMoon() are basically equivalent to quarterMoonWane() and halfMoonWane(). I repeated them because during the waning phase I needed the same shapes but different setTimeout() function calls.

Challenges and reflections

The <canvas> element is no joke. I spent two days working out how to achieve this animation. Granted, it was my first try and I had to do a lot of research and trial and error with tricky math, but it's still a challenging element to work with. Even though I'm glad I got acquainted with it, I can't really think of a situation where I'd want to use it again.

One of the hardest things about it is that you can't see your progress unless you call a method to connect the points you've established (I used ctx.fill() here, you can use ctx.stroke() to draw a line instead). It was cumbersome doing that after every line and then deleting them all(except the last one) once I knew what was happening. It makes me wonder if there is an easier way.

I also really wanted the transition between each stage to be a little smoother. I tried speeding up the intervals on setTimeout() but that didn't give me the effect I was hoping for. I also experimented with window.requestAnimationFrame(), another method used with <canvas>, but that made it way too fast by itself. I'm sure there is a way to make it work but I wasn't able to find it after much searching and experimenting.

Finally, since there is a lot of repeated code here, I'm sure there is a more elegant way of achieving this type of animation but in the end it gets the job done and I'm a fan!

Here's a resource for more info on the <canvas> element and, as always, here's my code if you'd like to inspect in more detail.

Discussion (2)

Collapse
shubhamkumar10 profile image
Shubham Kumar

This looks awesome @gabriellend Thanks for sharing!!

Collapse
gabriellend profile image
Gabrielle Davidson Author

Thank you for reading! :D