CSS Text Shadow Mouse Move Effect

이번 포스팅에서는 사용자의 마우스 위치에 따라 텍스트의 그림자가 변경되는 효과를 만들어볼 것이다.

초기 소스코드를 보면 “hero”라는 클래스 명을 가진 div tag 안에 그림자를 생성할 텍스트인 h1 tag가 하나 있다.

그리고 css 코드에서 h1 tag에 text-shadow를 설정해 그림자를 보여준다.

먼저 마우스의 이동을 감지하고 그 위치를 확인하는 작업부터 진행한다.

const hero = document.querySelector('.hero');
const text = hero.querySelector('h1');

function shadow(e) {
let { offsetX: x, offsetY: y } = e;

console.log(`x: ${x}, y: ${y}`);
}

hero.addEventListener('mousemove', shadow);

‘shadow’라는 function을 만들고 hero에 mousemove event가 발생할 때 실행시킨다. 흥미로운 부분은 x와 y의 변수 선언 부분인데,

let x = e.offsetX; 
let y = e.offsetY;

일반적으로 다음과 같이 표현 할 코드를 한 줄로 간편하게 나타내 주었다.

Image for post
Image for post

다음과 같이 마우스의 offsetX, offsetY값이 마우스 이동시 마다 출력된다. 그런데 여기서 문제가 하나 발생한다. mouseover event를 감지하는 요소가 두 가지로 나뉘어진다는 점이다.

하나는 리가 명시적으로 shadow function을 걸어주었던 hero이고, 또 하나는 event delegation에 의해서 발생하는 hero 내부 요소 text이다. 둘 다 shadow function이 동작해야 하는 것은 맞지만 위 콘솔창에서 나왔듯이 마우스가 text내부로 진입할 경우 x, y값이 hero에서 발생할 때와 크게 달라진다.

text에서 마우스가 이동할 때도 hero에서 이동할 때와 연속적으로 이어지는 x, y값을 유지해야 한다.

if (this !== e.target) {
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}

따라서 this !== e.target일 때 즉, hero와 이벤트가 발생한 요소가 같지 않을 때(text)일 때 text의 위치만큼을 더해주어 hero에서의 x, y값과 이어지게 처리해준다.

x값 = text에서의 x값 + text의 왼쪽 끝 지점의 x좌표

마우스의 좌표를 파악했으니 이제 좌표에 따른 그림자의 형태를 정해주어야 한다.

const walk = 500; // 500pxconst xWalk = Math.round((x / width * walk) - (walk / 2));
const yWalk = Math.round((y / height * walk) - (walk / 2));
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7)
`;

그림자는 text를 중심으로 마우스와 text의 거리가 그림자의 위치에 영향을 준다. 또한 text를 원점(0, 0)으로 보았을 때 원점에 대해 대칭인 위치에 마우스가 놓인다면 그림자 역시 원점에 대칭인 곳에 위치하도록 한다.

최종 완성 된 코드에는 그림자를 xWalk, yWalk값에 따라 변하도록 몇 개를 더 추가해 주었다.

<script>
const hero = document.querySelector('.hero');
const text = hero.querySelector('h1');
const walk = 500; // 500px

function shadow(e) {
const {offsetWidth: width, offsetHeight: height} = hero;
let {offsetX: x, offsetY: y} = e;

if (this !== e.target) {
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}

const xWalk = Math.round((x / width * walk) - (walk / 2));
const yWalk = Math.round((y / height * walk) - (walk / 2));
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
}

hero.addEventListener('mousemove', shadow);

</script>
Image for post
Image for post

완성된 모습은 다음과 같다.

Github Source Code

woohyun

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store