자바스크립트로 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;
}
전체적인 구조는 다음과 같다.
전체 구조를 보여주기 위해
.container
의overflow-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
을 붙여주었다.
오늘 자면서 도대체 왜 그런 이유가 생겼는지 생각해봐야겠다.
다음날 아침에 해결됨
그냥 문제가 생긴 파란 태그를
left
로100%
가 아닌110%
로 옮겨주었다.그리고 이 때 넣었던
MARGIN
값 때문에isScrollHalf
가 항상true
가 되어 애니메이션이 작동 안되더라 , 그래서 다시MARGIN
값 없애고 작성했다.
아주 세상 평화롭게 흘러가지만 실제 개발자도구를 열어 보면 매~우 부지런히 이동하고 있다.
그 이유는 배너를 이동시키기 위해 재귀적으로 위치를 변경시키고 있기 때문이다.
잦은 부하는 브라우저의 성능 저하를 일이킨다는 걸 봐서 이래도 되나 싶었다. 그냥 한 번 크게 이동시키고 transition
을 걸어두면 더 나을까 ? 하는 생각도 들었고 ..
근데 유튜브 제작자가 레퍼런스 삼았다는 사이트도 보니 똑같은 로직이더라.
그럼 괜찮을려나 ..
브라우저의 성능을 저하시키는 요소에 대해 정확하게 공부해봐야겠단 생각이 들었다.
전체 코드 링크
https://github.com/yonghyeun/javascript/tree/main/scroll-animation