
We're going to create a custom cursor follower. I love these kinds of UI elements that add that extra bit of magic to our websites, while potentially allowing us to clean up a bit.
For example, this cursor follower pops up over a swiper section and lets us know we can drag and swipe. This allows us to omit the swiper arrows, indicators etc.
Admittedly, it's not always the case that we do want to omit them, but still, a good opportunity to build a cursor follower!
The HTML 🏗️
First, let's add a container for our cursor:
<div class="cursor">
<div class="cursor_content">
<span>Drag Me!</span>
</div>
</div>
Alternatively, you should be able to add the cursor in the DOM using JS using `createElement`.
The Styling Magic ✨
Now, let's add some CSS that makes our cursor look smooth:
.cursor {
width: 80px;
height: 80px;
border-radius: 50%;
position: fixed;
transform-origin: center;
pointer-events: none !important;
z-index: 100;
transition:
transform 0.4s cubic-bezier(0.195, 1, 0.225, 1),
opacity 1.4s cubic-bezier(0.195, 1, 0.225, 1);
transform: scale(0);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
color: #fff;
background-color: #000;
}
.cursor_content {
transition:
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1),
opacity 1.4s linear;
opacity: 0;
transition-delay: 0.15s;
}
The JavaScript Wizardry 🧙♂️
Here's where the real magic happens. We'll create a function that brings our cursor to life:
function initCursorFollower(lastMouseX = 0, lastMouseY = 0) {
const cursor = document.querySelector('.cursor')
const cursorContent = document.querySelector('.cursor_content')
const hoverSection = document.querySelector('#hero-slider .swiper-wrapper')
const cursorWidth = 80
const cursorHeight = 80
let isHovering = false
const updateCursorPosition = (x, y) => {
const translateX = x - cursorWidth / 2
const translateY = y - cursorHeight / 2
cursor.style.transform = `translate(${translateX}px, ${translateY}px) scale(${isHovering ? 1 : 0})`
}
document.addEventListener('pointermove', function (event) {
lastMouseX = event.clientX
lastMouseY = event.clientY
updateCursorPosition(lastMouseX, lastMouseY)
})
document.addEventListener('scroll', function (event) {
lastMouseX = event.clientX
lastMouseY = event.clientY
updateCursorPosition(lastMouseX, lastMouseY)
})
hoverSection.addEventListener('mouseenter', function () {
isHovering = true
cursor.style.opacity = '1'
cursor.style.transform = `translate(${lastMouseX}px, ${lastMouseY}px) scale(1)`
cursorContent.style.opacity = '1'
cursorContent.style.transform = 'scale(1)'
})
hoverSection.addEventListener('mouseleave', function () {
isHovering = false
cursor.style.transform = `translate(${lastMouseX}px, ${lastMouseY}px) scale(0)`
cursor.style.opacity = '0'
cursorContent.style.opacity = '0'
cursorContent.style.transform = 'scale(0)'
})
}
The main ideas are:
Function update the cursor position: Use the cursor height and width to make sure the follower is placed with the cursor at its center
Event listeners to make sure the follower has the position of the cursor at all times. Besides an event on 'pointermove', we are updating the position on the 'scroll' event as well. We don't want to scroll while the follower is visible and misplace it.
Section specific: Add event listeners on the section we want the follower to appear.
Styling: Animations for cursor content
Pro Tips 💡
Performance Matters: We're using
transform
instead of changingtop
andleft
properties because it's more performant.Smooth Transitions: Those fancy cubic-bezier transitions? They make everything butter-smooth. Play around with the values to find your perfect animation.
Making It Your Own 🎨
Feel free to customize this! You could:
Change the cursor size
Add different hover effects
Modify the transition timing
Add different cursor styles for different sections
Wrapping Up 🎁
And there you have it! A cursor follower that'll make your website feel more interactive and engaging.
Happy coding! 🚀
P.S.: If you're using this with React or Vue, you might want to adjust the event listeners to work with your framework's lifecycle methods.