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로 이미지 삽입.
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 클래스 생성하기
type()
함수 내에서
변수 current을 index를 단어 길이로 나머지 연산을 해준 값으로 주고,
words의 index로 current를 줘서 결과적으로
data-words 속성에 내가 작성한 문자열을 순회하도록 잡아준다.
즉, data-words 속성에서의 단어마다
처음에는 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);
}
참고