DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป is a community of 966,155 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Mouse tracking eye using Vue 3, VueUse and CSS
Taliesin
Taliesin

Posted on • Originally published at pixelhop.io

Mouse tracking eye using Vue 3, VueUse and CSS

Introduction

In this blog, I will be showing you have to make a mouse-tracking eye component using Vue 3, VueUse and a sprinkle of CSS. This eye-catching component will make a quirky addition to your future projects.

Firstly let's break the eyes, my name is Taliesin, and I work at Pixelhop. I made this for our Halloween special project we at Pixelhop made called trick-or-treat.

If you would like to get your eyes on the whole code example, please find it here.

Readers are recommended to have a basic understanding of Vue 3 using the composition API, also not recommended for anyone with Ommetaphobia or if you have anything against terrible eye puns. So if we see eye to eye, let's crack on.

Summary

So, to summer-eyes, for this to work, we will need to have an SVG of an eye with the pupil to one side. We are then just going to set the transform rotate property to point the pupil in the direction of the mouse as it moves.

Project setup

If you already have a project and an eye SVG ready, you can roll your eyes on to the next section. But if you're like the alien called Alen and missing an eye, here I will just be setting up a basic Vue 3 project and setting the foundation of what we are making.
So first, we want to set up a basic Vue 3 project. The easiest way to do this is using npx and the Vue CLI by running the following command.

npx @vue/cli create mouse-tracking-eye
Enter fullscreen mode Exit fullscreen mode

Select the default vue 3 preset

Selecting the default vue 3 preset

Choose your dependancies manager (I'm using npm)

Selecting npm

cd into the folder and install the dependancies

cd mouse-tracking-eye/ && npm i
Enter fullscreen mode Exit fullscreen mode

We can now remove all the bits the vue cli gives us that we don't need. First, remove the components and assets folders. Then in the App.vue, we should remove all the base stuff it gives us. You are just left with the template, script and style tag.

I am using the <script setup> syntax, which you can read about here and typescript because why not.

<!-- App.js -->
<template>
</template>

<script lang="ts" setup>
</script>

<style>
</style>
Enter fullscreen mode Exit fullscreen mode

Now we need to eye up the template, I'm using the SVG I used for our Halloween project. Add a div and give it a class of container, then paste the SVG inside the container div.

<!-- App.ts > temaplate -->
<div class="container">
  <svg
    viewBox="0 0 33 33"
    fill="none"
  >
      ...
  </svg>
</div>
Enter fullscreen mode Exit fullscreen mode

Centre the eye and add a black background, so the eye stands out with a bit of CSS.

/* App.js > style */

.conatainer {
  background-color: black;
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
}
.eye {
  height: 3rem;
}
body {
  margin: 0;
  height: 100vh;
}
Enter fullscreen mode Exit fullscreen mode

Spec-tacular! Now, if you run your project, you should have an eye in the middle of your screen and a black background.

Mouse tracking functionality

This section will focus on getting the eye to follow the mouse.

As previously mentioned, we will be using the vueuse library. Vueuse is a super helpful function library for Vue 3 with a few functions that will simplify this. So let's install it:

npm i @vueuse/core
Enter fullscreen mode Exit fullscreen mode

Import the functions we need into our app and we might as well import the function we need from Vue as well.

<!-- App.vue > script -->
import {
  useMouse, useWindowSize, debouncedWatch, throttledWatch,
} from '@vueuse/core';
import {
  ref, onMounted,
} from 'vue';
Enter fullscreen mode Exit fullscreen mode

Eye eye, now we got those imported, we can start using them. The first two we will use are useMouse and useWindowSize.
useMouse returns the x and y of the mouse position, and useWindowSize returns... You guessed it, the window size width and height.
So just under the import, add the following:

// App.vue > script
const { x: mouseX, y: mouseY } = useMouse();
const { width, height } = useWindowSize();
Enter fullscreen mode Exit fullscreen mode

Next we need to get the eye location on the screen, to do this add a ref to the SVG in the template. So it will be something like this.

<!-- App.vue > template -->
<svg
  ref="eye"
  viewBox="0 0 33 33"
  fill="none"
>
...
</svg>
Enter fullscreen mode Exit fullscreen mode

and now we can reference it in the script, we just need to add a ref variable with null as its starting value.

// App.vue > script
const eye = ref(null as Element | null);
Enter fullscreen mode Exit fullscreen mode

Side note: If you are using a setup function inside a regular script tag, make sure you add the ref to the return object, or it will not work.

Now define the eye location reference

// App.vue > script
const eyeLocation = ref<DOMRect | undefined>(undefined);
Enter fullscreen mode Exit fullscreen mode

Here I am setting the eye location inside the onMounted function we import from vue earlier.

// App.vue > script
onMounted(() => {
  eyeLocation.value = eye?.value?.getBoundingClientRect();
}); 
Enter fullscreen mode Exit fullscreen mode

We also want to set the eye's location when the screen size is changed because depending on where it is this might move the eye. To achieve this we can use the debounceWatch
To summarise what happening here; we are watching for a change in the window height and width and for a change and running a debounce function when it does.

// App.vue > script
debouncedWatch([width, height], () => {
  eyeLocation.value = eye?.value?.getBoundingClientRect();
}, { debounce: 200 });
Enter fullscreen mode Exit fullscreen mode

Great, now we've now we've got an eye on its location, let's get the ball rolling and actually make the eye move.

// App.vue > script
const rotationDegrees = ref(0);

throttledWatch([mouseX, mouseY], ([x, y]) => {
  if (eyeLocation.value) {
    const radianDegrees = Math.atan2(x - eyeLocation.value.right, y - eyeLocation.value.top);
    rotationDegrees.value = radianDegrees * (180 / Math.PI) * -1 + 180;
  }
}, { throttle: 1000 / 60 });
Enter fullscreen mode Exit fullscreen mode

So if you got an eye for this sort of thing, then you'll be able to understand it, but if you're like a blind Bambi and have no-eye-deer. Don't worry; I'll give a quick summary of what's happening;

Firstly, we define the ref rotationDegrees which will be the number of degrees we need to rotate our eye. Next, we are using the throttledWatch function to watch the mouse location, then set the rotationDegrees.

First, it gets the radianDegrees using the Math.atan2 function; read more here. Basically, it receives the radian between the eye location and the mouse. I am using the top and right locations of the eye, but depending on where your eye's pupil is pointing, you may need to use a different location. Then we convert the radian into degrees we can use to rotate the eye.

This function is then throttled to 60 times per second as we don't need to run this more than that as most screens only run a 60 hertz anyway.

Now all we need to do is set the transform rotate property to the SVG and you'll really be turning some eyes.

<!-- App.vue > temaplate -->
<svg
  ref="eye"
  :style="`transform: rotate(${rotationDegrees - 40}deg) translateZ(0)`"
  viewBox="0 0 33 33"
  fill="none"
>
...
</svg>
Enter fullscreen mode Exit fullscreen mode

Side note: You may need to do what I did by adding or subtracting a few degrees if your eye is not pointing precisely left or right.

Conclusion

Because we are getting the eye location on mounted and screen size change, you can place your eye anywhere on the screen, and it will still work.

I hope you enjoyed this mini tutorial, and it helps you create some fun projects. Feel free to send us your creations. I would love to eye them up. You can find our contact details at https://www.pixelhop.io/contact/.

If you are interested to see the original eye, I made and our Halloween special project, check it out here: https://trick-or-treat.pixelhop.io/.

Please keep your eyes peeled for our new blogs at https://www.pixelhop.io/writing/ or sign up for our newsletter.

See you later ๐Ÿ‘‹

Top comments (2)

Collapse
 
jozefmaxted profile image
Jozef Maxted ๐Ÿ‘ป

The number of eye puns you managed to fit in here are eye watering! ๐Ÿคฃ

Collapse
 
lilgemvincent profile image
Gemma Vincent

This is awesome, really eye catching ๐Ÿ‘

๐Ÿคฏ

"I made 10x faster JSON.stringify() functions, even type safe"

โ˜๏ธ Must read for JS devs