typeWriterEffect

woolee의 기록보관소·2022년 11월 1일
0

FE 기능구현 연습

목록 보기
8/33

HTML

Brad Traversy The 뒤로 작성할 목록은 data-words에 넣어두기

<div class="container">
    <h1>Brad Traversy The
      <span class="txt-type" 
            data-wait="3000" 
            data-words='["Developer", "Designer", "Creator"]'>
      </span>
    </h1>
    <h2>Welcome To My Website</h2>
 </div>

CSS

body
background에 url로 이미지 삽입.

  • background-repeat => 배경 이미지 반복 여부와 방향 설정 (no-repeat : 반복하지 않음)
  • background-position => center center면 정 가운데
  • background-size => cover면 영역 안에 이미지 꽉차도록 함. 슬래시(/)로 구분짓기

container
.container에 flex로 h1과 h2를 세로로 줄 세우도록
상대 단위 : em, rem (em은 해당 단위 사용되고 있는 요소의 font-size 기준, rem에서 r은 root이므로 최상위 요소 font-size 기준)

@media
웹 뷰포트 별 인터랙션

.txt-type > .txt
자식 선택자(>)
고정된 글자를 .txt-type 클래스로 관리
변동 글자를 .txt 클래스로 관리
=> 변동 글자의 오른쪽 border 값만 스타일링 해주기

@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@100;200;300&display=swap');

body {
  font-family: 'Raleway', sans-serif;
  height: 100vh;
  background: #333 url('showcase.jpg') no-repeat center center / cover;
  color: #ccc;
  overflow: hidden;
}

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  padding: 0 3rem;
}

h1, 
h2 {
  font-weight: 200;
  margin: 0.4rem;
}

h1 {
  font-size: 3.5rem;
}

h2 {
  font-size: 2rem;
  color: #aaa;
}

/* Cursor */
.txt-type > .txt {
  border-right: 0.2rem solid #777;
} 

@media(min-width: 1200px) {
  h1 {
    font-size: 5rem;
  }
}

@media(max-width: 800px) {
  .container {
    padding: 0 1rem;
  }
  h1 {
    font-size: 3rem;
  }
}

@media(max-width: 500px) {
  h1 {
    font-size: 2.5rem;
  }
  h2 {
    font-size: 1.5rem;
  }
}

JS (ES6 Class 문법 사용)

TypeWriter 클래스 생성하기

  • parseInt(string문자열, radix진수)
  • 클래스 인자로 3개를 받는다.
    -- 첫번째 인자(txtElement)로 .txt-type을 받는다.
    -- 두번째 인자(words)로 data-words 속성을 배열 형태로 받아 온다(문자열을 parse로).
    -- 세번째 인자(wait)로 data-wait 속성을 문자열로 받아 온다.

type()
함수 내에서
변수 current을 index를 단어 길이로 나머지 연산을 해준 값으로 주고,
words의 index로 current를 줘서 결과적으로
data-words 속성에 내가 작성한 문자열을 순회하도록 잡아준다.

즉, data-words 속성에서의 단어마다
처음에는 false니까 즉, 문자열이 없으니까 하나씩 채워주고
꽉차면 다시 지워줄 수 있도록

  • this.isDeleting이 true면 => 문자열을 하나씩 감소시켜주고
  • this.isDeleting이 false면 => 문자열을 하나씩 증가시켜주고

즉, isDeleting이 true면 꽉채워진 것, false면 꽉차지 않은 것으로 분기점을 잡기.

this.isDeleting이 true면 속도를 절반으로 줄여주기

글자를 채워주다가 꽉차면 isDeleting을 true로 업데이트해주고.
글자를 빼주다가 비워지면 isDeleting을 false로 업데이트해주고 wordIndex++;을 해줘서 다음 단어로 넘어가도록 잡아준다. 그리고 지워지는 순간에는 속도를 500으로 넣어서 좀 가속도가 붙을 수 있게.

isDeleting을 false와 true 사이에 계속해서 ++과 --를 오고 가게 함으로써 계속 인터랙션이 구현될 수 있도록

DOMContentLoaded는 html 전부 읽고 DOM 트리 완성하는 즉시 발생함. 이미지나 스타일시트는 기다리지 않는다.

// ES6 Class 
class TypeWriter {
  constructor(txtElement, words, wait = 3000) {
    this.txtElement = txtElement;
    this.words = words;
    this.txt = '';
    this.wordIndex = 0;
    this.wait = parseInt(wait, 10);
    this.type();
    this.isDeleting = false;
  }

  type() {
    // console.log('Hello'); 웹사이트 열면 계속 찍힘
  
    // Current index of word
    const current = this.wordIndex % this.words.length;
    // Get full text of current word
    const fullTxt = this.words[current];
    
    // Check if deleting 
    if (this.isDeleting) {
      // Remove char
      this.txt = fullTxt.substring(0, this.txt.length - 1);
    } else {
      // Add char
      this.txt = fullTxt.substring(0, this.txt.length + 1);
    }

    // Insert txt into element 
    this.txtElement.innerHTML = `<span class="txt">${this.txt}</span>`;

    // Initial Type Speed
    let typeSpeed = 300;

    if (this.isDeleting) {
      typeSpeed /= 2;
    }

    // If word is complete
    if (!this.isDeleting && this.txt === fullTxt) {
      // Make pause at end
      typeSpeed = this.wait;
      // Set delete to true
      this.isDeleting = true;
    } else if (this.isDeleting && this.txt === '') {
      this.isDeleting = false;
      // Move to next word
      this.wordIndex++;
      // Pause before start typing 
      typeSpeed = 500;
    }

    setTimeout(() => this.type(), typeSpeed);
  }
}

// Init On DOM Loa1d 
document.addEventListener('DOMContentLoaded', init);

// Init App
function init() {
  const txtElement = document.querySelector('.txt-type');
  const words = JSON.parse(txtElement.getAttribute('data-words'));
  const wait = txtElement.getAttribute('data-wait');
  // Init TypeWriter
  new TypeWriter(txtElement, words, wait);
}

JS (prototype 사용)

const TypeWriter = function(txtElement, words, wait = 3000) {
  this.txtElement = txtElement;
  this.words = words;
  this.txt = '';
  this.wordIndex = 0;
  this.wait = parseInt(wait, 10);
  this.type();
  this.isDeleting = false;
}

// Type Method
TypeWriter.prototype.type = function () {
  // console.log('Hello'); 웹사이트 열면 계속 찍힘
  
  // Current index of word
  const current = this.wordIndex % this.words.length;
  // Get full text of current word
  const fullTxt = this.words[current];
  
  // Check if deleting 
  if (this.isDeleting) {
    // Remove char
    this.txt = fullTxt.substring(0, this.txt.length - 1);
  } else {
    // Add char
    this.txt = fullTxt.substring(0, this.txt.length + 1);
  }

  // Insert txt into element 
  this.txtElement.innerHTML = `<span class="txt">${this.txt}</span>`;

  // Initial Type Speed
  let typeSpeed = 300;

  if (this.isDeleting) {
    typeSpeed /= 2;
  }

  // If word is complete
  if (!this.isDeleting && this.txt === fullTxt) {
    // Make pause at end
    typeSpeed = this.wait;
    // Set delete to true
    this.isDeleting = true;
  } else if (this.isDeleting && this.txt === '') {
    this.isDeleting = false;
    // Move to next word
    this.wordIndex++;
    // Pause before start typing 
    typeSpeed = 500;
  }

  setTimeout(() => this.type(), typeSpeed);
}

// Init On DOM Loa1d 
document.addEventListener('DOMContentLoaded', init);

// Init App
function init() {
  const txtElement = document.querySelector('.txt-type');
  const words = JSON.parse(txtElement.getAttribute('data-words'));
  const wait = txtElement.getAttribute('data-wait');
  // Init TypeWriter
  new TypeWriter(txtElement, words, wait);
}

참고

Pure JavaScript Type Writer Effect

profile
https://medium.com/@wooleejaan

0개의 댓글