In this article, we are going to make an SVG Eye that will follow the mouse pointer with a clean UI and smooth transition. First, As always let's see what are we building.
PREVIEW
HTML
<img src="face-with-rolling-eyes.png" class="image">
<div class="container">
<svg width="100" height="100" class="eye">
<circle cx="50" cy="50" r="50" fill="white" class="eyeball_left" />
<circle cx="50" cy="50" r="20" fill="#0D0D20" class="pupil_left" />
</svg>
<svg width="100" height="100" class="eye">
<circle cx="50" cy="50" r="50" fill="white" class="eyeball_right" />
<circle cx="50" cy="50" r="20" fill="#0D0D20" class="pupil_right" />
</svg>
</div>
We will have an outer div with class .container
. It will have two separate children SVG which will be the eyes of our character.
Inside SVG we create 2 circles one for eyeball and one for pupil.
The img
tag will be the face of the character
I guess now you have an overview of what are we doing. Now let's get into the CSS.
CSS
body{
margin:0;
padding:0;
background: #282631;
display: flex;
width: 100%;
height:100vh;
}
.container{
margin: auto;
}
.image{
position: absolute;
top: 250px;
left: 620px;
z-index: -1;
}
.pupil_left{
position:relative;
}
.pupil_right{
position:relative;
}
Everything above is self-explanatory but If you have any queries then comment down.
JAVASCRIPT
This is where the fun begins. Let's see from the scratch.
First, we need to find elements with an "eyeball_left
" and "pupil_left
" class
let eyeball_left = document.querySelector(".eyeball_left"),
pupil_left = document.querySelector(".pupil_left"),
Now, We will get the radius of the circles to find the center of the circles. The getBoundingClientRect
returns a DOMRect object with eight properties: left, top, right, bottom, x, y, width, height.
eyeArea_left = eyeball_left.getBoundingClientRect(),
pupil_leftArea = pupil_left.getBoundingClientRect(),
R_left = eyeArea_left.width/2,
r_left = pupil_leftArea.width/2,
centerX_left = eyeArea_left.left + R_left,
centerY_left = eyeArea_left.top + R_left;
Copy the same code for right eye. Just change the variable names to ###_right
for right Eye.
let eyeball_right = document.querySelector(".eyeball_right"),
pupil_right = document.querySelector(".pupil_right"),
eyeArea_right = eyeball_right.getBoundingClientRect(),
pupil_rightArea = pupil_right.getBoundingClientRect(),
R_right = eyeArea_right.width/2,
r_right = pupil_rightArea.width/2,
centerX_right = eyeArea_right.left + R_right,
centerY_right = eyeArea_right.top + R_right;
Now, let us create a mouse event. Through which, we will find the distance between the pointer and center of the eyeball. Math.atan2
will return the angle in radians between the two points. By using the formula, we can convert radian to degree.
Using this angle we will position the pupil inside the eyeball
document.addEventListener("mousemove", (e)=>{
let x_left = e.clientX - centerX_left,
y_left = e.clientY - centerY_left,
theta_left = Math.atan2(y_left,x_left),
angle_left = theta_left*180/Math.PI + 360;
Create a same for the right eye
let x_right = e.clientX - centerX_right,
y_right = e.clientY - centerY_right,
theta_right = Math.atan2(y_right,x_right),
angle_right = theta_right*180/Math.PI + 360;
Finally, we will use JS style property to move and rotate the pupil inside the Eye to follow the cursor
pupil_left.style.transform = `translateX(${R_left - r_left +"px"}) rotate(${angle_left + "deg"})`;
pupil_left.style.transformOrigin = `${r_left +"px"} center`;
pupil_right.style.transform = `translateX(${R_right - r_right +"px"}) rotate(${angle_right + "deg"})`;
pupil_right.style.transformOrigin = `${r_right +"px"} center`;
});
Now we have covered all the aspects of this now let's see the full Javascript code.
<script>
let eyeball_left = document.querySelector(".eyeball_left"),
pupil_left = document.querySelector(".pupil_left"),
eyeArea_left = eyeball_left.getBoundingClientRect(),
pupil_leftArea = pupil_left.getBoundingClientRect(),
R_left = eyeArea_left.width/2,
r_left = pupil_leftArea.width/2,
centerX_left = eyeArea_left.left + R_left,
centerY_left = eyeArea_left.top + R_left;
let eyeball_right = document.querySelector(".eyeball_right"),
pupil_right = document.querySelector(".pupil_right"),
eyeArea_right = eyeball_right.getBoundingClientRect(),
pupil_rightArea = pupil_right.getBoundingClientRect(),
R_right = eyeArea_right.width/2,
r_right = pupil_rightArea.width/2,
centerX_right = eyeArea_right.left + R_right,
centerY_right = eyeArea_right.top + R_right;
document.addEventListener("mousemove", (e)=>{
let x_left = e.clientX - centerX_left,
y_left = e.clientY - centerY_left,
theta_left = Math.atan2(y_left,x_left),
angle_left = theta_left*180/Math.PI + 360;
let x_right = e.clientX - centerX_right,
y_right = e.clientY - centerY_right,
theta_right = Math.atan2(y_right,x_right),
angle_right = theta_right*180/Math.PI + 360;
pupil_left.style.transform = `translateX(${R_left - r_left +"px"}) rotate(${angle_left + "deg"})`;
pupil_left.style.transformOrigin = `${r_left +"px"} center`;
pupil_right.style.transform = `translateX(${R_right - r_right +"px"}) rotate(${angle_right + "deg"})`;
pupil_right.style.transformOrigin = `${r_right +"px"} center`;
});
</script>
The final product will look like:-
You can use the following CSS in body
selector to change the cursor by any image
cursor: url("heart.png"), auto;
Wrapping Up
I hope you enjoyed the article, if yes then don't forget to press β€οΈ. You can also bookmark it for later use. It was fun to make this Project and If you have any queries or suggestions don't hesitate to drop them. See you again.
Top comments (3)
Is better to use CSS custom properties and make all the calculations of position in CSS itself. Take a look at my website where I did just that. On mousemove I just set an
--x
and a--y
, and everything else is done by CSS. The good thing about this approach (besides the performance gains) is that it makes changes way easier. For example after a while I decided I wanted to make the avatar look to the front if the app looses focus (blur event), so I just set--x
and--y
to 0 when that even happens, and tada, everything works βΊοΈWooww. nice site!
Hi, thanks for the suggestion.. This is also a great approach :)