일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 회고
- 정규표현식
- AWS
- CDN
- go
- graphql
- react
- scrapping
- 포트포워딩
- component
- express
- cicd
- Recoil
- docker
- 반응형웹
- typescript
- 성능최적화
- npx
- Modal
- socket.io
- 웹팩
- styled-component
- route
- sequelize
- 웹크롤링
- javascript animation
- Redux
- Today
- Total
프로그래밍 공부하기
JSAnimation / BrawlStars GunFight! 본문
2일간 BrawlStars GunFight!라는 주제로 간단한 게임을 만들었다. 각 기능과 관련 클래스를 정리해본다.
1. CSS
1) calc
.topbar {
--widthA: 100px;
--widthB: calc(var(--widthA) / 2);
width: var(--widthB);
}
height: calc(100% - 50px);
css에서도 간단한 사칙연산을 수행할 수 있다. 단위가 맞지 않아도 사용 가능하며, var와 중첩하여 사용 가능하다. 위의 영상이 과거에 찍은 것이라 topbar의 크기만큼 스크롤이 생겨있는 모습을 볼 수 있는데 최근 calc를 활용하여 해결하였다.
2) animation
.enemy {
animation-name: move;
animation-duration: 12s; /*애니메이션 수행에 걸리는 시간*/
animation-iteration-count: infinite; /*애니메이션 무한반복*/
animation-direction: alternate; /*애니메이션 완료 시 다음에는 반대방향으로 수행*/
}
@keyframes move { /*from-to 혹은 0%-50%-100%... 로 단계를 설정 후 동작을 정의한다.*/
from { margin-left: 90%; }
to { margin-left: 0%; }
}
css에서 HTML 요소에 애니메이션을 넣을 수 있다. .enemy에 animation의 이름과 애니메이션 수행과 관련된 설정을을 넣은 뒤 .enemy 바깥에서 keyframse 키워드를 사용하여 애니메이션동작과 이름을 정의한다. animation은 animation-name을 입력할 때 ,(컴마)를 통해 여러개를 넣을 수도 있다.
3) Absolute
<div class="Field">
<img class="Hero" src="images/Hero.png" />
</div>
.Hero {
position:absolute;
bottom: 0;
right: 0;
width: 21rem;
}
요소를 정렬할 때 absolute를 사용할 수 있다. absolute는 position: static이 아닌 부모를 기준으로 움직인다. 즉 부모 중 position: relative 혹은 absolute 혹은 fixed인 태그 중 가장 가까운 요소를 기준으로 배치하는 것이다. 부모 중에 그러한 요소가 없다면 가장 상위태그(body) 기준으로 정렬한다. bottom: 0이라는 것은 하단으로부터 0 떨어진 곳에 붙인다는 것으로 하단에 딱 붙여 넣겠다는 의미이며, right: 0이라는 것도 마찬가지로 우측에 딱 붙여서 넣겠다는 의미이다.
2. JS
1. 브롤러추가
우측 상단의 브롤러추가를 선택하면 브롤러들이 나와서 좌우로 움직인다. 먼저 DancerClass(브롤러)클래스와 주요 메소드는 다음과 같다.
const enemisImg = ['images/Enemy_1.png', 'images/Enemy_2.png', 'images/Enemy_3.png'];
const createDancerElement = () => {
let dancer = document.createElement('img');
dancer.setAttribute("src", enemisImg[parseInt(Math.random() * 3)]); //랜덤 이미지
dancer.setAttribute("height", 150);
dancer.setAttribute("width", 150);
dancer.className = 'enemy';
dancer.addEventListener("click", function (e) { //브롤러 선택시
e.stopPropagation();
user.hitEnemy(true);
dancer.setAttribute("src", "images/explosion.gif"); // 폭발하기
setTimeout(dancer.remove.bind(this), 3000); //3초 후 브롤러 제거
})
return dancer;
}
class DancerClass {
constructor(top, left, timeBetweenSteps) {
this.$node = createDancerElement();
this.timeBetweenSteps = timeBetweenSteps;
//this.step();
this.setPosition(top, left);
}
setPosition(top, left) {
Object.assign(this.$node.style, {
top: `${top}px`,
left: `${left}px`
});
}
render(target) {
target.appendChild(this.$node);
}
}
DancerClass는 생성 시 createDancerElement() 함수를 사용하여 자신을 나타내는 HTML 요소인 $node를 만든다. $node를 만들 때 자신(브롤러)이 클릭되면 어떤 행동을 할지도 포함되어 있다(폭발 후 제거). 이 때 field 영역에 있는 $node가 클릭되면 이벤트 버블링에 의해 field에도 click 이벤트가 추가되므로 이를 막기 위해 propagation()함수를 사용하였다. 그 후 브롤러 소환할 때마다 새로운 DancerClass 인스턴스가 생성되며 render 함수를 통해 field 영역에 추가된다.
2. 스코어링 및 궁극기 사용 처리
스코어링 및 궁극기 사용을 위해 User 클래스를 만들어 이들을 관리하며 fieldArea와 브롤러에 Click이벤트를 달고 UserClass의 hitEnemy() 함수를 호출하도록 설계하였다.
사용자가 브롤러를 클릭했을 때 빗나간 경우와 명중한 경우 두 가지 케이스로 나뉜다. 빗나간 경우는 fieldArea에 클릭 이벤트가 발생한 경우이며 이 경우 유저클래스의 hitEnemy(false)를 호출한다. 명중한 경우는 위의 브롤러추가에서 설명하였듯이 브롤러 $node를 생성할 때 처리되며 유저클래스의 hitEnemy(true)를 호출한다.
//init.js: 빗나간 경우
fieldArea.addEventListener("click", (e) => {
user.hitEnemy(false);
});
//dancerClass.js: 명중한 경우
const createDancerElement = () => {
let dancer = document.createElement('img');
dancer.setAttribute("src", enemisImg[parseInt(Math.random() * 3)]); //랜덤 이미지
//...생략
dancer.addEventListener("click", function (e) { //브롤러 클릭시
e.stopPropagation();
user.hitEnemy(true);
dancer.setAttribute("src", "images/explosion.gif"); // 폭발하기
setTimeout(dancer.remove.bind(this), 3000); //3초 후 브롤러 제거
})
return dancer;
}
스코어링 및 궁극기 관리를 위한 User 클래스는 다음과 같다.
if (typeof window === 'undefined') {
var jsdom = require('jsdom');
var { JSDOM } = jsdom;
var { document } = (new JSDOM('')).window;
}
class User {
constructor() {
this.score = 0;
this.specialSkillState = false;
this.hitcount = 0;
}
plustScore() {
this.score += 10;
document.querySelector("#Point").textContent = this.score;
}
activeSpecialSkillState(isActive) {
this.specialSkillState = isActive;
let img = document.querySelector("#SkillImg");
if (isActive) {
img.setAttribute("src", "images/SpecialAttack_active.png");
} else {
img.setAttribute("src", "images/SpecialAttack_non_active.png")
}
}
useSpecialSkill() {
if (this.specialSkillState) {
let enemyList = document.querySelectorAll(".enemy"); //필드상의 모든 브롤러 리스트
enemyList.forEach((element) => {
this.plustScore(); //점수추가
element.click(); //클릭이벤트 발생(-> 폭발 -> 제거)
})
this.activeSpecialSkillState(false); //궁극기 상태 비활성화
}
}
hitEnemy(isHit) {
if (isHit) {
this.hitcount++;
this.plustScore();
console.log(this.hitcount);
if (this.hitcount >= 3) {
this.activeSpecialSkillState(true);
}
} else {
this.hitcount = 0;
}
}
}
JS는 비교적 빨리 끝났는데 CSS에 시간을 많이 소모하였다. 스크롤도 그렇고, Absolute로 요소를 우측하단에 놓는 것도 그렇고 보기에 별 건 아닌데 해본 적이 없어서 내 상황에 맞는 최적의 방법을 찾고 적용하는 것이 쉽지 않았다, CSS 경험을 많이 해봐야할 것 같다.
'Project > Practice' 카테고리의 다른 글
TypeScript / Project Timer (0) | 2021.04.05 |
---|---|
GraphQL / Tour Sight Search (0) | 2021.02.21 |
socket.io / 실시간 채팅 (0) | 2021.02.09 |
DOM / Twittller (0) | 2020.12.30 |
JS / 계산기 (0) | 2020.12.17 |