This tutorial is out of date and no longer maintained.
Last week on the Code Challenge #4, we delved into mouse tracking with JavaScript and simple animations with CSS. The goal of the challenge was to implement a mouse tracking feature on an Alien and have its eye simply track the position and movement of the mouse cursor. We shall proceed to discuss the solution to this challenge.
The base of our challenge consisted of HTML and CSS which constructed the Alien body.
Divs were used to create individual parts of the Alien including the body, ears, mouth, tooth, and eye.
<div class="ufo">
<div class="monster" style="color: #7cb342">
<div class="body">
<div class="ear"></div>
<div class="ear"></div>
<div class="vampi-mouth">
<div class="vampi-tooth"></div>
</div>
</div>
<div class="eye-lid">
<div class="eyes">
<div class="eye"></div>
</div>
</div>
</div>
</div>
CSS was used to style the individual elements. See the codepen demo for the base CSS used.
The Eye which is of particular concern to us was split into three parts - eye-lid, eyes, and eye. We needed to gain control of each part of the eye to control it thereby splitting it into various parts. The eye-lid is the larger white pear-shaped part and is styled with:
.eye-lid {
text-align: center;
display: flex;
font-size: 0.65em;
width: 1em;
height: 1em;
position: absolute;
left: 0.25em;
top: 0.3em;
background-color: white;
border-radius: 0.5em 0.5em 0.5em 0.5em / 0.6em 0.6em 0.4em 0.4em;
box-shadow: 0.03em 0.14em rgba(0,0,0,0.1);
}
To isolate the eye-lid
from the rotating part of the eyes, we created a div partly similar to the eye-lid
which serves as the parent element of the rotating part. We name this, eyes
and it’s styled with:
.eyes{
text-align: center;
display: flex;
font-size: 0.65em;
width: 1em;
height: 1em;
position: absolute;
left: 0.25em;
top: 0.3em;
}
The eyes
element will be controlled by JavaScript to implement the rotating feature.
The black pupil was created with the eye
element whereas the white shadow on the pupil was created using the pseudo-class :after
. These two elements were styled with:
.eye {
position: relative;
display: inline-block;
border-radius: 50%;
width: 75%;
height: 75%;
background-color: black;
border-radius: 50%;
}
.eye:after { /*white shadow*/
--pupil-size: 0.2em;
position: absolute;
top: 0.05em;
left: 0.3em;
width: var(--pupil-size);
height: var(--pupil-size);
background: white;
border-radius: 50%;
content: " ";
}
On implementing the mouse tracking feature, JavaScript was simply used to track the position of the mouse cursor relative to the eyes
div, and the div is rotated in the direction of the cursor using the CSS transform property.
Since the whole page is to be tracked for cursor movement, we assigned the parent ufo
div to a variable called ufo
. An addeventListener()
method was used to track the mousemove
event. Hence, once the mouse is moved over the selected area (ufo
) the event function triggers.
let ufo = document.querySelector('.ufo');
ufo.addEventListener('mousemove', (e) => {
});
Next, the eyes
element is selected from the DOM and assigned to a variable - eye
.
The getBoundingClientRect()
method is used to get the top and left position of the eye
element relative to the viewport. This comes in handy when we would like to determine the relative position of our element with respect to our viewport.
ufo.addEventListener('mousemove', (e) => {
let eye = document.querySelector('.eyes');
let mouseX = (eye.getBoundingClientRect().left);
let mouseY = (eye.getBoundingClientRect().top);
});
To achieve this, we employ the Math.atan2()
function to calculate the arctangent of the mouse position at any given point. The pageX
property returns the horizontal coordinates of the mouse on triggering the mousemove
event and the corresponding pageY
property also returns the vertical coordinates, all relative to the left and top of the listened area - ufo
, repectively. We now have:
let ufo = document.querySelector('.ufo');
ufo.addEventListener('mousemove', (e) => {
let eye = document.querySelector('.eyes');
let mouseX = (eye.getBoundingClientRect().left);
let mouseY = (eye.getBoundingClientRect().top);
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
});
We employ a formula to calculate the angle of the mouse cursor in degrees, relative to the eyes
element. This converts radianDegrees
into a rotation degree with a range from 0 - 360degrees depending on the position of the cursor.
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
The value of rotationDegrees
is the angle of the cursor position relative to the eyes
element. Therefore, by setting the CSS transform property with JavaScript, we simply rotate the eyes
element to the cursor on mousemove using the value of rotationDegrees
. We now have:
let ufo = document.querySelector('.ufo');
ufo.addEventListener('mousemove', (e) => {
let eyes = document.querySelector('.eyes');
let mouseX = (eyes.getBoundingClientRect().left);
let mouseY = (eyes.getBoundingClientRect().top);
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
eyes.style.transform = `rotate(${rotationDegrees}deg)`
});
To achieve this, we used CSS animations. The animation name - blink, solves our challenge. In the eye-lid
class, we include these animation properties and their values:
animation-name: blink;
animation-timing-function: ease-in-out;
animation-direction: forwards;
animation-iteration-count: infinite;
animation-duration: 10s;
The property names clearly explain what each property does. However, we shall use the shorthand animation property. We concatenate the animation properties into:
animation: blink forwards infinite 10s ease-in-out;
This solves the problem.
Here is the final product.
https://codepen.io/Chuloo/pen/RQYbvm
So far I believe we have learned a number of things from this awesome challenge including cursor tracking and simple CSS animations, let’s look forward to the next challenge on Monday. Happy coding!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.