마우스로 눌렀을 때 3D 형식으로 문이 열리고 캐릭터가 화면에 나오는 애니메이션을 만들어보려고 한다.
CSS만 활용해서 먼저 애니메이션을 구현하고 이후 사용자 이벤트와 함께 연동할 생각이다.
CSS 애니메이션
애니메이션을 만든다고 바로 적용하는 것이 아닌 우선 레이아웃을 구성할 계획이다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./index.css" />
<title>3 - Door</title>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back"></div>
<img class="door-character" src="./images/ilbuni_0.png" />
<div class="door-front"></div>
</div>
<div class="door">
<div class="door-back"></div>
<img class="door-character" src="./images/ilbuni_1.png" />
<div class="door-front"></div>
</div>
<div class="door">
<div class="door-back"></div>
<img class="door-character" src="./images/ilbuni_2.png" />
<div class="door-front"></div>
</div>
</div>
</body>
</html>
css 코드를 넣어주기 위해서 link 태그를 통해 파일을 연결해 주고 배경화면이 되는 stage div와
각각의 문을 나타내는 door div, 배경이 되는 back과 캐릭터, 열리는 문이 될 front가 존재한다.
css를 사용해서 가운데 정렬을 해주고 문의 크기, 색상들을 먼저 넣어줬다.
.stage {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
background: #333;
}
.door {
position: relative;
width: 100px;
height: 150px;
}
.door-back {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 100%;
height: 100%;
background: black;
}
.door-character {
position: relative;
z-index: 1;
width: 100px;
height: auto;
}
.door-front {
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
height: 100%;
opacity: 0.8;
}
.door:nth-child(1) .door-front {
background: red;
}
.door:nth-child(2) .door-front {
background: yellow;
}
.door:nth-child(3) .door-front {
background: green;
}
레이아웃 관련 스타일이라 크게 설명할 부분은 없고 요소들에게 z index를 줘서 순서를 나타내주었다.
.door-front {
position: relative;
z-index: 1;
width: 100px;
height: auto;
}
캐릭터를 움직일 때 absolute를 사용해도 되지만 transfrom을 사용하는 방식이 성능상 더 좋기 때문에
relative로 설정해서 z index만 주고 추후 transfrom을 사용할 것이다.
.door:nth-child(1) .door-front {
background: red;
}
.door:nth-child(2) .door-front {
background: yellow;
}
.door:nth-child(3) .door-front {
background: green;
}
추가적으로 문은 순서대로 색상이 달라지기 때문에 nth-child를 사용해서 색상을 할당했다.
여기까지 구현하면 다음과 같은 레이아웃이 완성될 것이다.
이제 본격적으로 애니메이션을 넣어주자.
<div class="door">
<div class="door-back">
<img class="door-character" src="./images/ilbuni_0.png" />
</div>
<div class="door-front"></div>
</div>
애니메이션을 넣으려고 생각하자마자 바로 변경사항이 발생했는데, 구현 목표를 보면 캐릭터가 검은색 배경 밖으로
나가면 사라진다.
이것을 opacity 등 다양한 방법으로 처리할 수 있겠지만 검은 배경 자식으로 이미지를 넣어주고 검은 배경에 overflow hidden을 준다면 간단하게 처리할 수 있을 것이다. 바로 변경해 주었다.
.door-back {
position: absolute;
left: 0;
top: 0;
z-index: 0;
display: flex;
justify-content: center;
align-items: flex-end;
width: 100%;
height: 100%;
background: black;
overflow: hidden;
}
레이아웃을 바꿔주면서 스타일도 변경해 주었다. 달라진 부분은 overflow를 hidden으로 주었고, 캐릭터가
가운데 하단 정렬이 돼야 하기 때문에 display 속성을 추가로 주었다.
.door-character {
position: relative;
z-index: 1;
width: 100px;
height: auto;
transform: translate3d(100%, 0, 0);
}
이후 캐릭터 요소에도 transform 속성을 주었다.
이때 translateX를 사용해도 되는데 translate3d를 사용한 이유는 하드웨어 가속을 확정적으로 주기 위해서이다.
하드웨어 가속?
복잡한 계산이 필요한 인터렉션은 렌더링에 많은 시간이 걸린다.
이때 CPU가 아닌 GPU에서 계산하게 해 준다면 렌더링 시간을 줄일 수 있게 된다.
transform3 d 요소를 사용하면 하드웨어 가속을 사용할 수 있다.
GPU를 사용하기 때문에 일반적은 CPU 처리보단 좋은 퍼포먼스를 얻을 수 있지만 주의할 점이 있다.
- 너무 많은 ( 무분별한 ) 하드웨어 가속은 오히려 브라우저를 느리게 만든다.
- 하드웨어 가속 속성이 부여되면 즉시 GPU에 업로드되며, 이로 인해 깜빡이는 증상이 발생할 수 있다.
- 성능 낮은 기기는 오히려 성능 저하를 발생할 수 있다.
장점이 있지만 모든 요소를 transform3d를 사용한다면 성능 저하가 발생할 수 있으니 주의하면서 사용해야 한다.
.door:hover .door-front {
transform: perspective(800px) rotateY(-100deg);
}
자바스크립트를 활용해서 클릭 시 애니메이션이 동작되는 것이 최종 목표지만 우선 css로 애니메이션을 적용시키기 때문에 transform 속성을 사용해서 애니메이션을 줬다.
이때 perspective 속성은 해당 요소를 3D 영역으로 만들어주는 속성이다.
속성을 줬을 때와 주지 않았을 때 차이가 확실하게 나타난다.
또한 rotateY의 경우에도 양수를 주면 안쪽으로 회전하고 음수를 주면 밖으로 회전한다. 이번 목표는 문을 당겨서 여는 애니메이션이기 때문에 -100을 줘서 밖에서 여는 애니메이션을 주었다.
.door-front {
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
height: 100%;
opacity: 0.8;
transform-origin: left;
transition: 0.5s;
}
매끄럽게 애니메이션이 동작할 수 있게 transition 속성을 주었고 회전축이 왼쪽으로 붙어서 실행될 수 있게
tranform-origin 속성을 주었다.
.door-character {
position: relative;
z-index: 1;
width: 100px;
height: auto;
transform: translate3d(100%, 0, 0);
transition: 0.5s 0.3s;
}
.door:hover .door-character {
transform: translate3d(0, 0, 0);
}
마지막으로 캐릭터 역시 애니메이션이 실행되면 노출되야 하므로 transform 속성을 줬으며,
transition 속성 역시 주었다.
이때, 문이 열리면서 바로 캐릭터가 나오는 것이 아니기 때문에 0.3초 딜레이를 주었다.
여기까지 작업하면 css를 통해서 애니메이션을 구현하는 작업은 끝이 났다.
JavaScript 작성
<script type="text/javascript" src="./index.js"></script>
외부 스크립트 파일을 불러왔다.
내부에 작성해도 되지만 이왕이면 나눠서 작성하는게 보기 편하다고 생각한다.
자바스크립트로 문을 클릭하면 애니메이션이 동작되게 하는 것이 목표이기 때문에 앞서 css로 작업했던
호버 애니메이션을 수정해줄 필요가 있다.
.door-opened .door-character {
transform: translate3d(0, 0, 0);
}
.door-opened .door-front {
transform: perspective(800px) rotateY(-100deg);
}
호버로 설정해둔 코드를 opend 클래스를 가지고 있을 때 동작하는 방식으로 변경하였다.
(function () {
const stageElem = document.querySelector(".stage");
function doorHandler(e) {
const targetElem = e.target;
if (targetElem.classList.contains("door-front")) {
targetElem.parentNode.classList.add("door-opened");
}
}
stageElem.addEventListener("click", doorHandler);
})();
전역 변수가 오염되는 것을 방지하기 위해서 즉시 실행 함수로 실행했다.
즉시 실행 함수를 사용하면 선언한 변수가 전역에서 사용할 수 있게 되는 문제를 방지할 수 있다.
반드시 필수는 아니지만 해당 코드는 굳이 전역에서 동작할 필요가 없기 때문에 사용하였다.
stage 요소를 가지고 와서 click 이벤트를 주었다.
이벤트에서는 클릭한 요소의 클래스에 door-front가 있다면 opened 클래스를 제공할 수 있게 설정했다.
contains는 매개변수로 넘겨준 클래스명을 포함하고 있는지 없는지에 따라 true, false를 반환한다.
여기까지 작성하면 문을 누르면 애니메이션이 동작할 수 있도록 구현되었다. 이어서 다른 문을 누르면 열린 문이 닫히는 작업을 진행하자.
(function () {
const stageElem = document.querySelector(".stage");
let openedElem;
function doorHandler(e) {
const targetElem = e.target;
if (openedElem) {
openedElem.classList.remove("door-opened");
}
if (targetElem.classList.contains("door-front")) {
targetElem.parentNode.classList.add("door-opened");
openedElem = targetElem.parentNode;
}
}
stageElem.addEventListener("click", doorHandler);
})();
openedElem 변수를 하나 만들어서 문 여는 이벤트가 실행될 때 넣어주고 다시 호출되는 순간에 기존 클래스를 삭제해 주었다. 이렇게 작업하면 다른 요소를 클릭하면 열려있던 문은 닫히게 된다.
'Web' 카테고리의 다른 글
단방향 암호화와 양방향 암호화 (1) | 2024.10.05 |
---|---|
웹사이트 로그인의 역사 (1) | 2024.10.02 |
CSS 레이아웃 시스템의 변화 (1) | 2024.06.05 |
CSS 3D (2) | 2024.05.08 |
CSS 선택자 (2) | 2024.05.05 |