Welcome to Ray's Blog

Stay Hungry Stay Foolish - Steve Jobs

0%

Creating an Interactive Elastic Cursor with React and GSAP

Creating an Interactive Elastic Cursor with React and GSAP

Introduction

In modern UI design, subtle interactions can significantly improve user experience. One such effect is a custom animated cursor, which provides a more dynamic feel to mouse movements. In this blog, we’ll break down an Elastic Cursor component built with React, GSAP (GreenSock Animation Platform), and TypeScript.

This component enhances cursor movement with a jelly-like animation effect, responding to user interactions smoothly. Let’s dive into how it works and the thought process behind it.

How the Elastic Cursor Works

The ElasticCursor component is designed to follow the user’s mouse movements while dynamically adapting its shape and size. Key features include:

  • Smooth elastic movement using GSAP animations.
  • Size and rotation adjustments based on cursor velocity.
  • Hover effects that dynamically resize and reposition the cursor.
  • Mobile detection to disable the effect on small screens.

Breaking Down the Code

1. Setting Up State and References

1
2
3
4
const jellyRef = useRef<HTMLDivElement>(null);
const [isHovering, setIsHovering] = useState(false);
const { x, y } = useMouse();
const isMobile = useMediaQuery('(max-width: 768px)');
  • jellyRef: A reference to the cursor element.
  • isHovering: Tracks whether the cursor is hovering over an interactive element.
  • x, y: Mouse coordinates obtained from a custom hook.
  • isMobile: A media query to disable the effect on smaller screens.

2. Handling Cursor Movement with GSAP

1
2
3
4
5
6
7
8
useLayoutEffect(() => {
set.x = gsap.quickSetter(jellyRef.current, 'x', 'px');
set.y = gsap.quickSetter(jellyRef.current, 'y', 'px');
set.r = gsap.quickSetter(jellyRef.current, 'rotate', 'deg');
set.sx = gsap.quickSetter(jellyRef.current, 'scaleX');
set.sy = gsap.quickSetter(jellyRef.current, 'scaleY');
set.width = gsap.quickSetter(jellyRef.current, 'width', 'px');
}, []);

GSAP’s quickSetter is used to optimize animation updates by directly modifying element properties, improving performance.


3. Animating the Cursor with Elastic Motion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const loop = useCallback(() => {
if (!set.width || !set.sx || !set.sy || !set.r) return;
const rotation = getAngle(vel.x, vel.y);
const scale = getScale(vel.x, vel.y);

if (!isHovering && !isLoading) {
set.x(pos.x);
set.y(pos.y);
set.width(50 + scale * 300);
set.r(rotation);
set.sx(1 + scale);
set.sy(1 - scale * 2);
} else {
set.r(0);
}
}, [isHovering, isLoading]);
  • Calculates rotation and scale based on cursor velocity.
  • Applies transformations dynamically when not hovering.
  • Resets rotation when hovering over interactive elements.

4. Handling Hover Effects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const setFromEvent = (e: MouseEvent) => {
const el = e.target as HTMLElement;
const hoverElemRect = getRekt(el);
if (hoverElemRect) {
const rect = el.getBoundingClientRect();
setIsHovering(true);
gsap.to(jellyRef.current, {
width: el.offsetWidth + 20,
height: el.offsetHeight + 20,
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2,
borderRadius: 10,
duration: 1.5,
ease: 'elastic.out(1, 0.3)',
});
} else {
gsap.to(jellyRef.current, {
borderRadius: 50,
width: CURSOR_DIAMETER,
height: CURSOR_DIAMETER,
});
setIsHovering(false);
}
};
  • Detects if the cursor is hovering over an interactive element.
  • Expands and moves the cursor over the element.
  • Restores default size when no hover is detected.

5. Preventing Animations on Mobile

1
if (isMobile) return null;

If the device width is below 768px, the cursor effect is disabled to maintain usability on touch devices.


6. Rendering the Cursor UI

1
2
3
4
5
6
7
8
9
return (
<>
<div
ref={jellyRef}
className="jelly-blob fixed rounded-lg pointer-events-none"
></div>
<div className="w-3 h-3 rounded-full fixed pointer-events-none"></div>
</>
);
  • The primary div acts as the elastic cursor.
  • The secondary div creates a smooth tracking effect.

Final Thoughts

The ElasticCursor component creates a highly interactive experience that enhances the visual appeal of web applications. By leveraging GSAP animations, event listeners, and React hooks, this component seamlessly reacts to user input, making the UI feel modern and responsive.

Potential Enhancements:

✅ Custom colors & themes 🎨
✅ More interactive animations ✨
✅ Support for click-based interactions 🖱️

Try implementing this in your project and let me know what creative modifications you make! 🚀

Full code
Live View