🛠

ch9 - 종합예제

자, 지금까지 배운 내용을 종합하여 상호작용하는 애니메이션을 만들어봅시다. 주인공 '냥이' 가 숲속에서 길을 잃은 동물들을 구조하는 애니메이션입니다. 구현해 볼 화면과 동작은 아래와 같습니다.
notion imagenotion image
1. 키보드에 있는 좌우 방향키를 이용해 냥이를 움직입니다. 2. 친구들을 만나면 하늘로 솟아오르게 합니다. 끝입니다. 간단하죠?? 하지만 안에는 냥이의 발 동작을 스프라이트 애니메이션을 통해 구현하고, 좌우 이동에 따른 회전, 배경의 속도를 이용한 원근감 효과, 친구들의 좌표를 이용한 위치 파악 등 여러가지 기능들이 숨어 있습니다. 아래는 실제 어떤 방식으로 동작하는 지에 대한 간단한 그림 설명입니다.
notion imagenotion image
보시다시피 냥이와 동물 친구들의 위치는 고정되어 있고 실제로는 배경만 움직이는 것이 핵심입니다. 배경의 움직임으로 마치 냥이가 실제로 숲을 걷도록 느끼게 만드는 것이죠. 이제 직접 따라 치면서 상호 작용하는 즐거움을 느껴봅시다 :)
 

HTML 파일

<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>냥이 구조대</title> <!-- 스타일 파일을 불러옵니다 --> <link rel="stylesheet" href="style/style.css"> </head> <body id="cat_ani"> <div class="cont_container"> <div class="cont_viewer"><!-- 애니메이션이 보일 화면입니다 --> <div class="cont_bg"> <div class="bg_sky"></div><!-- 하늘 배경 --> <div class="bg_ground"></div><!-- 땅 배경 --> <div class="bg_forest_far"></div><!-- 먼 숲 배경 --> <div class="bg_forest_near"><!-- 가까운 숲 배경 --> <div class="obj_friends obj_friend1"></div><!-- 첫번째 동물친구 --> <div class="obj_friends obj_friend2"></div><!-- 두번째 동물친구 --> <div class="obj_friends obj_friend3"></div><!-- 세번째 동물친구 --> </div> </div> <div class="obj_cat"></div><!-- 냥이입니다 --> </div> </div> <script src="script/script.js"></script><!-- 스크립트를 불러옵니다 --> </body> </html>
 

CSS 파일

/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) 리셋 CSS 부분입니다. 마이어 CSS를 사용했습니다.*/ 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; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } /* 리셋 CSS 끝 */ .cont_container { display: flex; align-items: center; justify-content: center; width: 100vw; height: 100vh; } .cont_viewer { overflow: hidden; position:relative; width: 60vw; height: 500px; } /* 배경 스타일 시작 */ .cont_bg { position:relative; width: 1800px; height: 500px; background: skyblue; } .bg_sky{ position: absolute; top:0; left:0; width:100%; height: 70%; background:skyblue; } .bg_ground{ position: absolute; bottom:0; left:0; width:100%; height: 20%; background:saddlebrown } .bg_forest_far { position:absolute; top:30px; left:0; width:100%; height:400px; background:url(../image/bg_tree_far.png); background-size: contain; transition: 0.1s } .bg_forest_near { position:absolute; bottom:15px; left:0; width:100%; height:250px; background:url(../image/bg_tree_near.png); background-size: contain; transition: 0.1s } /* 배경 스타일 끝 */ /* 고양이 스타일 시작 */ .obj_cat{ display:block; position:absolute; bottom:15px; left:50%; margin-left:-40px; width:80px; height:109px; background-image:url(../image/cat_sprite.png); background-size: 240px 109px; background-position: 0 0; background-repeat: no-repeat; transform:rotate(0); } /* 걷는 애니메이션 */ @keyframes ani_walk{ 100%{ background-position: -240px 0; } } /* img_walk 클래스에 걷는 애니매이션을 등록합니다 */ .img_walk{ animation: ani_walk 0.3s steps(3) infinite; } /* 고양이 스타일 끝 */ /* 친구 동물들 스타일 시작 */ .obj_friends{ display:block; position:absolute; bottom:0; width:80px; height:109px; background-size: 80px 109px; background-position: 0 0; background-repeat: no-repeat; animation: ani_friends 0.2s alternate infinite; } .obj_friend1{ left:800px; background-image:url(../image/dog.png); } .obj_friend2{ left:1200px; background-image:url(../image/rabbit.png); } .obj_friend3{ left:1700px; background-image:url(../image/sheep.png); } /* 냥이와 친구들이 만났을 때의 애니메이션을 등록하는 클래스입니다 */ .obj_contect{ animation: ani_contect 3s forwards; } /* 친구들의 기본 동작 */ @keyframes ani_friends{ 100%{ transform:translateY(3px); } } /* 냥이가 친구들을 만났을 때의 동작 */ @keyframes ani_contect{ 100%{ transform:translateY(-1000px); } } /* 친구 동물들 스타일 끝 */
 

스크립트 파일

(() => { // DOM을 통해 요소들을 저장합니다 const viewer = document.querySelector('#cat_ani .cont_viewer'); const viewerWidth = viewer.offsetWidth/2; const bgNear = document.querySelector('#cat_ani .bg_forest_near'); const bgFar = document.querySelector('#cat_ani .bg_forest_far'); const objCat = document.querySelector('#cat_ani .obj_cat'); const objFriend1 = document.querySelector('#cat_ani .obj_friend1'); const objFriend2 = document.querySelector('#cat_ani .obj_friend2'); const objFriend3 = document.querySelector('#cat_ani .obj_friend3'); const objFriend1_pos = objFriend1.offsetLeft-viewerWidth; const objFriend2_pos = objFriend2.offsetLeft-viewerWidth; const objFriend3_pos = objFriend3.offsetLeft-viewerWidth; // 배경의 이동 속도를 제어합니다 let speed = 3; let moveNear = 0; let moveFar = 0; // 가까운 배경과 먼 배경의 속도에 차이를 두어 원근감을 줍니다 let speedNear = speed + 5; let speedFar = speed + 1; // 냥이에게 걷는 애니메이션이 들어간 클래스를 부여합니다 const funcSkinChanger = () => { objCat.classList.add('img_walk'); } // 냥이에게 걷는 애니메이션이 들어간 클래스를 제거합니다 const funcSkinRecover = () => { objCat.classList.remove('img_walk'); } // 방향키에 따라 냥이의 몸을 회전시킵니다 const funcDirection = (arrow) => { if (arrow === 'front') { objCat.style.transform = 'rotateY(0)'; } else { objCat.style.transform = 'rotateY(180deg)'; } } // 오른쪽으로 이동할 때의 동작들을 수행하는 함수입니다. 배경을 왼쪽으로 움직여 냥이가 걷는것 처럼 보이게 합니다. const funcBgMoveRight = (near, far) => { moveNear = moveNear - speedNear; moveFar = moveFar - speedFar; near.style.transform = `translateX(${moveNear}px)`; far.style.transform = `translateX(${moveFar}px)`; funcSkinChanger(); funcDirection('front'); } // 왼쪽으로 이동할때의 동작들을 수행하는 함수입니다. 배경을 오른쪽으로 움직여 냥이가 뒤로가는것 처럼 보이게 합니다. const funcBgMoveLeft = (near, far) => { moveNear = moveNear + speedNear; moveFar = moveFar + speedFar; near.style.transform = `translateX(${moveNear}px)`; far.style.transform = `translateX(${moveFar}px)`; funcSkinChanger(); funcDirection('back'); } // 방향키에 따른 동작을 수행하는 함수입니다 const funcBgMove = (e) => { switch (e.keyCode) { case 39: // 우로 이동 if(-(bgNear.style.transform.slice(11, -3))>objFriend1_pos){ objFriend1.classList.add('obj_contect'); } if(-(bgNear.style.transform.slice(11, -3))>objFriend2_pos){ objFriend2.classList.add('obj_contect'); } if(-(bgNear.style.transform.slice(11, -3))>objFriend3_pos){ objFriend3.classList.add('obj_contect'); } if(-(bgNear.style.transform.slice(11, -3)) <= bgNear.offsetWidth){//배경의 넓이보다 더 이동하면 이동 불가 funcBgMoveRight(bgNear, bgFar); } break; case 37: // 좌로 이동 if ((bgNear.style.transform).slice(11, -3) <= 0) { //배경의 좌표가 0보다 작으면 이동 불가 funcBgMoveLeft(bgNear, bgFar); } break; } } // 키를 눌렀을 때를 감지합니다 window.addEventListener('keydown', (e) => { return funcBgMove(e); }); // 키를 때었을 때를 감지합니다. 걷기 애니메이션을 제거합니다. window.addEventListener('keyup', () => { return funcSkinRecover(); }); })();