Cursor Effects and Animation with CSS and JS

Custom cursor effects and animation using css and javascript. Learn how to customize the mouse pointer and their events.

 Yogesh    27 Jul, 2020    8944

In this demo, we'll learn how to create custom cursor effects and animation with simple steps. The post contains 6 demos. 

The beautiful cursor and hover animation will change the look of your web page. In the latest trend, most of the side uses a custom animated cursor to emphasize the components like navigation, buttons, links, cat, etc… 

Let's create the Cursor/Pointer markup. 

Cursor HTML

<div id="cursor"><div id="circle"></div></div>

We've used simple div with cursor id and circle id then we will apply the CSS and JS events to these ids. 

And hide the system cursor

html, body { cursor: none; }

Then apply the CSS to cursor id #cursor

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 .5s;
  z-index: 99;
}

That CSS style may vary according to the different demos.

Next, we will create the Javascript events for cursor move & hover animation.

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) {
  console.log(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. 

We have a common HTML page structure for all demos. 

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 .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 .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 .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 .25s;
}
.card__inner label {
  color: var(--purple);
  opacity: .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 .5s, width .5s, height .5s, clip-path .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 .5s, height .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: .1s;
}
#cursor span:nth-child(2) {
  top: 20px;
  left: -15px;
  animation-delay: .2s;
}
#cursor span:nth-child(3) {
  top: 5px;
  left: 40px;
  animation-delay: .3s;
}
#cursor span:last-child {
  right: -5px;
  bottom: -5px;
  animation-delay: .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 .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, .5);
  transition: all .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');
}

I hope, you will like the demo. Play around it and modify it according to your need.


Credits / Resources

  • Tabler Icons (https://tablericons.com/)
  • Google Font: Baloo 2 (https://fonts.google.com/)



Related Snippets