🛠️ Spinning Pacman: JavaScript
We want Pacman to follow the mouse. To do this, we need an event that triggers whenever the mouse moves. Here, we can use the mousemove
event.
document.addEventListener('mousemove', event => {
// ...
})
We can find the mouse’s position with clientX
and clientY
.
document.addEventListener('mousemove', event => {
const mouseX = event.clientX
const mouseY = event.clientY
console.log(mouseX, mouseY)
})
It’s quite difficult to follow the mouse position via the console. Let’s write these positions into the DOM. You’ll find a <div>
with a debug
class in the HTML
. We’ll display the mouse movement on this .debug
element.
<div class="debug"></div>
const debug = document.querySelector('.debug')
document.addEventListener('mousemove', event => {
// ...
debug.innerHTML = `
<div> mouseX: ${mouseX} </div>
<div> mouseY: ${mouseY} </div>
`
})
Calculating the Angle
We can calculate the angle we need with Trigonometry (via Tangent).
We need to know the opposite
and adjacent
values to calculate the angle.
opposite
: Difference between mouseY
and Pacman’s vertical center
adjacent
: Difference between mouseX
and Pacman’s horizontal center
We can calculate Pacman’s vertical and horizontal center with values from getBoundingClientRect
.
const debug = document.querySelector('.debug')
document.addEventListener('mousemove', event => {
// ...
const pacmanBox = pacman.getBoundingClientRect()
const pacmanXCenter = (pacmanBox.left + pacmanBox.right) / 2
const pacmanYCenter = (pacmanBox.top + pacmanBox.bottom) / 2
debug.innerHTML = `
<div> mouseX: ${mouseX} </div>
<div> mouseY: ${mouseY} </div>
<div> pacmanXCenter: ${pacmanXCenter} </div>
<div> pacmanYCenter: ${pacmanYCenter} </div>
`
})
opposite
is the difference between the mouseY
and pacmanYCenter
. adjacent
is the difference between mouseX
and pacmanXCenter
.
document.addEventListener('mousemove', event => {
// ...
const deltaX = mouseX - pacmanXCenter
const deltaY = mouseY - pacmanYCenter
debug.innerHTML = `
<div> deltaX: ${deltaX} </div>
<div> deltaY: ${deltaY} </div>
`
})
To calculate the angle, we can either use Math.atan
or Math.atan2
. They both give the angle (in radians), but their output is slightly different:
document.addEventListener('mousemove', event => {
// ...
const atan1 = Math.atan(deltaY / deltaX)
const atan2 = Math.atan2(deltaY, deltaX)
debug.innerHTML = `
<div> deltaX: ${deltaX} </div>
<div> deltaY: ${deltaY} </div>
<div> atan1: ${atan1} </div>
<div> atan2: ${atan2} </div>
`
})
I don’t know about you, but I’m much more comfortable with degrees (compared to radians). We can convert radians back to degrees with this formula: radian * 180 / Math.PIÂ
.
(In case you were wondering, I did not remember the formula by heart. I found it on MDN).
document.addEventListener('mousemove', event => {
// ...
const atan1 = Math.atan(deltaY / deltaX) * 180 / Math.PI
const atan2 = Math.atan2(deltaY, deltaX) * 180 / Math.PI
debug.innerHTML = `
<div> deltaX: ${deltaX} </div>
<div> deltaY: ${deltaY} </div>
<div> atan1: ${atan1} </div>
<div> atan2: ${atan2} </div>
`
})
Pay attention to atan1
and atan2
values when you move your mouse around the Pacman. You’ll notice this:
atan1
gives you values between 0deg
and ±90deg
atan2
gives you values between 0deg
and ±180deg
We can use either value to rotate the Pacman.
Rotating the Pacman
Let’s use the angle from atan2
. We can rotate the Pacman by setting a rotate
transform.
document.addEventListener('mousemove', event => {
// ...
const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI
pacman.style.transform = `rotate(${angle}deg)`
})
We positioned five directions (North, North-east, East, South-east, and South) correctly with this code. What’s next is to flip Pacman so it has the correct orientation in the other three directions.
Pay attention to the angle values. You’ll notice we have 135deg
when the Pacman faces South-west.
This means we need to use the fourth formula in our transform chart.
document.addEventListener('mousemove', event => {
// ...
const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI
let transform = `rotate(${angle}deg)`
if (Math.abs(angle) > 90) transform += 'scaleY(-1)'
pacman.style.transform = transform
})
More Pacman!
Once we get a single Pacman to rotate properly, adding more Pacman is a simple feat. Go ahead and add more Pacman SVGs into the HTML. Use 8 for the best results.
<main>
<svg class="pacman"><use href="images/pacman.svg#pacman"></use></svg>
<svg class="pacman"><use href="images/pacman.svg#pacman"></use></svg>
<svg class="pacman"><use href="images/pacman.svg#pacman"></use></svg>
<!-- ...5 more pacman SVG... -->
</main>
In JavaScript, make sure you do two things:
Use querySelectorAll
to select all the SVGs.
Make each Pacman follow the mouse with forEach
.
const pacmen = [...document.querySelectorAll('.pacman')]
document.addEventListener('mousemove', event => {
const mouseX = event.clientX
const mouseY = event.clientY
pacmen.forEach(pacman => {
// ...
})
})
Finally, let’s spice up the page by adding a nice-looking header font! We’ll also remove .debug
since we don’t need it anymore.
<body>
<header>
<h1>
<div data-spinning>Spinning</div>
<div data-pacman>Pacman</div>
</h1>
</header>
<main><!-- ... --></main>
<script src="js/main.js"></script>
</body>
Your browser doesn't support embedded videos. Watch the video here instead.
I played with the Spinning Pacman quite a bit after I made it. Here’s a video if you want to see what I made! (Turn the sound on for the music).