타이핑 애니메이션 만들기


자기 소개 페이지의 리퍼런스를 찾다보니 많은 분들이 타이핑 애니메이션을 사용한 것을 볼 수 있었다. 아직 내 페이지의 구성이 결정된 건 아니지만 그냥 연습 삼아 직접 만들어보기로 했다.
배경은 애증의 도시 뉴욕 🗽



1. 타이핑 애니메이션 분석 📽

문서 편집기에 글을 적을 때 입력 커서가 밀리면서 한 글자씩 입력되는 모습을 볼 수 있다. 글자를 입력중일때 커서는 깜빡이지 않는다. 입력이 멈추면 커서는 약 500ms 간격으로 깜빡인다.

setInterval로 일정한 간격을 두고 DOM API의 textContent를 활용하여 문자열을 하나씩 더하는 기능을 구현하면 쉽게 만들 수 있을 것 같다. 문자열이 하나씩 더해지고, 입력 커서는 문자열이 추가될 때마다 조금씩 밀려나는 모습을 표현한다.



2. 준비 🏃🏻‍♂️


2-1. HTML 🏗

<body>
  <section class="wrapper">
    <article class="banner">
      <div>
        <div class="container">
          <h2 class="blinking__txt"></h2>
          <span class="cursor">|</span>
        </div>
      </div>
    </article>
  </section>
</body>

타이핑 되는 키워드는 <h2> 태그를 사용하고 형제 요소로써 대표적인 인라인 요소인 <span> 내부에 커서를 표현한다.

2-2. CSS 🎨

.body {
  font-family: 'Bebas Neue', Arial, Helvetica, sans-serif;
  background-color: #f4f4f4;
  height: 100vh;
  font-size: 10px;
}

.wrapper {
  margin: 0 auto;
  width: 1280px;
  height: 2000px;
  padding: 50px;
}

.banner {
  height: 500px;
  overflow: hidden;
}

.banner > div {
  height: 100%;
  width: 100%;
  background-image: url(img/newyork.jpeg);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  background-attachment: fixed;
  position: relative;
}

.banner .container {
  position: absolute;
  width: 700px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.banner div h2 {
  color: #FEDC45;
  font-size: 5em;
  display: inline;
}

.banner div .cursor {
  color: #FEDC45;
  font-size: 5em;
  opacity: 1;
}

<div>로 타이핑 되는 요소들을 묶어주어야 글자가 추가되기 전에도 가운데 정렬을 유지할 수 있다.



3. 기능 구현 👨🏼‍💻


3-1. JS ⚙️

const h2 = document.querySelector('.blinking__txt');
const cursor = document.querySelector('.cursor');

제일 먼저 DOM 조작(Manipulation)이 이루어질 요소들을 변수에 미리 할당한다. 변수에 할당된 DOM 요소들은 한 곳에 모아놓는 것이 좋다.
이제 타이핑 애니메이션을 수행하는 typing 함수를 만든다.

const typing = function(_, counter = 0) {
    // 출력할 내용
  const txt = `I'm Wonkook Lee.\nPleased to meet you 👋`;
  
  setInterval(() => {
    // 글자가 모두 출력되면 setInterval을 멈출 것
    if (txt.length === counter) {
      cursor.classList.add('blink_animate');
      return;
    };
    // 문자열 하나하나 h2의 텍스트 컨텐츠로 추가한다 
    h2.textContent += txt[counter];
    // 카운터 증산
    counter++;
  }, 80); 
};

// 페이지 요소가 로드되면 타이핑 애니메이션을 수행할 것
window.addEventListener('load', typing);

아주 작은 TMI 😇

typing 함수의 parameter에 언더바(_)와 할당연산자(=)가 있는 이유는, 함수가 이벤트 핸들러의 콜백으로 사용되었기 때문에 첫번째 인자는 event 객체이며, 굳이 사용하지 않는 argument이기 때문에 언더바로 처리한 것.

parameter에 값을 할당한 것은 default parameter라고 하는데, 인자로 전달되지 않았을 경우 할당된 값을 기본값으로 사용하겠다는 의미이다.

3-2. 페이지 요소가 로드되기 전 애니메이션이 시작되는 문제 🛑

굳이 'load' 이벤트 핸들러를 추가한 이유는 아래와 같다.

개발자 도구에서 Slow 3G 속도로 페이지를 로드했을때, 전체 이미지나 웹폰트가 로드되지 않았음에도 타이핑 애니메이션이 먼저 시작되어버린다. script 태그에 defer 키워드를 넣어 비동기적으로 로드해도, <body> 태그의 끝에 삽입해도 똑같은 현상이 일어난다.

그래서 DOM tree가 모두 구성되면 타이핑 애니메이션을 수행하기 위한 이벤트 핸들러를 추가했다.

3-3. 이벤트 핸들러로 애니메이션 비동기화 ✅


모든 리소스가 로드되고 난 후 애니메이션이 작동되는 것을 확인할 수 있다.
느린 통신 환경의 유저가 아직 작동하지 않는 화면을 보게 되더라도, 빈 화면의 커서가 호기심을 끌어 무슨 일이 일어날 것 같은 기대감을 줄 것이라 생각한다.

유저에게 애니메이션의 정상적인 작동을 보여주는 것이 목표라면 어느정도 달성했다고 할 수 있겠다.

3-4. 문자열 출력을 마친 후 커서가 깜빡이는 효과 ✨

    if (txt.length === counter) {
      cursor.classList.add('blink_animate');
      return;
    };

위 코드에서 알 수 있듯이 setInterval의 반복이 문자열의 길이와 같아진다면 커서를 표현한 <span> 태그에 blink_animate 클래스가 추가되고 종료된다.

즉 타이핑이 끝난 상태여야만 커서가 깜빡이기 시작한다는 이야기다.

그리고 해당 클래스엔 키프레임 애니메이션을 담은 CSS 속성을 담는다.

.blink_animate {
  animation-name: blink;
  animation-duration: 1000ms;
  animation-iteration-count: infinite;
  animation-timing-function: step-end;
  /* step-end 또는 step(1)을 넣지 않으면 transition이 일어난다*/
}

또 다른 setInterval 함수로 커서의 깜빡임을 표현하는 것은 리소스 관리 면에서 비효율적일 것 같아 css의 키프레임 애니메이션을 활용했다.



4. 완성 🥚🐓

페이지가 로드될 때 단 한번만 실행한다면 즉시실행함수(IIFE) 안에 묶어놔도 될 것 같다.

프론트엔드 분야는 결과물이 즉각 눈에 보이는 점이 매력적이다




5. 번외


5-1. Prompt로 사용자의 이름을 받아 인사하기 👋




5-2. 값이 없으면 기본 값으로 출력하기 🙌🏻

const blink = document.querySelector('.blinking__txt');
const cursor = document.querySelector('.cursor');

const typing = function(_, counter = 0) {
  const yourName = prompt(`Please type your name 😇`);
  const txt = `Hey ${yourName ? yourName : 'there'}.\nPleased to meet you 👋`;

  setInterval(() => {
    if (txt.length === counter) {
      cursor.classList.add('blink_animate');
      return;
    };
    blink.textContent += txt[counter];
    counter++;
  }, 80);
}

window.addEventListener('load', typing);

🙏🏻 잘못된 정보가 있다면 지적해주세요

profile
© 가치 지향 프론트엔드 개발자

0개의 댓글