Magnetic Hover Interaction
Magnetic Hover Interaction

Hy Everyone, In this tutorial we will create a menu with magnetic hover interaction. It means we will change our mouse cursor with a red dot and a circle will follow our cursor dot.

Table of Contents

Magnetic Hover Interaction

For creating our magnetic hover interaction menu, we will need three files called index.html, style.css, and script.js. So let’s start with our index.html file.

index.html

<main>
	
	<button>
		<ion-icon name="logo-facebook"></ion-icon>
	</button>
	<button>
		<ion-icon name="logo-twitter"></ion-icon>
	</button>
	<button>
		<ion-icon name="logo-instagram"></ion-icon>
	</button>
	<button>
		<ion-icon name="logo-youtube"></ion-icon>
	</button>
	<button>
		<ion-icon name="logo-dribbble"></ion-icon>
	</button>

</main>


<div class="cursor cursor--large"></div>
<div class="cursor cursor--small"></div>


<div class="support">
	<a href="https://twitter.com/DevLoop01" target="_blank"><i class="fab fa-twitter-square"></i></a>
	<a href="https://dribbble.com/devloop01" target="_blank"><i class="fab fa-dribbble"></i></a>
</div>

style.css

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  background: #121212;
  cursor: none;
}

.cursor {
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
  z-index: 100;
}
.cursor--large {
  --size: 40px;
  border: 1px solid #ff3c3c;
}
.cursor--small {
  --size: 10px;
  background: #ff3c3c;
  transform: translate(-50%, -50%);
}

main {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
main button {
  --size: 60px;
  border: none;
  min-width: var(--size);
  min-height: var(--size);
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1.2rem;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  transition: background 200ms ease, color 200ms ease;
  cursor: none;
}
main:hover button {
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.04);
}
main:hover button:hover {
  color: #fff;
}

.support {
  position: absolute;
  right: 10px;
  bottom: 10px;
  padding: 10px;
  display: flex;
}
.support a {
  margin: 0 10px;
  color: #fff;
  font-size: 1.8rem;
  backface-visibility: hidden;
  transition: all 150ms ease;
}
.support a:hover {
  transform: scale(1.1);
}

script.js

console.clear();

const { gsap } = window;

const cursorOuter = document.querySelector(".cursor--large");
const cursorInner = document.querySelector(".cursor--small");
let isStuck = false;
let mouse = {
	x: -100,
	y: -100,
};

// Just in case you need to scroll
let scrollHeight = 0;
window.addEventListener('scroll', function(e) {
	scrollHeight = window.scrollY
})

let cursorOuterOriginalState = {
	width: cursorOuter.getBoundingClientRect().width,
	height: cursorOuter.getBoundingClientRect().height,
};
const buttons = document.querySelectorAll("main button");

buttons.forEach((button) => {
	button.addEventListener("pointerenter", handleMouseEnter);
	button.addEventListener("pointerleave", handleMouseLeave);
});

document.body.addEventListener("pointermove", updateCursorPosition);
document.body.addEventListener("pointerdown", () => {
	gsap.to(cursorInner, 0.15, {
		scale: 2,
	});
});
document.body.addEventListener("pointerup", () => {
	gsap.to(cursorInner, 0.15, {
		scale: 1,
	});
});

function updateCursorPosition(e) {
	mouse.x = e.pageX;
	mouse.y = e.pageY;
}

function updateCursor() {
	gsap.set(cursorInner, {
		x: mouse.x,
		y: mouse.y,
	});

	if (!isStuck) {
		gsap.to(cursorOuter, {
			duration: 0.15,
			x: mouse.x - cursorOuterOriginalState.width/2,
			y: mouse.y - cursorOuterOriginalState.height/2,
		});
	}

	requestAnimationFrame(updateCursor);
}

updateCursor();

function handleMouseEnter(e) {
	isStuck = true;
	const targetBox = e.currentTarget.getBoundingClientRect();
	gsap.to(cursorOuter, 0.2, {
		x: targetBox.left, 
		y: targetBox.top + scrollHeight,
		width: targetBox.width,
		height: targetBox.width,
		borderRadius: 0,
		backgroundColor: "rgba(255, 255, 255, 0.1)",
	});
}

function handleMouseLeave(e) {
	isStuck = false;
	gsap.to(cursorOuter, 0.2, {
		width: cursorOuterOriginalState.width,
		height: cursorOuterOriginalState.width,
		borderRadius: "50%",
		backgroundColor: "transparent",
	});
}

That’s it.

The source code is available on Codepen.

If you find this post helpful then please share this with your friends.

You Might Like This:

Our Courses:

HTML – Beginner to Advance

CSS – Beginner to Advance

Oh, hi there 👋 It’s nice to meet you.

Sign up to receive awesome content in your inbox, every week.

We don’t spam! Read our privacy policy for more info.

Leave a Reply