reset.css
html, body, h1, h2, h3, h4, h5, h6, p, blockquote, code, img, dl, dt, dd, ol, ul, li, fieldset, legend, caption { margin: 0; padding: 0; border: 0; }
div, span, article, section, header, footer, p, ul, li, fieldset, legend, label, a, nav { box-sizing: border-box; }
html {
height: 100%;
}
body {
min-height: 100%;
}
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
ol,
ul {
list-style: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/reset.css">
<style>
.stage{
width: 100vw;
height: 100vh;
background: #333;
display: flex;
justify-content: center;
align-items: center;
}
.door {
position: relative;
width: 100px;
height: 150px;
outline: 2px dashed yellow;
}
.door-back{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
</style>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
</div>
</body>
</html>
먼저 강아지 사진을 넣어주기 위해 레이아웃인 stage와 배경인 door-back을 설정해준다.
그후 강아지의 위치를 대략적으로 잡기 위해
position relative <-> absolute를 활용한 고정 레이아웃 속성을 통해 위치를 잡아준다 .
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
outline: 2px dashed red;
}
그후 이미지를 넣어준다.
.door:nth-child(1) .dog{
background-image: url("images/dog1.png");
}
.door:nth-child(2) .dog{
background-image: url("images/dog2.png");
}
.door:nth-child(3) .dog{
background-image: url("images/dog3.png");
}
(강아지 사진들은 위 사이트에서 다운받았다)
이 사진 위를 문으로 덮어준다.
.door-body{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.door:nth-child(1) .door-body{
background: rgba(255, 0, 0, 0.7);
}
.door:nth-child(2) .door-body{
background: rgba(0, 255, 0, 0.7);
}
.door:nth-child(3) .door-body{
background: rgba(0, 0, 255, 0.7);
}
레이아웃을 잡아줬으니 outline은 제거해준다.
이때까지의 html은 다음과 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/reset.css">
<style>
.stage{
width: 100vw;
height: 100vh;
background: #333;
display: flex;
justify-content: center;
align-items: center;
}
.door {
position: relative;
width: 100px;
height: 150px;
}
.door-back{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
}
.door:nth-child(1) .dog{
background-image: url("images/dog1.png");
}
.door:nth-child(2) .dog{
background-image: url("images/dog2.png");
}
.door:nth-child(3) .dog{
background-image: url("images/dog3.png");
}
.door-body{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.door:nth-child(1) .door-body{
background: rgba(255, 0, 0, 0.7);
}
.door:nth-child(2) .door-body{
background: rgba(0, 255, 0, 0.7);
}
.door:nth-child(3) .door-body{
background: rgba(0, 0, 255, 0.7);
}
</style>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
</div>
</body>
</html>
강아지 움직이기
먼저 강아지를 오른쪽 -> 왼쪽으로 나타나게 해주려는 모션을 취해주기 위해서는
translate속성을 활용해 미리 강아지 사진을 옮겨놓고 추후 애니메이션으로 나타게 하는 방식으로
진행한다.
이를 위해 강아지를 옮겨주었다.
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
/*
이동 애니메이션을 위해 미리 이동시켜놓기
translate3D를 활용하면 GPU를 특별히 사용한다고 한다
%를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
*/
transform: translate3d(100%, 0, 0);
}
translate를 활용해서 강아지를 오른쪽으로 옮겨주고
overflow-hidden을 통해 door-back을 벗어나는 부분들은 가려지도록 하였다.
.door-back{
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
이제 문을 여는 애니메이션을 추가해준다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/reset.css">
<style>
.stage{
width: 100vw;
height: 100vh;
background: #333;
display: flex;
justify-content: center;
align-items: center;
}
.door {
position: relative;
width: 100px;
height: 150px;
}
.door-back{
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
/*
이동 애니메이션을 위해 미리 이동시켜놓기
translate3D를 활용하면 GPU를 특별히 사용한다고 한다
%를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
*/
transform: translate3d(100%, 0, 0);
}
.door:nth-child(1) .dog{
background-image: url("images/dog1.png");
}
.door:nth-child(2) .dog{
background-image: url("images/dog2.png");
}
.door:nth-child(3) .dog{
background-image: url("images/dog3.png");
}
.door-body{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
transition: 1s;
transform-origin: 0%;
}
.door:nth-child(1) .door-body{
background: rgba(255, 0, 0, 0.7);
}
.door:nth-child(2) .door-body{
background: rgba(0, 255, 0, 0.7);
}
.door:nth-child(3) .door-body{
background: rgba(0, 0, 255, 0.7);
}
.door:hover .door-body{
transform: perspective(800px) rotateY(-120deg);
}
</style>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
</div>
</body>
</html>
클릭 이벤트를 주기 전
rotateY 속성을 활용해서 y축으로 회전을 시키고 perspective를 활용해서 입체감이 있게 만들었다.
이제 dog부분이 다시 나타날 수 있도록 정해준다.
hover시 translate를 0으로 돌아오게하고
.door:hover .dog{
transform: translate3d(0, 0, 0);
}
.dog에 transition을 추가해주었다.
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
/*
이동 애니메이션을 위해 미리 이동시켜놓기
translate3D를 활용하면 GPU를 특별히 사용한다고 한다
%를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
*/
transform: translate3d(100%, 0, 0);
transition: 1s 1s;
}
이제 스크립트 부분을 추가해서 click이벤트로 지정하기 위해
hover 부분을 제거하고 door-opened로 변경해준다.
.door-opened .door-body{
transform: perspective(800px) rotateY(-120deg);
}
.door-opened .dog{
transform: translate3d(0, 0, 0);
}
이제 짜야할 로직은 click 시에 해당 문을 열었다가 닫는 로직을 작성해주면된다.
이벤트 위임을 활용해서 click 이벤트를 줘봤다.
또한 3초가 지나면 해당 class가 사라지도록 진행해주었다.
<script>
(function(){
const stage = document.querySelector(".stage");
function doorHandler(e){
// 부모클래스를 타고 가도록 진행하기
let element = e.target;
element.classList.add('door-opened');
setTimeout(() => {
element.classList.remove('door-opened');
}, 3000)
}
stage.addEventListener('click', doorHandler)
})();
</script>
당연히 door이 클릭이 될 줄 알았지만 그게 아닌 door-body에 클릭이 진행이 된다.
따라서 부모 태그를 타고 가서 해당 태그가 door일때 해당 클래스를 추가 할 수 있도록 해준다..
<script>
(function(){
const stage = document.querySelector(".stage");
function doorHandler(e){
// 부모클래스를 타고 가도록 진행하기
let element = e.target;
while(!element.classList.contains('door')){
element = element.parentNode;
if(element.nodeName === 'BODY'){
element = null;
return;
}
}
element.classList.add('door-opened');
}
stage.addEventListener('click', doorHandler)
})();
</script>
위의 코드를 if문을 활용해서 변경시켜보았다.
<script>
(function(){
const stage = document.querySelector(".stage");
function doorHandler(e){
if(element.classList.contains('door-body')){
element.parentNode.classList.add('door-opened');
}
}
stage.addEventListener('click', doorHandler)
})();
</script>
이제 닫는 부분을 진행 시켜줘야한다.
닫는 부분을 진행 할 때 필요한 조건은 다른 문을 열때 열려있던 문을 닫아줘야한다.
이를 위해 변수를 활용해서 값을 저장시키는 방식을 활용한다..
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/reset.css">
<style>
.stage{
width: 100vw;
height: 100vh;
background: #333;
display: flex;
justify-content: center;
align-items: center;
}
.door {
position: relative;
width: 100px;
height: 150px;
}
.door-back{
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
/*
이동 애니메이션을 위해 미리 이동시켜놓기
translate3D를 활용하면 GPU를 특별히 사용한다고 한다
%를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
*/
transform: translate3d(100%, 0, 0);
transition: 1s 1s;
}
.door:nth-child(1) .dog{
background-image: url("images/dog1.png");
}
.door:nth-child(2) .dog{
background-image: url("images/dog2.png");
}
.door:nth-child(3) .dog{
background-image: url("images/dog3.png");
}
.door-body{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
transition: 1s;
transform-origin: 0%;
}
.door:nth-child(1) .door-body{
background: rgba(255, 0, 0, 0.7);
}
.door:nth-child(2) .door-body{
background: rgba(0, 255, 0, 0.7);
}
.door:nth-child(3) .door-body{
background: rgba(0, 0, 255, 0.7);
}
.door-opened .door-body{
transform: perspective(800px) rotateY(-120deg);
}
.door-opened .dog{
transform: translate3d(0, 0, 0);
}
</style>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
</div>
<script>
(function(){
const stage = document.querySelector(".stage");
// 현재 활성화된 아이템을 저장
let currentItem;
function doorHandler(e){
// 부모클래스를 타고 가도록 진행하기
let element = e.target;
if(currentItem){
currentItem.classList.remove('door-opened');
}
if(element.classList.contains('door-body')){
element.parentNode.classList.add('door-opened');
currentItem = element.parentNode;
}
}
stage.addEventListener('click', doorHandler)
})();
</script>
</body>
</html>
currentItem을 활용해서 값을 저장해주고 클릭 이벤트 발생시에 해당 문을 닫아준다.
해당 부분을 리팩토링 진행해주면
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/reset.css">
<style>
.stage{
width: 100vw;
height: 100vh;
background: #333;
display: flex;
justify-content: center;
align-items: center;
}
.door {
position: relative;
width: 100px;
height: 150px;
}
.door-back{
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: black;
}
.dog{
position: absolute;
left: 0;
bottom: 0;
width:100px;
height: 100px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
/*
이동 애니메이션을 위해 미리 이동시켜놓기
translate3D를 활용하면 GPU를 특별히 사용한다고 한다
%를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
*/
transform: translate3d(100%, 0, 0);
transition: 1s 1s;
}
.door:nth-child(1) .dog{
background-image: url("images/dog1.png");
}
.door:nth-child(2) .dog{
background-image: url("images/dog2.png");
}
.door:nth-child(3) .dog{
background-image: url("images/dog3.png");
}
.door-body{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
transition: 1s;
transform-origin: 0%;
}
.door:nth-child(1) .door-body{
background: rgba(255, 0, 0, 0.7);
}
.door:nth-child(2) .door-body{
background: rgba(0, 255, 0, 0.7);
}
.door:nth-child(3) .door-body{
background: rgba(0, 0, 255, 0.7);
}
.door-opened .door-body{
transform: perspective(800px) rotateY(-120deg);
}
.door-opened .dog{
transform: translate3d(0, 0, 0);
}
</style>
</head>
<body>
<div class="stage">
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
<div class="door">
<div class="door-back">
<div class="dog"></div>
</div>
<div class="door-body"></div>
</div>
</div>
<script>
(function(){
const stage = document.querySelector(".stage");
// 현재 활성화된 아이템을 저장
let currentItem;
function activate(item){
item.classList.add('door-opened');
currentItem = item;
}
function inactivate(item){
item.classList.remove('door-opened');
}
function doorHandler(e){
// 부모클래스를 타고 가도록 진행하기
let element = e.target;
if(currentItem){
inactivate(currentItem);
}
if(element.classList.contains('door-body')){
activate(element.parentNode);
}
}
stage.addEventListener('click', doorHandler);
activate(document.querySelector('.door:first-child'))
})();
</script>
</body>
</html>
움직이는 문이 완성되었다.
www.inflearn.com/course/interactive_web/dashboard
위의 강의를 듣고 정리한 글입니다.
'JavaScript > javascript 활용하기' 카테고리의 다른 글
javascript - transition, animation (0) | 2020.12.06 |
---|---|
CSS - 스크롤 이벤트 (0) | 2020.12.06 |
이벤트 위임 처리하기 (0) | 2020.11.29 |
javascript를 활용한 tic tac toe 만들어보기 (0) | 2020.07.08 |
자바스크립트를 활용해 현재 날짜부터 마지막 날짜 구하기 (0) | 2020.03.04 |