This is the 16th project of Wes Bos's JS30 series. To see the whole 30 part series, click here We'll be creating an effect where the text shadow of an element is controlled by the mouse position!
The video can be found here
The codepen for the starter code
The starter files are minimal, the HTML element who's text shadow well be manipulating is the h1
element below
<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>
We have set the contenteditable
attribute set on the <h1>
, which means the user can edit the text contents of that element!
h1 {
text-shadow: 10px 10px 0 rgba(0.5, 0.5, 0.5, 0.4);
font-size: 50px;
}
In the CSS we just set a simple text shadow to the title. (Iv'e changed the color values a little bit to make it appear shadow like!)
To get the text shadow to follow the cursor
mousemove
event listenerconst hero = document.querySelector('.hero');
const text = hero.querySelector('h1');
function shadow(e) {
}
// tracking the mouse will only be done in the div.hero area
// (in this case it is the whole page though)
hero.addEventListener('mousemove', shadow);
The event object's pageX, pageY
properties give us the X, Y coordinates of the mouse wrt the page. But we need it with respect to the hero element, so we use the offsetX, offsetY
properties. The offsetX property of the MouseEvent provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. So incase we're hovering over h1
and not div.hero
we have to adjust the x, y values.
function shadow(e) {
let { offsetX: x, offsetY: y } = e;
if (this !== e.target) {
//adjusting the values if e.target is not div.hero
// by adding relative position of e.target
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}
}
Let us assume the range of the displacement of the text shadow to be max of 50px from the actual text.
let range = 50 * 2 //50 px * 2
function shadow(e) {
let { offsetX: x, offsetY: y } = e;
if (this !== e.target) {
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}
// height and width of div.hero
const { offsetWidth: width, offsetHeight: height } = hero;
const xRange = Math.round( (x/width * range) - range/2 );
const yRange = Math.round( (y/height * range) - range/2 );
}
What we're doing here is simply mapping the range of width to current 'x' and 'y' to calculate the displacement. For ex, when x=0
the x displacement is -50px
and if x == width
, the x displacement is 50px
.
Basically, get the fraction, multiply to get the value, shift the origin. x/width
give us the fraction, multiplying by range
gives us the value, but we need to subtract range/2
since the displacement is from the center of h1
(origin is here, not top of screen) and going to the left would mean a negative value for x. Same applies for y.
function shadow(e) {
//...
text.style.textShadow = `${xRange}px ${yRange}px 0 rgba(255,0,255,0.7)`;
}
Update the text shadow with xRange, yRange
.
Final codepen -