우선 신기록을 갱신할때마다 알려줘야한다. 그 기능을 구현하고 폭죽을 구현해보자. 그럼 알고리즘을 어떻게 짜면 될까?
맨처음엔 어떻게 생각했냐면 html 에 first ranking 의 innerHtml이 바뀔때 마다 알려주는 API가 없을까 라고 검색하던 도중 발견한게 하나 있다. 바로 MutationObserver
라는 API이다. 근데 사실은, paint함수때문에 게임이 최초 실행될때마다 , 또는 게임이 끝날때 마다 first ranking이 다시 새로쓰여지기 때문에 매번 trigger 될것이다. 따라서 의미 없다.(그래도 유용하게 쓰일지 몰라 나중에 새로알게된 문법에 정리하려고 한다.)
그럼 알고리즘을 짜야한다. 어떻게 짜야할까?
1. 게임을 다 완료한 순간 걸리는 시간을 계산(new record 라고 하자).
{: .notice--success}
2. 이전에 1등했던 기록을 가져옴(old record 라고 하자)).
{: .notice--success}
3. new record - old record 가 0보다 작으면 신기록이라고 하면 된다.
{: .notice--success}
4. 혹시 처음 기록을 쓰는 경우 innerHtml = ''라고 가정하면 된다.
{: .notice--success}
recordToggle.js
updateRecord() {
this.timeRecord = this.aggregateRecord();
this._compareRecord(this.timeRecord);
this._firstRankingDetector(this.timeRecord);
this._paintRecord(this.recordList);
this._saveRecord();
}
_firstRankingDetector(record) {
let firstRankingNumberOnly = this.firstRankingSpan.innerHTML.substring(
5,
7
);
let difference = record - firstRankingNumberOnly;
if (this.firstRankingSpan.innerHTML === '') {
console.log('first record!');
startFireworks();
sound.playfireworksSound();
} else if (difference > 0) {
console.log('not that special');
} else if (difference === 0) {
console.log('same record');
} else if (difference < 0) {
console.log('new record');
startFireworks();
sound.playfireworksSound();
}
}
first record / new record 에서 폭죽이 터지도록 했다. 그럼 폭죽을 구현해보자.
폭죽은 내가 구현하지 못하니 검색을 통해 이미 구현되어있는 것을 가져오기로 했다. 그러던중 좋은것을 발견하였다. 코드도 길지않았고 간단했다.
fireworks.js
const max_fireworks = 10,
max_sparks = 10;
let canvas = document.getElementById("myCanvas");
let context = canvas.getContext("2d");
var fireworkId;
let num = 0;
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight - 5;
let fireworks = [];
function makeFireworks() {
num = 0;
for (let i = 0; i < max_fireworks; i++) {
let firework = {
sparks: [],
};
for (let n = 0; n < max_sparks; n++) {
let spark = {
vx: Math.random() * 5 + 0.5,
vy: Math.random() * 5 + 0.5,
weight: Math.random() * 0.3 + 0.03,
red: Math.floor(Math.random() * 199),
green: Math.floor(Math.random() * 199),
blue: Math.floor(Math.random() * 199),
};
if (Math.random() > 0.5) spark.vx = -spark.vx;
if (Math.random() > 0.5) spark.vy = -spark.vy;
firework.sparks.push(spark);
}
fireworks.push(firework);
resetFirework(firework);
}
}
function resetFirework(firework) {
firework.x = Math.floor(Math.random() * canvas.width);
firework.y = canvas.height;
firework.age = 0;
firework.phase = "fly";
}
function explode() {
num++;
context.clearRect(0, 0, canvas.width, canvas.height);
fireworks.forEach((firework, index) => {
if (firework.phase == "explode") {
firework.sparks.forEach((spark) => {
for (let i = 0; i < 10; i++) {
let trailAge = firework.age + i;
let x = firework.x + spark.vx * trailAge;
let y =
firework.y +
spark.vy * trailAge +
spark.weight * trailAge * spark.weight * trailAge;
let r = Math.floor(spark.red * 2);
let g = Math.floor(spark.green * 2);
let b = Math.floor(spark.blue * 2);
context.beginPath();
context.fillStyle = "rgba(" + r + "," + g + "," + b + ",1)";
context.rect(x, y, 4, 4);
context.fill();
}
});
firework.age++;
if (firework.age > 100 && Math.random() < 0.05) {
resetFirework(firework);
}
} else {
firework.y = firework.y - 10;
for (let spark = 0; spark < 15; spark++) {
context.beginPath();
context.fillStyle = "rgba(" + index * 50 + "," + spark * 17 + ",0,1)";
context.rect(
firework.x + Math.random() * spark - spark / 2,
firework.y + spark * 4,
4,
4
);
context.fill();
}
if (Math.random() < 0.001 || firework.y < 200) firework.phase = "explode";
}
});
fireworkId = window.requestAnimationFrame(explode);
}
makeFireworks();
fireworkId = window.requestAnimationFrame(explode);
캔버스로 구현했다. 굉장히 복잡해보이지만 원리는 간단하다. 일단 makefireworks로 폭죽 스파크를 랜덤으로 만든다. 그리고 explode로 그것을 1초에 60번 표시하면서 애니메이션을 구현해낸다. 폭죽의 갯수와 스파크의 갯수도 조절할 수 있고 색깔또한 조절할 수 있다. 그리고 캔버스의 크기는 css로 조절할 수 없다. 따라서 화면에 꽉차게 하기 위해서 width / height를 위와 같이 window api를 이용해서 지정해줬다. 반응형이다 히히
그럼 html에 myCanvas를 추가하고 css로 z-index를 조절해주자.
<canvas id="myCanvas"></canvas>
#myCanvas {
position: absolute;
z-index: -3;
}
그리고 fireworks를 조금 손봐야한다. 폭죽이 시작되서 중지되기 까지 로직을 짜봤다.
1. 폭죽이 시작되면 z-index가 커지면서 표시가 되게 해야한다.
2. 시간이 지나면서 서서히 없어져야하고 다 없어지면 애니메이션이 중지가 되어야한다.
3. 중지가 되면 z-index가 낮아지면서 게임을 클릭가능하게 해야한다.
폭죽을 수정하는건 다음글에서:)