I've seen a lot of websites using custom cursors for a while now and I was very curious to know how they usually do it. What I discovered is that one of the ways to achieve this effect is using the GSAP library.
Check out the Codepen result with vanilla JS here.
What's the GSAP library?
As they describe themselves, it's a "a wildly robust JavaScript animation library built for professionals".
GSAP allows you to effortlessly animate anything JS can touch. Delivering silky-smooth performance and unmatched support so you can focus on the fun stuff. - GSAP
How to create a cursor follower
Of course, there are other ways to create this effect, but here we'll focus on developing it using GSAP with Vanilla JS and also with React.
Let's start with CSS
For the Vanilla JS and React implementation, we are using the same styles, which you can check below.
.cursor-follower {
position: fixed;
top: 0;
left: 0;
width: 20px;
height: 20px;
border-radius: 100%;
background-color: rgba(144, 70, 254, 0.6);
opacity: 0;
z-index: 10000;
user-select: none;
pointer-events: none;
}
I believe this code is self-explanatory but I also have some additional notes for a better understanding:
z-index
: the high number will make sure the cursor element is over all elementsuser-select
: when set asnone
, it will prevent the element to be selected. A use case scenario is when the user is trying to select some text for example.pointer-events
: this prevents the element from being the target of pointer events when set tonone
. By using this style we guarantee that the element will not cause bugs when the user tries to click on links and buttons.
Implementation using GSAP with Vanilla JS
In order to use GSAP with Vanilla JS we need to import its scripts from the CDN.
Example:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
Check more options to import GSAP from its docs.
Besides that, you need an HTML element containing the class cursor-follower
.
Code:
// Check if it's a touch device
const isTouchDevice = 'ontouchstart' in window;
const createCursorFollower = () => {
const $el = document.querySelector('.cursor-follower');
// Each time the mouse coordinates are updated,
// we need to pass the values to gsap in order
// to animate the element
window.addEventListener('mousemove', (e) => {
const { target, x, y } = e;
// Check if target is inside a link or button
const isTargetLinkOrBtn = target?.closest('a') || target?.closest('button');
// GSAP config
gsap.to($el, {
x: x + 3,
y: y + 3,
duration: 0.7,
ease: 'power4', // More easing options here: https://gsap.com/docs/v3/Eases/
opacity: isTargetLinkOrBtn ? 0.6 : 1,
transform: `scale(${isTargetLinkOrBtn ? 3 : 1})`,
});
});
// Hidding the cursor element when the mouse cursor
// is moved out of the page
document.addEventListener('mouseleave', (e) => {
gsap.to($el, {
duration: 0.7,
opacity: 0,
});
});
};
// Only invoke the function if isn't a touch device
if (!isTouchDevice) {
createCursorFollower();
}
Some explanations for better understanding of this JavaScript snippet code (cursor element is the custom element that will follow the mouse cursor):
isTouchDevice
check if it's a touch device to apply the effect only when it's not touchable- the cursor element reference is stored in a variable to avoid selecting it whenever we need it
- so we have two different event listeners:
mousemove()
will animate the cursor element according to the mouse cursor position (x and y), whilemouseleave()
will do it when the mouse cursor is moved off the page isTargetLinkOrBtn
is where we store the information if the mouse cursor is over some link or button as we use it to make the cursor element a bit different in these cases. Sotarget?.closest('a')
means: if target exists, check if it has ana
as a parent or any ancestors.
- then we use the power of GSAP when we declare
gsap.to($el, {...})
. Basically, it uses theto
method to set the destination values of the cursor element animation. This is how we move the element through the viewport. This method expects the target whose properties will be animated (in this case the cursor element) and an object containing all the properties to be animated.
For target selection, GSAP uses document.querySelectorAll
internally. So you can use ".class", "#id", etc.
ease
property is the way GSAP changes the look and feel of our animations. You can play with it in the Easy visualizer in the GSAP documentation.
Implementation using GSAP with React
In order to use GSAP with React we need to import its Node dependency from NPM.
Check out all options to import GSAP from its docs.
Code:
import { useRef, useEffect } from 'react';
import gsap from 'gsap';
import './styles.css';
export default function CursorFollower() {
// Using useRef() to store the cursor element reference
const cursorRef = useRef(null);
// Check if it's a touch device
const isTouchDevice = 'ontouchstart' in window;
// useEffect() to only execute this code
// when the HTML is ready as well as the element reference
useEffect(() => {
const cursor = cursorRef.current;
// If device is touchable or cursor element
// doesn't exist, stop here
if (isTouchDevice || !cursor) {
return;
}
// Using mousemove() to animate the element cursor
// based on the mouse cursor position (x and y)
window.addEventListener('mousemove', (e) => {
const { target, x, y } = e;
// check if the mouse cursor is over some link or button
const isTargetLinkOrBtn =
target?.closest('a') || target?.closest('button');
// using the GSAP power to animate some properties
gsap.to(cursor, {
x: x + 3,
y: y + 3,
duration: 0.7,
ease: 'power4',
opacity: isTargetLinkOrBtn ? 0.6 : 1,
transform: `scale(${isTargetLinkOrBtn ? 3.5 : 1})`,
});
});
// Using mouseleave() to animate the element cursor
// when the mouse cursor is moved off the page
document.addEventListener('mouseleave', () => {
gsap.to(cursor, {
duration: 0.7,
opacity: 0,
});
});
}, []);
return <S.Cursor ref={cursorRef} />;
}
If you need more explanation about this code, you can check the notes in the section above related to Implementation with Vanilla JS.
Conclusion
I hope you learned how simple it is to implement a custom cursor follower using GSAP animation library with Vanilla JS and also with React. GSAP is a very famous animation library that you can also use to build advanced animations using SVG or even combined with WebGL to create 3D animations.
See you next time! 😁