JS를 통해 CSS에 값 전달하기

JS 코드에서 어떤 값을 정해서 CSS에 전달해서 CSS를 통한 스타일을 정의할 때 전달받은 값을 사용하는 내용입니다.

먼저 JS에서 값을 전달하기 위해서는 특정 DOM에 대한 style에 속성(Property)를 설정해 주면 되는데요. 예를 들어서 –x와 –y라는 속성을 설정해 주는 코드는 다음과 같습니다.

const pos = document.documentElement
pos.addEventListener("mousemove", e => {
    pos.style.setProperty("--x", e.clientX + "px")
    pos.style.setProperty("--y", e.clientY + "px")
})

위의 코드는 document에 해당하는 DOM의 스타일에 대한 속성(–x, –y)를 설정하는 코드입니다. 좀더 자세히 설명드리면 마우스 커서의 위치 값으로 설정하고 있습니다. 반드시 document의 스타일 속성으로 설정해야 합니다.

이렇게 JS에서 설정한 값을 CSS에서 사용하는 방식은 다음과 같습니다.

.banner {
  clip-path: circle(15vh at var(--x) var(--y));
}

핵심은 var이라는 명령을 통해 JS를 통해 설정했던 –x와 –y의 값을 바로 사용할 수 있다는 것입니다.

위의 코드를 활용한 예제는 아래와 같습니다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      font-family: 'Gill Sans';
      box-sizing: border-box;
    }

    body {
      background: #111;
      user-select: none;
    }

    .banner {
      position: fixed;
      width: 100%;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      background: #fff;
      z-index: 1;
      /* clip-path: circle(15vh at center center); */
      clip-path: circle(15vh at var(--x) var(--y));
      transition: all .1s linear;
    }

    .banner h2 {
      /* position: relative; */
      color: transparent;
      -webkit-text-stroke: 4px #333;
      font-size: 25vh;
      pointer-events: none;
    }

    .content {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }

    .content h2 {
      color: transparent;
      -webkit-text-stroke: 4px #333;
      font-size: 25vh;
      pointer-events: none;
    }
  </style>
</head>
<body>
  <section class="banner">
    <h2>GIS DEVELOPER</h2>
  </section>
  <section class="content">
    <h2>GIS DEVELOPER</h2>
  </section>

  <script>
    const pos = document.documentElement
    pos.addEventListener("mousemove", e => {
      pos.style.setProperty("--x", e.clientX + "px")
      pos.style.setProperty("--y", e.clientY + "px")
    })
  </script>
</body>
</html>

실행 결과는 다음과 같습니다.

연기처럼 사라지는 텍스트 효과

먼저 이 글의 내용은 Online Tutorials라는 유튜브 채널에 올라온 내용을 보고 작성한 글입니다. 완전이 같지는 않으나 거의 대부분의 기술적인 내용은 이 채널의 내용을 토대로 합니다. 매우 좋은 채널이므로 웹 개발자라면 참고해 보시면 좋을 것 같습니다.

만들어진 최종 결과는 아래 영상과 같습니다.

위의 결과에 대한 코드를 살펴보면.. 먼저 아래처럼 HTML로 구조를 잡습니다.

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="_style.css">
        <script type="module" src="_app.js" defer>
    </head>
    <body>
        <div>
            <p class=text>산모퉁이를 돌아 논가 외딴 우물을 홀로 찾아가선 가만히 들여다봅니다.우물 속에는 달이 밝고 구름이 흐르고 하늘이 펼치고 파아란 바람이 불고 가을이 있습니다. 그리고 한 사나이가 있습니다. 어쩐지 그 사나이가 미워져 돌아갑니다. 돌아가다 생각하니 그 사나이가 가엾어집니다. 도로 가 들여다보니 사나이는 그대로 있습니다. 다시 그 사나이가 미워져 돌아갑니다. 돌아가다 생각하니 그 사나이가 그리워집니다. 우물 속에는 달이 밝고 구름이 흐르고 하늘이 펼치고 파아란 바람이 불고 가을이 있고 추억처럼 사나이가 있습니다.| 윤동주의 자화상</p>
        </div>
    </body>
</html> 

다음은 CSS의 일부입니다.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

div {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: #000;
}

div .text {
    color: #fff;
    margin: 100px;
    user-select: none;
    font-size: 1.5em;
}

이제 자바스크립트 코드를 작성하는데요. 아래와 같습니다.

const text = document.querySelector("div .text");
text.innerHTML = text.textContent.replace(/\S/g, "$&");

document.querySelectorAll("div .text span").forEach((letter) => {
    letter.addEventListener("mouseover", () => {
        letter.classList.add("active");
    });
});

코드의 컨셉은.. 먼저 글자 하나하나를 span 요소로 만들고, 마우스의 커서가 span 요소에 올라가면 해당 요소에 active 클래스를 지정하는 것이 전부입니다. 이제 span 요소와 active 클래스에 대한 CSS를 살펴보면 다음과 같습니다.

div .text span {
    padding: 2px;
    display: inline-block;
}

div .text span.active {
    animation: smoke 5s linear forwards;
    transform-origin: bottom;
}

@keyframes smoke {
    0% {
        pointer-events: none;
        opacity: 1;
        z-index: -1;
    }
    50% {
        opacity: 1;
    }
    100% {
        opacity: 0;
        visibility: hidden;
        filter: blur(20px);
        transform: translateX(100px) translateY(-300px) rotate(720deg) scale(6);
    }
}