Tilting Cursor effect with CSS and JS
Anneta Wamono
Posted on June 27, 2023
Welcome to my Frontend Studies - a series where I try to recreate and give general notes on a UI element that inspires me. I hope beginner and intermediate learners can take something away from this, and as well as help me improve in return.
My inspiration for this article is Studio Brot's website. I like it a lot because it is a subtle animation effect that results in a pretty fun mouse interaction.
I broke down the element into the following parts:
- Replace cursor with a custom cursor
- Find your range of rotation
- Rotate the cursor along the x-axis
The HTML
<div class="cursor">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 550.58 667.15"><path class="d" d="M0,50.09C0,8.48,47.86-14.92,80.7,10.63l450.48,350.41c37.58,29.23,16.91,89.46-30.7,89.46h-228.41c-16.82,0-32.71,7.7-43.14,20.89l-139.7,176.67c-29.4,37.19-89.22,16.4-89.22-31.01V50.09ZM500.48,400.5L50,50.09V617.05l139.7-176.68c19.91-25.18,50.26-39.87,82.36-39.87h228.41Z" fill="currentColor"/></svg>
</div>
Replace cursor with a custom cursor
- Get an SVG cursor. Remember to size it appropriately. My cursor started out much larger than I wanted, so I used CSS to scale it down.
.cursor svg {
width: 100px;
}
- Make your SVG cursor follow your mouse pointer using event listener
mousemove
. The event parameter gives you access to theclientX
andclientY
properties which represent the x and y position of your pointer. I'm also using CSS translate property to update the position of the SVG cursor, which can be passed through in JavaScript.
const cursor = document.querySelector(".cursor");
document.addEventListener("mousemove", function(e) {
let clientX = e.clientX;
let clientY = e.clientY;
let vw = window.innerWidth;
cursor.style.transform = `translate(calc(${clientX}px), calc(${clientY}px)`;
});
Find your range of rotation
- You'll need to make sure that the tip of the SVG cursor matches the tip of your mouse pointer so that clicking with the SVG cursor feels accurate. CSS has a
transform-origin
property that defines the point all transforms are based on. By default it is in the center of the element, but you can adjust it.
.cursor svg {
/* block elements have a width and height value needed for transformations */
display: block;
transform-origin: top left;
}
- Find a range of rotation that suits your new cursor. I chose
52deg
.
Rotate the cursor along the x-axis
- Find viewport width with
window.innerWidth
. - Take
clientX
value of your cursor and the viewport width and normalize them to map to your range of rotation. Normalization is taking any range of values and mapping it to the range of 0 to 1. The result is that when you input a value within your initial range, you get a corresponding output value between 0 and 1. So in our code, our initial range is 0 to our viewport width. We pass the x position into our normalize function so that it returns a number between 0 and 1. We can then take that number and multiply it with our range of rotation52deg
so that we can get the correct degree of rotation at our mouse's x position. - Pass that degree to our SVG cursor as a CSS property.
const cursor = document.querySelector(".cursor");
document.addEventListener("mousemove", function(e) {
let clientX = e.clientX;
let clientY = e.clientY;
let vw = window.innerWidth;
cursor.style.transform = `translate(calc(${clientX}px), calc(${clientY}px)`;
let rotation = Math.round(52 * normalize(clientX, vw));
cursor.style.setProperty("--cursor-rotate", `${rotation}deg`);
});
function normalize(curr, max) {
return curr / max;
}
Takeaways
When I first saw this effect my gut reaction was "I totally know how to do that". I had enough knowledge to already piece together how I was going to achieve this without needing to do a ton of research. I think this signaled to me that my skillset was improving.
Thank you for reading my article. If there is anything you would like more details on, leave a comment below. I'd like to continue improving the quality of my articles, so any feedback is appreciated.
See you at the next one!
Posted on June 27, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.