Custom Cursor with JS and GSAP

Pankaj Jaiswal
The Startup
Published in
3 min readFeb 1, 2021

--

Code of the custom cursor can be a little overwhelming at first but trust me it is quite straightforward once you understand what exactly happens behind the scenes.

Mockup

HTML
<div class="cursor"></div>
CSS.cursor {
position: fixed;
top: 0px;
left: 0px;
z-index: 10;
contain: layout style size;
pointer-events: none;
}

.cursor:before{
content: "";
position: absolute;
top: -24px;
left: -24px;
display: block;
width: 48px;
height: 48px;
transform: scale(0.2);
background: black;
border-radius: 50%;
}

The above code is self-explanatory so instead of wasting any time here, we will jump right into JS.

The JS

We will be using GSAP to move the cursor so feel free to use CDN or install the GSAP package via npm, Whichever you prefer. In my case, I had downloaded using npm.

For making things a little simple I’ve divided the whole code into some smaller chunks.

1. Getting the current position of the mouse

// Gets the mouse position

const getMousePos = e => {
return {
x : e.clientX,
y : e.clientY
};
};
// Linear interpolation
const lerp
= (a, b, n) => (1 - n) * a + n * b;

getMousePos is a sort of help function which returns the xPosition and yPositiona of passed element into the function.

lerp is also a help function that accepts 3 parameters (xPoint, yPoint, and amount). It basically returns give a value between xPoint and yPoint which is later used for moving the mouse correctly.

Cursor.js

Make a new JS file name Cursor.js we will right everything related cursor in this file.

Cursor.js//Grab mouse position and set it to mouse state
let
mouse = {x: 0, y: 0};
window.addEventListener('mousemove', (ev) => (mouse = getMousePos(ev)));

In the above code, we basically grabbing the mouse potion with our helper function and setting it to variable.

Cursor.jsexport default class Cursor {
constructor(el) {
this.Cursor = el;
this.Cursor.style.opacity = 0;

this.cursorCnfigs = {
x: {previous: 0, current: 0, amt: 0.2},
y: {previous: 0, current: 0, amt: 0.2}
};
}
}

So let’s start with creating a new Cursor class with a constructor that accepts one argument. This argument will our cursor, In above there is nothing new it just set an argument to this. Cursor and in the next line we hide the Cursor by setting its opacity to 0. this.CursorConfig stores the previous and current position of the mouse and amt defined how smooth the cursor will be. The number can be between 0–1.

Show cursor when mouse moves

this.onMouseMoveEv = () => {
this.cursorCnfigs.x.previous = this.cursorCnfigs.x.current = mouse.x;
this.cursorCnfigs.y.previous = this.cursorCnfigs.y.current = mouse.y;

// Set cursor opacity to 1 when hovered on the screen
gsap.to(this.Cursor, {
duration: 1, ease: 'Power3.easeInOut',
opacity: 1,
})

// requestAnimationFrame
requestAnimationFrame
(() => this.render())

// Cleanup function (Removing after one cycle complete)
window.removeEventListener('mousemove', this.onMouseMoveEv)
}
// Assign the mouse function
window.addEventListener('mousemove', this.onMouseMoveEv)

Continue in constructor function, this.onMoiseMoveEn simply assign mouse movements to our config object, and later we used GSAP to show the cursor. We used requestAnimationFrame for better performance and we calling render function which is responsible for the cursor. Later we remove the eventlistener when one single cycle is completed. Outside this function we adding “mousemove” eventlistener on the window and calling the onMouseMoveEn function as a callback.

Final Piece

render() {
this.cursorCnfigs.x.current = mouse.x;
this.cursorCnfigs.y.current = mouse.y;
for (const key in this.cursorCnfigs) {
this.cursorCnfigs[key].previous = lerp(
this.cursorCnfigs[key].previous,
this.cursorCnfigs[key].current,
this.cursorCnfigs[key].amt
)

}
// setting the cursor x and y to our cursor html element
this
.Cursor.style.transform = `
translateX(${this.cursorCnfigs.x.previous}px)
translateY(${this.cursorCnfigs.y.previous}px)
`;

requestAnimationFrame(() => this.render())

}

In the render function, we again setting the current mouse position to our config object. In the very next line, we looping to each key of obj i.e x,y, and amt. As I mentioned earlier we using lerp function to find a middle value between two states here lerps is return a value between the previous state of the mouse and the current state of the mouse and we assigning the returned value to .previous.

In the next chunk, we using the retuned value by lerp to animate the cursor. We basically giving inline style to our .cursor element.

Link to my git repo for this article: https://github.com/midnightgamer/cursor

Comment down your question I’ll be happy to help and Let me know if you guys need some more cursor animation article.

--

--