토이프로젝트를 하나 완성했다. 앞으로 수정하고 업데이트 할 일이 많아보이는데, 조급해하지 않고 천천히 하려고 한다. 의식의 흐름대로 쓴 작업 수기는 이미 네이버 블로그에 올렸기 때문에 여기선 거기서 못 다 한 로고 애니메이션을 코드와 함께 풀어볼까 한다.
완성된 사이트 : http://typo.co.kr/
이전에 리액트를 깨작거리면서 자바스크립트에 대한 갈증이 많이 생겼었다. 자바스크립트도 잘 모르는데 대뜸 리액트부터 들이박다보니 너무 많이 허둥댔던 기억이 난다. 그래서 이번에 html, css과 함께 바닐라 자바스크립트만 가지고 작업을 마무리 했다. 어차피 Node를 쓸 정도의 기술이 필요했던 것도 아니어서, '오히려 좋아'를 외치며 재밌게 작업했다. 이번엔 조금이라도 기초를 다지자 싶어 기본적인 내용부터 다시 학습하면서 병행했다. 그러다보니 중간에 코드를 리팩토링하는 과정도 제법 빈번했다. 리팩토링할 수 있다는 것 자체가 학습효과를 방증하는 게 아니겠나 싶어서 다행이랄까. 그래서 그런지 초반과 후반의 코드 스타일이 달라진다.😂 이번에 진행하면서 알게 된 기초적인 내용이라도 좀 정리해서 남겨놔야겠다. 뒤돌아서면 까먹는 수준이라 나를 믿을 수 없다.
타이핑 하듯 움직이는 로고 모션만들기
네이버 블로그에 언급했지만, 특정 로고 형태로 고정시키지 않고 가변형 로고를 만들면서 자바스크립트로 간단한 애니메이션을 곁들이게 됐다.
위 gif 이미지처럼 빠르진 않고 10초에 한 번씩 변경된다.
로직은 아래와 같다.
1. 여러가지 스타일의 폰트 랜덤 선택
2. 폰트마다 사이즈가 달라 로고가 바뀔때마다 가로 길이에 따라 파란 박스가 자동으로 늘어나야 됨
3. 파란박스 둘레의 6개의 작은 박스도 거기에 맞게 재정렬되어야 함
4. 타이핑 하듯 커서가 깜빡거리면서 쫓아다닐 것
5. 10초에 한 번씩 한글자씩 지워지고 다시 한글자씩 나타나야 됨
6. 사용된 폰트의 이름을 로고 아래 보여줄 것(페이드인/아웃)
7. 다른 브라우저 탭으로 옮겨가는 등 화면에서 벗어나면 브라우저 리소스를 위해 잠시 멈출 것
8. 화면 안에 다시 돌아오면 다시 재생할 것
제약사항을 대략적으로 정리하고 하나씩 처리했다. 폰트 이름을 제외하면 200줄이 안된다.
전부는 아니고 몇 가지 언급하자면,
const randomLetterStyle = () => {
addLogoText();
plus.style.fontFamily = randomFont();
typo.style.fontFamily = randomFont();
setTimeout(deleteLogoText, CHANGE_LOGO_TIME - 2000);
};
기본 뼈대는 로고를 한글자씩 쓰는 addLogoText 함수와 거기 랜덤폰트를 입히는 함수, 그리고 setTimeout을 이용해 한글자씩 지우는 함수로 이루어져 있다.
커서의 깜빡임은 '|' 이 텍스트를 적당한 사이즈와 굵기 등 스타일을 넣고, opacity를 0과 1로 주기적으로 변형하도록 했다.
const cursorBlink = () => {
fontCursor.style.opacity = fontCursor.style.opacity === "0" ? "1" : "0";
};
setInterval(cursorBlink, cursorBlinkTime);
로고의 상태를 다루는 건 클래스를 활용해 봤는데, 이게 맞나 싶은 느낌이라서 솔직히 자신은 없다. (하긴 자신있는 코드가 하나도 없는데 무슨 쓸데없는 고민을...😇)
class LogoState {
isLogoVisible;
visibility;
constructor() {
this.isLogoVisible = this.getLogoVisibility();
this.visibility = setInterval(randomLetterStyle, CHANGE_LOGO_TIME);
}
getLogoVisibility(visible) {
let boundary = logo.getBoundingClientRect().top > -100;
if (visible === 'visible' && boundary === true) { // visiblityState event
return true;
}
if (this.visibility > 0 && boundary === true) { // 이미 진행중인 경우
return null;
} else if (boundary === true && (this.visibility === undefined || this.isLogoVisible === null)) {
return boundary;
} else if (this.visibility > 0 && boundary === false) { // 로고가 지정영역을 벗어난 경우
return boundary;
}
}
getLogoState() {
return this.isLogoVisible;
}
setLogoState(visible) {
this.isLogoVisible = this.getLogoVisibility(visible);
}
getVisibility() {
return this.visibility;
}
handleLogoEvent() {
if (this.isLogoVisible === null || this.isLogoVisible === undefined) {
return;
}
if (this.isLogoVisible) {
this.visibility = setInterval(randomLetterStyle, CHANGE_LOGO_TIME);
} else if (!this.isLogoVisible) {
clearInterval(this.visibility);
this.visibility = undefined;
}
}
handleLogoChange(visible) {
this.setLogoState(visible);
this.handleLogoEvent();
}
}
이 클래스를 통해 스크롤 이벤트 시 화면에서 들락거리게 되는 것과 다른 브라우저 탭 등으로 이탈할 때 상태를 체크하여 멈추거나 다시 재생시키도록 처리했다. 이탈 체크는 visibilitychange라는 이벤트가 존재해서 처음 고민했던 것과 달리 생각보다 쉽게 해결됐다. MDN에 따르면 브라우저 탭의 컨텐츠가 visible 또는 hidden 상태로 변화할 때 발생된다고 한다. 여러가지 상황에서 이용해 볼 수 있겠단 생각이 든다.
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
logoState.handleLogoChange('visible');
} else {
clearInterval(logoState.getVisibility());
}
});
좀 많이 헤맸던 건 글자를 지우고 다시 쓰는 동작이었는데, setInerval을 처음 써보다보니 딱 원하는 순간에 멈추거나 다시 재생시키는 컨트롤이 어려웠다. 잘 학습해서 깔끔하게 한방에 끝냈다기 보다는 계속 테스트 해보면서 몸으로 떼운 느낌이다.(헷갈) 이번에도 역시 MDN setInterval문서를 참고했다. 내 작업의 포인트는 interval ID를 반환받아 clearInterval() 함수를 적절한 시점에 잘 써주는 것이었다.
const addLogoText = () => {
const plusLoop = setInterval(() => {
if (plus.innerText.length < 4) {
plus.innerHTML += getLogoText(LOGO_TEXT[currentIndex]);
currentIndex++;
}
if (currentIndex === 4) {
clearInterval(plusLoop);
const typoLoop = setInterval(() => {
if (typo.innerText.length < 4) {
typo.innerHTML += getLogoText(LOGO_TEXT[currentIndex]);
currentIndex++;
}
if (currentIndex === 8) {
currentIndex = 0;
fadeInLogoFont();
plusFont.innerHTML = plus.style.fontFamily.replace(/\"/gi, "");
typoFont.innerHTML = typo.style.fontFamily.replace(/\"/gi, "");
setTimeout(fadeOutLogoFont, CHANGE_LOGO_TIME - 2000);
clearInterval(typoLoop);
}
}, 80);
}
}, 50);
};
const deleteLogoText = () => {
const typoLoop = setInterval(() => {
typo.innerHTML = typo.innerHTML.slice(0, -1);
if (typo.innerHTML.length === 0) {
clearInterval(typoLoop);
const plusLoop = setInterval(() => {
plus.innerHTML = plus.innerHTML.slice(0, -1);
if (plus.innerHTML.length === 0) {
clearInterval(plusLoop);
}
}, 80);
}
}, 50);
};
다음에는 가장 많이 사용한 canvas에 대해서 실제 사용한 코드와 함께 정리해 볼까 한다.
자바스크립트를 쓰면 쓸수록 더 잘 쓰고 싶은 욕구가 생긴다. 지금 당장 할 수 있는 게 훨씬 많아지는 기분이랄까. 두뇌야 힘내.
'개발 > Etc' 카테고리의 다른 글
SQL 기본 문법 (0) | 2023.06.13 |
---|---|
git 간단 명령어 모음 (0) | 2023.01.25 |
웹 성능 개선 - 폰트편 (0) | 2023.01.12 |
Cumulative Layout Shift(누적 레이아웃 이동, CLS) (0) | 2023.01.11 |
npm trends _ 라이브러리 선택할 때 유용한 서비스 (2) | 2023.01.11 |