스크롤에 따라 움직이는 가로 배너 만들기

ChoiYongHyeun·2024년 1월 14일
0

망가뜨린 장난감들

목록 보기
5/19
post-thumbnail

프로젝트 출처

자바스크립트로 5분만에 텍스트 스크롤 애니메이션 효과(text marquee effect) 마스터하기

해당 영상을 보고 내 스타일대로 구현해보려고 따라해봤다.

유튜브 제작자의 레퍼런스는 다음 사이트이다.

https://wildcatterla.com

배너뿐이 아니라 다양한 기능이 있는 이쁜 웹사이트이다.

HTML / CSS

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="container">
      <p class="first-banner"></p>
      <p class="second-banner"></p>
    </div>
  </body>
  <script src="script.js"></script>
</html>
@import url('https://fonts.googleapis.com/css2?family=Pacifico&display=swap');

html,
body {
  font-family: 'Pacifico', cursive;
  margin: 0 auto;
  background-color: #aaa;
}

p {
  padding: 1rem 0;
  margin: 0;
  font-size: 2vw;
  position: absolute;
  white-space: nowrap; /* 줄바꿈 방지 */
}

.container {
  margin: 0 auto;
  width: 80vw;
  height: 300vh;
  margin-bottom: 10rem;
  background-color: black;
  position: relative;
  overflow-x: hidden; 
}

.first-banner {
  top: 50vh;
  background-color: #ff3796;
  rotate: 2deg;
}

.second-banner {
  top: 55vh;
  background-color: #5800ff;
  rotate: -2deg;
  display: flex;
  justify-content: flex-end;
  position: absolute;
}

전체적인 구조는 다음과 같다.

전체 구조를 보여주기 위해 .containeroverflow-x 를 주석처리 해뒀다.
실제 사용 시에는 overflow-x : hidden 으로 사용한다.

로직

로직은 되게 단순하다.

배너를 전체 뷰포트 너비보다 길게 만들어 둔 후

재귀적으로 배너를 translateX 를 이용해 이동시켜주고

어느정도 이동이 되었다 싶으면 배너를 제자리로 돌려주는 것이다.

웬만한 슬라이드 애니메이션이 이런 원리로 작동되고 있던 것 같다. 유튜브로 몇 개를 봐도 비슷하더라.

Javscript

현재 나는 리팩토링을 자세히 모르면서 리팩토링 뽕에 취해있고

디자인 패턴을 자세히 모르면서 디자인 패턴 뽕 , 객체지향도 자세히 모르면서 객체지향 뽕에 취해있는 상태이기 때문에

코드가 비효율적일 수도 있다.

이동시킬 배너 객체 생성자 만들기

// 배너 객체 생성

// 배너 객체 생성

class BannerTag {
  constructor() {
    this.count = 0;
  }

  setTag(identifier) {
    this.tag = document.querySelector(identifier);
    return this;
  }

  setTextArray(text) {
    // textArr 는 설정된 text 의 두 배를 넣기
    this.textArray = text.split(' ');
    this.textArray.push(...this.textArray);
    return this;
  }

  setDirection(direction) {
    if (direction === -1) {
      // 배너의 이동 방향이 반대라면 해당 배너를 좌측으로 옮겨줘야 한다.
      this.tag.style.left = '-110%';
    }
    this.direction = direction;
    return this;
  }

  setTextContent() {
    this.tag.textContent = this.textArray.join('\u00A0\u00A0\u00A0\u00A0');
    return this;
  }
	...
}
// 사용 예시
const $banner1 = new BannerTag()
  .setTag('.first-banner')
  .setTextArray(
    'Yummy Tasty Delicious Useful Coding Yummy Yummmmy Yummmmmmmmmy yum',
  )
  .setDirection(1)
  .setTextContent();

우선 이동할 배너 객체를 생성 할 수 있도록 빌터 패턴을 이용하여 클래스를 구현해주었다.

그래서 다음처럼 배너 객체 두 개를 생성해준다.

이후 배너들이 공통적으로 갖는 메소드인 이동하기 로직을 구현해준다.

class BannerTag {
 	...
    
  marqueeText() {
    const isScrollHalf = this.count > this.tag.scrollWidth / 2;

    if (isScrollHalf) {
      this.count = 0;
      this.tag.style.transform = 'translateX(0px)';
      return this.count;
    }
    this.tag.style.transform = `translateX(${-this.count * this.direction}px)`;
    return this.count;
  }

  animate() {
    this.count += 1;

    this.count = this.marqueeText();

    requestAnimationFrame(this.animate.bind(this));
  }
}

이동하는 로직은 되게 단순하다.

재귀적으로 requestAnimationFrame 을 호출시켜주며 , 재귀적으로 marqueeText 를 호출한다.

marqueeText 의 역할은 현재 배너의 이동 횟수가 , 본인 너비의 절반 이상인지를 확인하고

만약 절반 이상 이동했다면 이동 횟수와 배너의 위치를 원상태로 초기화 한다.

만약 절반 이상 이동하지 않았다면 설정된 방향에 맞춰 배너를 이동시킨다.

const $banner1 = new BannerTag()
  .setTag('.first-banner')
  .setTextArray(
    'Yummy Tasty Delicious Useful Coding Yummy Yummmmy Yummmmmmmmmy yum',
  )
  .setDirection(1)
  .setTextContent();

const $banner2 = new BannerTag()
  .setTag('.second-banner')
  .setTextArray(
    'Chicken Hamburger Pizza Salad Sushi Bibimbab Gimbab JJajangmyeon JJambbong',
  )
  .setDirection(-1)
  .setTextContent();

$banner1.animate();
$banner2.animate();

스크롤에 따라 배너 이동시키기

현재 배너는 재귀적으로 1px 씩 이동한다.

그럼 스크롤을 하면 배너가 추가적으로 움직이게 하려면

스크롤을 할 때마다 배너의 count 수를 늘려줘야 한다.

코드에 대해서 열심히 설명 안했지만 배너는 count 프로퍼티에 따라 이동한다.

window.addEventListener('scroll', () => {
  $banner1.count += 15;
  $banner2.count += 15;
});

전역 객체에 스크롤이 올라 갈 때 마다 배너들의 카운트를 올려주었다.

15로 설정한 데 있어 이유는 따로 없다. 해당 수는 높을 수록 스크롤 때 배너가 많이 움직일 것이고 낮을 수록 배너가 조금 움직일 것이다.

회고

처음으로 class 를 좀 의미있게 쓴 것 같은 기분이 든다.

이렇게 사용하니 객체로서 배너들을 관리하는 것이 가능했다.

다만 아쉬웠던 점은 class 내부의 메소드 중

 marqueeText() {
    const MARGIN = 2000;
    const isScrollHalf = this.count + MARGIN > this.tag.scrollWidth / 2;

이 부분의 isScrollHalf 를 구하는 데 있어 내가 생각하던 로직과 달랐다.

강의에서도 분명 해당 태그의 너비의 절반 만큼 이동하면 배너를 제자리 이동 시키도록

this.count > this.tag.scrollWidth / 2 라는 조건에 따라 변경했는데

나는 저 MARGIN 값을 붙여주지 않으면 좌에서 우로 이동하는 배너(파란 배너)가

제자리를 찾지 못한다.

그래서 인위적인 값인 MARGIN 을 붙여주었다.

오늘 자면서 도대체 왜 그런 이유가 생겼는지 생각해봐야겠다.

다음날 아침에 해결됨

그냥 문제가 생긴 파란 태그를 left100% 가 아닌 110% 로 옮겨주었다.

그리고 이 때 넣었던 MARGIN 값 때문에 isScrollHalf 가 항상 true 가 되어 애니메이션이 작동 안되더라 , 그래서 다시 MARGIN 값 없애고 작성했다.

아주 세상 평화롭게 흘러가지만 실제 개발자도구를 열어 보면 매~우 부지런히 이동하고 있다.

그 이유는 배너를 이동시키기 위해 재귀적으로 위치를 변경시키고 있기 때문이다.

잦은 부하는 브라우저의 성능 저하를 일이킨다는 걸 봐서 이래도 되나 싶었다. 그냥 한 번 크게 이동시키고 transition 을 걸어두면 더 나을까 ? 하는 생각도 들었고 ..

근데 유튜브 제작자가 레퍼런스 삼았다는 사이트도 보니 똑같은 로직이더라.

그럼 괜찮을려나 ..

브라우저의 성능을 저하시키는 요소에 대해 정확하게 공부해봐야겠단 생각이 들었다.

전체 코드 링크
https://github.com/yonghyeun/javascript/tree/main/scroll-animation

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글