If your browser supports <canvas> (which if it doesn’t you need to switch to a better one 😆 ) you can see a simple JavaScript animation:

Image (with link) Canvas

Drawing Pacman

Drawing Pacman is quite a small task as you would imagine. It simply involves drawing a partial circle with lines going back to the center. Assuming that context refers to the 2D context of the <canvas> (canvas.getContext('2d')) the following code could be used to draw the shape of Pacman’s head:


// An arc which goes from 36 degrees to 324 degrees to draw Pacman's head
context.beginPath();
context.arc(100, 100, 100, 0.2 * Math.PI, 1.8 * Math.PI);

// The line leading back to the center and then closing the path to finish the
// open mouth
context.lineTo(100, 100);
context.closePath();

// Fill pacman's head yellow
context.fillStyle = "#FF0";
context.fill();

Next, since Pacman is cartoony, let’s add an outline to him:


// Outline the head
context.strokeStyle = '#000';
context.stroke();

Now we can add the eye to this popular cyclops :lol::


// A circle for the eye
context.beginPath();
context.arc(100, 50, 10, 0, 2 * Math.PI);
context.fillStyle = "#000";
context.fill();

// Outline the eye
context.strokeStyle = '#FFF';
context.stroke();

You’ll notice that I actually have a clickable <img> element which contains a still version of Pacman drawn when the page was loaded. It was drawn and loaded into the <img> by leveraging the canvas’ toDataURL() function:


img.src = canvas.toDataURL();

Animating Pacman’s Head

Now that we have animated him we need to figure out how we want him to open and close his mouth. This is where a little bit of trigonometry comes into play.

To calculate the x position (horizontal position) of a point on a circle given a specific degree we can use cosine (Math.cos()):


var x = Math.cos(degree * Math.PI / 180);

To calculate the y position (vertical position) of a point on a circle given a specific degree we can use sine (Math.sin()):


var y = Math.sin(degree * Math.PI / 180);

Since we first drew Pacman with his mouth open pretty wide, let’s assume that is as wide as his mouth gets. Let’s also assume that he will close his mouth completely. If we are going to make both the top and bottom of his mouth move in until closing we can assume that they will meet in the middle. Originally we drew his mouth starting at an angle of 36° (0.2 * Math.PI) on the top and 324° (1.8 * Math.PI) on the bottom. This means that the distance that either side will cover to close will only be 36° (or 0.2 * Math.PI). Keeping all of this in mind, here is a function to draw Pacman with his mouth open a specified percent (a number ranging from 0 to 100):


function drawPacman(pctOpen) {
  // Convert percent open to a float
  var fltOpen = pctOpen / 100;

  // Clear the canvas to draw the next part of the animation
  context.clearRect(0, 0, canvas.width, canvas.height);
  
  // An arc which stops at a specific percent to allow for the
  // open mouth to be drawn
  context.beginPath();
  context.arc(100, 100, 100, fltOpen * 0.2 * Math.PI, (2 - fltOpen * 0.2) * Math.PI);

  // The line leading back to the center and then closing the
  // path to finish the open mouth.
  context.lineTo(100, 100);
  context.closePath();

  // Fill pacman's head yellow
  context.fillStyle = "#FF0";
  context.fill();
  
  // Outline the head
  context.strokeStyle = '#000';
  context.stroke();
}

Animating Pacman’s Eye

Making Pacman open and close his mouth is relatively straightforward once we understand how to leverage cosine and sine. The slightly harder task is to get the eye to move accordingly.

Originally we put his eye in an upper horizontally centered position. The upper horizontally centered position of a circle is at 90° (which is 0.5 * Math.PI).

We also established that the distance of the center of the eye from the center of Pacman’s head is 50. We know this because Pacman’s center is at 100,100 and his eye is centered at 100,50 (50 pixels above the center of Pacman’s head).

Keeping all of this in mind we need to leverage cosine and sine to move Pacman’s eye only 36° (0.2 * Math.PI) to the right when his mouth closes:


function drawPacman(pctOpen) {
  // ...code for drawing Pacman's head

  // A circle for the eye
  var angle = Math.PI * (0.3 + fltOpen * 0.2),
      xDelta = 50 * Math.cos(angle),
      yDelta = 50 * Math.sin(angle);
  context.beginPath();
  context.arc(100 + xDelta, 100 - yDelta, 10, 0, 2 * Math.PI);
  context.fillStyle = "#000";
  context.fill();
  
  // Outline the eye
  context.strokeStyle = '#FFF';
  context.stroke();
}

Here we establish the angle at which the eye is positioned based on the percent that the mouth is open. We are also using deltas to offset the eye from the center at the right angle.

Now we can put everything together and use setInterval() to animate Pacman:


<canvas id="pacman_canvas" height="200" width="200"></canvas>
<script type="text/JavaScript">
// Get things rolling once the DOM loads
document.addEventListener('DOMContentLoaded', function() {
  var canvas = document.getElementById('pacman_canvas');
  var context = canvas.getContext('2d');
  
  // dir is the offset that will be added to pctOpen on every interval
  var dir = -10, pctOpen = 100;
  
  function drawPacman(pctOpen) {
    // Convert percent open to a float
    var fltOpen = pctOpen / 100;

    // Clear the canvas to draw the next part of the animation
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    // An arc which stops at a specific percent to allow for the
    // open mouth to be drawn
    context.beginPath();
    context.arc(100, 100, 100, (fltOpen * 0.2) * Math.PI, (2 - fltOpen * 0.2) * Math.PI);

    // The line leading back to the center and then closing the
    // path to finish the open mouth.
    context.lineTo(100, 100);
    context.closePath();

    // Fill pacman's head yellow
    context.fillStyle = "#FF0";
    context.fill();
    
    // Outline the head
    context.strokeStyle = '#000';
    context.stroke();

    // A circle for the eye
    var angle = Math.PI * (0.3 + fltOpen * 0.2),
        xDelta = 50 * Math.cos(angle),
        yDelta = 50 * Math.sin(angle);
    context.beginPath();
    context.arc(100 + xDelta, 100 - yDelta, 10, 0, 2 * Math.PI);
    context.fillStyle = "#000";
    context.fill();
    
    // Outline the eye
    context.strokeStyle = '#FFF';
    context.stroke();
  }
  
  // update pacman every 0.1 seconds
  setInterval(function() {
    drawPacman(pctOpen += dir);

    // when the mouth reaches its limit reverse the direction
    if (pctOpen % 100 == 0) {
      dir = -dir;
    }
  }, 100);
});
</script>

In the end there wasn’t TOO much math involved and we end up with a neat little animation. Have fun! 😎

Categories: BlogGamesJavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *