On a website I'm creating there is a cursor that needs to change its color smoothly.
When it is on a white background the cursor needs to be the blue #0059ff
(this is important and I will explain why later on) and when it is on blue then the cursor needs to be white; and the transition needs to be smooth like so:
To get the white color with mix-blend-mode
I'm calculating the inverted color using adjust-hue($color, 180)
(in SCSS) and applying this color to the cursor.
When the background color is #0000ff
then cursor should be #ffff00
.
I have started a prototype using mix-blend-mode: difference
that works on "primary colors" (basically colors like #ff0000
, #ff00ff
and so on).
Result:
Problems begin when I try to change the "primary" blue #0000ff
to the one needed by the project #0059ff
. The inverted color is calculated to be #ffa600
and the result is, let's say, "unsatisfactory" because I want the cursor to be white on some background color and said color on white background.
Calculating the difference will not work with this color and I have no idea how to make it so that when the cursor is not over the white background then the cursor becomes blue (-ish) and when it's over the blue background it becomes white.
My whole code so far:
(SCSS compiled so it can run in StackSnippet)
const bigBall = document.querySelector('.cursor-ball-big');
const smallBall = document.querySelector('.cursor-ball-small');
const allHoverable = document.querySelectorAll('a, .hoverable');
TweenMax.to(bigBall, .3, {fill: 'none'});
allHoverable.forEach(hoverable => {
hoverable.addEventListener('mouseenter', () => {
TweenMax.to(bigBall, .3, {scale: 4});
TweenMax.to(bigBall.querySelector('circle'), .3, {strokeWidth: 1});
});
hoverable.addEventListener('mouseleave', () => {
TweenMax.to(bigBall, .3, {scale: 1});
TweenMax.to(bigBall.querySelector('circle'), .3, {strokeWidth: 2});
});
});
document.body.addEventListener('mousemove', e => {
const {clientX, clientY} = e;
TweenMax.to(smallBall, .1, {x: clientX - 5, y: clientY - 7});
TweenMax.to(bigBall, .4, {x: clientX - 15, y: clientY - 17});
});
:root {
--color1: #0059FF;
--color2: #FFFFFF;
--cursor: #ffa600;
}
body {
height: 100vh;
cursor: none;
margin: 0;
display: flex;
font-family: monospace;
}
.cursor {
pointer-events: none;
mix-blend-mode: difference;
}
.cursor .cursor-ball {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
}
.cursor .cursor-ball.cursor-ball-small circle {
fill: var(--cursor);
}
.cursor .cursor-ball.cursor-ball-big circle {
stroke: var(--cursor);
}
a {
border-bottom: 2px solid transparent;
padding: 10px 0;
margin-top: 25px;
text-decoration: none;
display: inline-block;
cursor: none;
}
.left,
.right {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.left {
background-color: var(--color1);
}
.left a {
border-color: var(--color2);
}
.left h1,
.left p,
.left a {
color: var(--color2);
}
.right {
background-color: var(--color2);
}
.right a {
border-color: var(--color1);
}
.right h1,
.right p,
.right a {
color: var(--color1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div class="cursor">
<div class="cursor-ball cursor-ball-big">
<svg xmlns="http://www.w3.org/2000/svg" height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="2"/>
</svg>
</div>
<div class="cursor-ball cursor-ball-small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"/>
</svg>
</div>
</div>
<div class="left">
<a href="#" title="Hover me">Hover me</a>
</div>
<div class="right">
<a href="#" title="Hover me">Hover me</a>
</div>