Cursor Effects and Animation with CSS and JS
Creative custom cursor effects and animation using css and javascript. Customize the mouse pointer and their behaviour.
In this tutorial, we’ll explore the process of creating custom cursor effects and animations with straightforward steps.
The post contains 6 unique demos.
The beautiful cursor and hover animation will enchance page’s hero unit. As custom animated cursors are gaining popularity, we’ll walk through various designs that emphasize different elements such as navigation, buttons, links, and more.
Let’s begin with Cursor/Pointer markup.
Cursor HTML
<div id="cursor"><div id="circle"></div></div>
We’ve used a simple div with the cursor ID and an internal circle ID. These elements will be manipulated using CSS and JavaScript to achieve the desired cursor effects. Additionally, we’ll hide the default system cursor.
`html,
body {
cursor: none;
}
Next, we’ll apply the common CSS styling to the cursor element:
Common Cursor CSS
#cursor {
position: absolute;
width: 40px;
height: 40px;
top: 50%;
left: 50%;
border-radius: 100%;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
transition: transform 0.5s;
z-index: 99;
}
That CSS style may vary according to the different demos.
Subsequently, we’ll proceed with creating JavaScript events for cursor movement and hover animations:
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
}
// event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("hovered");
} // event: mouse leave on link
function handleMouseLeave() {
cursor.classList.remove("hovered");
}
The page design
Next, we design the page & hover elements.
Now let’s move on to designing the page layout and hover elements for each demo:
HTML Markup
<div id="cursor"><div id="circle"></div></div>
<div class="box">
<ul class="menu">
<li><a href="#" class="a-link">Home</a></li>
<li><a href="#" class="a-link">About</a></li>
<li><a href="#" class="a-link">Services</a></li>
<li><a href="#" class="a-link">Blog</a></li>
<li><a href="#" class="a-link">Contact</a></li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/user.svg" /></a>
</li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/search.svg" /></a>
</li>
</ul>
<div class="clearfix"></div>
<br />
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem, laborum molestiae vero
iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate et.
Error, delectus? Lorem ipsum dolor sit amet consectetur adipisicing elit.
<a class="a-link" href="https://designdrastic.com">Quidem inventore</a>
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem, laborum molestiae vero
iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate et.
Error, delectus?
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem inventore
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem,
<a href="https://designdrastic.com" class="a-link">Laborum molestiae</a>
vero iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate
et. Error, delectus? Lorem ipsum dolor sit amet consectetur adipisicing
elit. Quidem inventore iusto voluptates, aliquid veritatis ut incidunt
labore repudiandae at. Dolorum odit, eveniet a doloremque nulla at
distinctio facere nihil commodi. Lorem ipsum dolor sit amet, consectetur
adipisicing elit. Incidunt alias impedit distinctio nobis consequatur
exercitationem, laborum molestiae vero iure quisquam nesciunt blanditiis
atque qui nostrum veritatis voluptate et. Error, delectus?
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem inventore
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
</p>
<div class="clearfix"></div>
</div>
Common CSS for all Demos
:root {
--dark: #1f1f24;
--purple: #c2c2ff;
}
html,
body {
position: relative;
cursor: none;
height: 100%;
width: 100%;
background: var(--dark);
margin: 0;
}
.box {
padding: 30px;
color: var(--purple);
font-family: "Baloo 2", cursive;
font-size: 1.2rem;
max-width: 900px;
margin: 0 auto;
line-height: 30px;
}
.right {
float: right;
}
.a-link {
color: #fff;
font-weight: 800;
cursor: none;
}
.menu {
list-style: none;
padding: 0;
}
.menu li {
display: inline-block;
}
.menu li a {
display: inline-block;
margin-right: 30px;
text-decoration: none;
}
Design all demos one by one…
Demo 1
Cursor & page HTML
<div id="cursor"><div id="circle"></div></div>
<div class="box">
<ul class="menu">
<li><a href="#" class="a-link">Home</a></li>
<li><a href="#" class="a-link">About</a></li>
<li><a href="#" class="a-link">Services</a></li>
<li><a href="#" class="a-link">Blog</a></li>
<li><a href="#" class="a-link">Contact</a></li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/user.svg" /></a>
</li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/search.svg" /></a>
</li>
</ul>
<div class="clearfix"></div>
<br />
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem, laborum molestiae vero
iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate et.
Error, delectus? Lorem ipsum dolor sit amet consectetur adipisicing elit.
<a class="a-link" href="https://designdrastic.com">Quidem inventore</a>
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem, laborum molestiae vero
iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate et.
Error, delectus?
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem inventore
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt alias
impedit distinctio nobis consequatur exercitationem,
<a href="https://designdrastic.com" class="a-link">Laborum molestiae</a>
vero iure quisquam nesciunt blanditiis atque qui nostrum veritatis voluptate
et. Error, delectus? Lorem ipsum dolor sit amet consectetur adipisicing
elit. Quidem inventore iusto voluptates, aliquid veritatis ut incidunt
labore repudiandae at. Dolorum odit, eveniet a doloremque nulla at
distinctio facere nihil commodi. Lorem ipsum dolor sit amet, consectetur
adipisicing elit. Incidunt alias impedit distinctio nobis consequatur
exercitationem, laborum molestiae vero iure quisquam nesciunt blanditiis
atque qui nostrum veritatis voluptate et. Error, delectus?
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem inventore
iusto voluptates, aliquid veritatis ut incidunt labore repudiandae at.
Dolorum odit, eveniet a doloremque nulla at distinctio facere nihil commodi.
</p>
<div class="clearfix"></div>
</div>
CSS
/* Cursor design */
#cursor {
position: absolute;
width: 40px;
height: 40px;
top: 50%;
left: 50%;
border-radius: 100%;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
transition: transform 0.5s;
z-index: 99;
}
#cursor.hovered {
transform: scale(1.75);
}
#cursor.hovered #circle {
position: absolute;
width: 100%;
height: 100%;
border-radius: 100%;
animation: rotateDot 2s infinite linear;
}
#cursor #circle:after {
position: absolute;
content: "";
width: 4px;
height: 4px;
background-color: var(--dark);
top: 5px;
left: 5px;
opacity: 0;
border-radius: 100%;
}
#cursor.hovered #circle:after {
opacity: 1;
}
@keyframes rotateDot {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
} // event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("hovered");
} // event: mouse leave on link
function handleMouseLeave() {
cursor.classList.remove("hovered");
}
Demo 2
The HTML markup will be same**(Refer demo 1 HTML markup)**
CSS
#cursor {
position: absolute;
width: 60px;
height: 60px;
top: 50%;
left: 50%;
border-radius: 45% 77% 75% 45% / 45% 45% 75% 75%;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
z-index: 9;
transition: transform 0.5s;
}
#cursor.hovered {
transform: scale(1.5);
animation: animateBlob 3s infinite linear;
}
@keyframes animateBlob {
0%,
100% {
border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}
25% {
border-radius: 72% 28% 30% 70% / 30% 28% 72% 70%;
}
50% {
border-radius: 53% 47% 31% 69% / 48% 70% 30% 52%;
}
75% {
border-radius: 42% 58% 68% 32% / 68% 52% 48% 32%;
}
}
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
}
// event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("hovered");
}
// event: mouse leave on link
function handleMouseLeave() {
cursor.classList.remove("hovered");
}
Demo 3
The HTML markup will be same**(Refer demo 1 HTML markup)**
#cursor {
position: absolute;
width: 60px;
height: 60px;
top: 50%;
left: 50%;
clip-path: polygon(20% 0, 85% 10%, 100% 55%, 70% 90%, 10% 90%, 0 40%);
border-radius: 5px;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
z-index: 9;
transition: transform 0.5s;
}
#cursor.hovered {
transform: scale(1.75);
}
#cursor.hovered.shape1 {
clip-path: polygon(0 23%, 100% 14%, 80% 79%, 0 69%);
}
#cursor.hovered.shape2 {
clip-path: polygon(12% 21%, 94% 30%, 100% 70%, 0 80%);
}
#cursor.hovered.shape3 {
clip-path: polygon(0 30%, 100% 34%, 96% 79%, 6% 71%);
}
#cursor.hovered.shape4 {
clip-path: polygon(11% 22%, 100% 34%, 94% 80%, 0 73%);
}
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove); // Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
}
// event: mouse enter on link
function handleMouseEnter(event) {
var _a = this;
var _a_width = _a.offsetWidth;
var classes = ["shape1", "shape2", "shape3", "shape4"];
var shape_class = classes[Math.floor(Math.random() * classes.length)];
$("#cursor")
.css("width", _a_width + "px")
.addClass("hovered " + cls);
cursor.style.width = _a_width + "px";
cursor.classList.add("hovered", shape_class);
}
// event: mouse leave on link
function handleMouseLeave() {
cursor.style.width = "60px";
cursor.classList = "";
}
Demo 4
In deom4, we’ve slightly different HTML page(box) & element structure to represent the arrows.
HTML
<div id="cursor"></div>
<div class="box">
<ul class="menu">
<li><a href="#" class="a-link">Home</a></li>
<li><a href="#" class="a-link">About</a></li>
<li><a href="#" class="a-link">Services</a></li>
<li><a href="#" class="a-link">Blog</a></li>
<li><a href="#" class="a-link">Contact</a></li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/user.svg" /></a>
</li>
<li class="right">
<a href="#" class="a-link"><img src="assets/icons/search.svg" /></a>
</li>
</ul>
<div class="clearfix"></div>
<br />
<div class="card">
<div class="card__inner left" dd-box_hover="left">
<label>Previous</label>
</div>
<div class="card__inner right" dd-box_hover="right">
<label>Next</label>
</div>
</div>
</div>
CSS
.card {
height: 500px;
width: 100%;
}
.card__inner {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
float: left;
width: 50%;
height: 100%;
margin: -1px;
box-sizing: border-box;
border: 2px solid var(--purple);
z-index: 9;
transition: all 0.25s;
}
.card__inner label {
color: var(--purple);
opacity: 0.3;
font-size: 5rem;
font-weight: 900;
z-index: 0;
cursor: none;
display: inline-block;
pointer-events: none;
}
#cursor {
position: absolute;
width: 10px;
height: 10px;
top: 50%;
left: 50%;
border-radius: 5px;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
transition: transform 0.5s, width 0.5s, height 0.5s, clip-path 0.5s;
}
#cursor.scaled {
transform: scale(5);
}
#cursor.hovered {
width: 75px;
height: 75px;
transform: scale(1.5);
}
#cursor.hovered.left-arrow {
clip-path: polygon(
40% 0%,
40% 20%,
100% 20%,
100% 80%,
40% 80%,
40% 100%,
0% 50%
);
}
#cursor.hovered.right-arrow {
clip-path: polygon(
0% 20%,
60% 20%,
60% 0%,
100% 50%,
60% 100%,
60% 80%,
0% 80%
);
}
Javascript
var a_link = document.querySelectorAll(".a-link");
var dd_box = document.querySelectorAll("[dd-box_hover]");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
// Handle box hover
dd_box.forEach((e) => e.addEventListener("mouseenter", handleBoxEnter));
dd_box.forEach((e) => e.addEventListener("mouseleave", handleBoxLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
}
// event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("scaled");
}
// event: mouse leave on link
function handleMouseLeave() {
cursor.classList = "";
}
function handleBoxEnter(event) {
cursor.classList = "";
var box_side = event.target.getAttribute("dd-box_hover");
box_side == "left"
? cursor.classList.add("hovered", "left-arrow")
: cursor.classList.add("hovered", "right-arrow");
}
function handleBoxLeave() {
cursor.classList = "";
}
Demo 5
In demo 5, we have the same HTML page structure. But the Cursor structure will be changed.
Cursor Markup for Demo 5
<div id="cursor"><span></span> <span></span> <span></span> <span></span></div>
CSS
#cursor {
position: absolute;
width: 40px;
height: 40px;
top: 50%;
left: 50%;
border-radius: 25%;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
z-index: 9;
animation: rotateShape 10s infinite linear;
transition: width 0.5s, height 0.5s;
}
@keyframes rotateShape {
0% {
-webkit-transform: rotate(0);
-moz-transform: rotate(0);
-ms-transform: rotate(0);
-o-transform: rotate(0);
transform: rotate(0);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#cursor span {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
background: #fff;
opacity: 0;
}
#cursor.hovered span {
animation: animateBlob 1.5s infinite;
}
#cursor span:first-child {
top: -5px;
left: -5px;
animation-delay: 0.1s;
}
#cursor span:nth-child(2) {
top: 20px;
left: -15px;
animation-delay: 0.2s;
}
#cursor span:nth-child(3) {
top: 5px;
left: 40px;
animation-delay: 0.3s;
}
#cursor span:last-child {
right: -5px;
bottom: -5px;
animation-delay: 0.4s;
}
@keyframes animateBlob {
0% {
opacity: 1;
}
40% {
transform: scale(10);
opacity: 0;
}
100% {
opacity: 0;
}
}
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var top = event.pageY - cursor.clientHeight / 2;
var left = event.pageX - cursor.clientWidth / 2;
cursor.style.top = top + "px";
cursor.style.left = left + "px";
}
// event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("hovered");
}
// event: mouse leave on link
function handleMouseLeave() {
cursor.classList.remove("hovered");
}
Demo 6
In demo 5, we have the same HTML page structure. But the Cursor markup will be changed.
Cursor Markup for Demo 6
<div id="cursor"></div>
<div id="circle"></div>
CSS
#cursor {
position: absolute;
width: 10px;
height: 10px;
top: 50%;
left: 50%;
border-radius: 100%;
background: var(--purple);
pointer-events: none;
mix-blend-mode: difference;
z-index: 9;
transition: transform 0.5s;
}
#cursor.hovered {
background: var(--purple);
transform: scale(6.2);
}
#circle {
position: absolute;
pointer-events: none;
width: 50px;
height: 50px;
top: calc(50% - 25px);
left: calc(50% - 25px);
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.5);
transition: all 0.2s cubic-bezier(0, 0.5, 1, 1);
z-index: 0;
}
Javascript
var a_link = document.querySelectorAll(".a-link");
// Handle mouse over/out event on links
a_link.forEach((e) => e.addEventListener("mouseenter", handleMouseEnter));
a_link.forEach((e) => e.addEventListener("mouseleave", handleMouseLeave));
window.addEventListener("mousemove", handleMouseMove);
// Move the cursor in dom/window
function handleMouseMove(event) {
var cursor_top = event.pageY - cursor.clientHeight / 2;
var cursor_left = event.pageX - cursor.clientWidth / 2;
var circle_top = event.pageY - circle.clientHeight / 2;
var circle_left = event.pageX - circle.clientWidth / 2;
cursor.style.top = cursor_top + 2 + "px";
cursor.style.left = cursor_left + 2 + "px";
circle.style.top = circle_top + "px";
circle.style.left = circle_left + "px";
}
// event: mouse enter on link
function handleMouseEnter() {
cursor.classList.add("hovered");
circle.classList.add("hovered");
}
// event: mouse leave on link
function handleMouseLeave() {
cursor.classList.remove("hovered");
circle.classList.remove("hovered");
}
These demos provide a wide range of custom cursor effects and animations. You can choose and customize the demo that best fits your design preferences and objectives.
Thanks for reading! Feel free to share them with others.