How to create a custom animated cursor without using canvas
A quick and easy tutorial
The custom cursor
If you scroll through the Awwwards website, you’ll see them very often. Animated cursors. Sometimes its a cirkel beneath your cursor, sometimes it completely replaces your cursor. Some cursors animate on click, some animate on hover. When thought out right, it can add a lot to your brand. But… How do you build one?
There are a few ways how you can build animated cursors. By using a canvas element, or just by using div’s. To keep this short tutorial as easy as possible, we’ll be using div’s.
Let's get started
First we'll be adding one or multiple div elements (in the example below I added one div. It’s called “.cursorWrapper”). In CSS you’ll give this element a fixed position, a top and a left with the value “0” and a transform that takes two css-variables. That will later be the “x” and a “y” coordinates of your mouse.
Your CSS should now look like this.
1:root {2 --posX: 0px3 --posY: 0px4}56.cursorWrapper {7 /* We're using translate3d for optimal performance */8 transform: translate3d(var(--posX), var(--posY), 0);9 position: fixed;10 top: 0;11 left: 0;12 pointer-events: none;13}
Now you have your base. You still don’t see anything. The actual visuals, we put in a ::before
pseudo-element. We’re doing this, because if we want to animate the scale asynchronously from the position we’d rather have them in separate selector.
Now we want to create the cursor itself. Let’s create a before pseudo-element. This element wil be the visible cursor. Don’t forget that ::before
needs a “content” property and “position: absolute”.
In terms of styling you can do what you want. Make sure you add a width and height. And also disable pointer-events in your CSS. We do this, so you’re able to click through the custom cursor. We add this code to our CSS file:
1.cursorWrapper::before {2 position: absolute;3 width: 50px;4 height: 50px;5 pointer-events: none;67 background-color: purple;8 border-radius: 25px;9}
Add any other styling you want. You can for instance use clip-path to make your cursor get different shapes.
Adding the mouse movement
So right now, we have all the styling of the cursor, but it still doesn’t work. So we’ll be adding a layer of JavaScript right now. We’ll start bij adding a constant where we store a reference to the root.
Then we’ll be adding the event listener. We’ll be listening to the mouse mouse on the body. Once we’ve done that, we’ll be adding a function. In my case it’s called “applyCursorPos”. We need the clientX and the clientY of the event. Since this function will be called directly from the event listener, the event parameter is passed automagicly. We can destructure that. In the parameter of the “applyCursorPos” and use it in the function.
Since the size of our cursor is 50px, we need to subtract 25px of our cursors position, so the middle of our custom cursor is actually the middel. You can do this in JavaScript but you can also do this in CSS by using a calc function.
By using the setProperty function on the style of root, we’ll be able to change the value of the css variable.
1const root = document.querySelector(':root')23const applyCursorPos = ({clientX, clientY}) => {4 const x = `${clientX - 25}px`5 const y = `${clientY - 25}px`67 root.style.setProperty("--posX", x);8 root.style.setProperty("--posY", y);9}1011document.body.addEventListener(“mousemove", applyCursorPos)
If done correctly, your custom cursor should now follow your mouse move. You can now decide if you want your cursor to lay above all the elements and use mix-blend-mode to change the colors of the elements beneath it, or you can change the z-index so the custom cursor appears beneath the elements. Take in account that your cursor will also appear beneath images, which can be annoying. A quick fix, is to give all the images an even lower z-index than the custom cursor.
There’s a lot more you can do with the custom cursor, for instance, changing the color or size when hovering over certain elements. I’ve done this with buttons, anchors and inputs.
1cursorHoverState = (e, {hovering}) => {2 /* we’re saving the width, height, left, top and such of the element we’re3 hovering over in this ‘rect’ constant. */4 const rect = e.target.getBoundingClientRect();5 // Now we’re setting the mouse position to the middle of the button6 root.style.setProperty("--posX", rect.x + (rect.width / 2) - 25 + "px");7 root.style.setProperty("--posY", rect.y + (rect.height / 2) - 25 + “px");8 // And changing the background-color of the custom cursor9 root.style.setProperty("--color", hovering ? "orange" : color);10}1112document.querySelectorAll("button, a, input, select").forEach(btn => {13 btn.addEventListener("mouseover", (e) => {cursorHoverState(e, {hovering: true})})14 btn.addEventListener("mouseout", (e) => {cursorHoverState(e, {hovering: false})})15});
In the cursorHoverState you can do all kinds of things, you can change the clip-path of your custom cursor, and such.
Upgrading
There’s much more you can do. I’ve added even more cirkels to my custom cursor and gave them all a different size, opacity and transition delay (on mouse move). Please leave a comment about what you think of the custom cursor and a codepen of your own, so I can check it out.
Other posts you might like
How to develop fast web animations by using FLIP and RAIL techniques
September 17, 2021Renders Let's say a <div> element needs to be rendered on a web page. Then the browser first looks…
How to develop a Web AR Facefilter with React and ThreeJS / React Three Fiber in 2021
September 09, 2021For the 3 year anniversary of 🧙🏼♂️ Level30Wizards we wanted to build something that's related…
Accessible Wavy Text Animation using React Hooks and Framer Motion
February 17, 2021In this article i'll recreate a wavy text animation we built with React Hooks and GSAP v3 but…