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.


Cursor Effects and Animation with CSS and JS


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.