When i change a frame size, like 3×3, 4×4, 5×5 etc. canvas draw n number of times constantly calculation in progression. I can’t figure out where the error might be. Can you help me fix this bug. I suspect that the problem is in the function call in the class, or in one of the functions.
Code snippet and screenshot picture i attached
const defaultFrame = 4;
class GeneratePage {
constructor() {
this.element = `
<div class="wrapper">
<div class="control">
<button type="button" class="btn btn__start"><span>Shuffle and start</span></button>
<button type="button" class="btn btn__stop"><span>Stop</span></button>
<button type="button" class="btn btn__save"><span>Save</span></button>
<button type="button" class="btn btn__results"><span>Results</span></button>
</div>
<div class="info">
<p class="info__moves">Move: <span class="info__step">0</span></p>
<p class="info__time">Time: <span class="info__timepass">00:00</span></p>
</div>
<div class="frame">
<p class="frame__title">Frame: <span class="frame__selection">4x4</span></p>
<div class="frame__wrapper">
<p class="frame__select">Choose frame size:</p>
<select class="frame__list" name="frames">
<option class="frame__option" value="3">3x3</option>
<option class="frame__option" value="4" selected>4x4</option>
<option class="frame__option" value="5">5x5</option>
<option class="frame__option" value="6">6x6</option>
<option class="frame__option" value="7">7x7</option>
<option class="frame__option" value="8">8x8</option>
</select>
</div>
</div>
</div>
</div>
`;
this.generate();
}
generate = () => {
document.body.innerHTML = this.element;
};
}
class Game {
constructor(context, cellSize, framesize) {
this.nowstate = framesize;
console.log(this.nowstate);
this.list = document.querySelector('.frame__list');
this.selection = document.querySelector('.frame__selection');
this.color = '#008aff';
this.context = context;
this.cellSize = cellSize;
this.clicks = 0;
this.state = Game.checkSize(this.nowstate);
this.eventHandlers();
}
eventHandlers = () => {
this.list.addEventListener('change', (e) => this.changeSize(e));
};
changeSize = (e) => {
e.preventDefault();
const nowstate = e.target.value;
this.selection.textContent = `${nowstate}x${nowstate}`;
Game.createPuzzle(nowstate);
};
static getClicks() {
return this.clicks;
}
static checkSize = (n) => {
const matrix = [];
for (let i = 0; i < n; i += 1) {
matrix.push([]);
}
for (let i = 0; i < n; i += 1) {
for (let j = 0; j < n; j += 1) {
matrix[i][j] = n * i + j + 1;
}
}
matrix[n - 1][n - 1] = 0;
return matrix;
};
cellView = (x, y) => {
this.context.fillStyle = this.color;
this.context.fillRect(
x + 1,
y + 1,
this.cellSize - 2,
this.cellSize - 2,
);
};
numView = () => {
this.context.font = '1.8em Montserrat';
this.context.textAlign = 'center';
this.context.textBaseline = 'middle';
this.context.fillStyle = '#000';
};
draw = () => {
for (let i = 0; i < this.nowstate; i += 1) {
for (let j = 0; j < this.nowstate; j += 1) {
if (this.state[i][j] > 0) {
this.cellView(
j * this.cellSize,
i * this.cellSize,
);
this.numView();
this.context.fillText(
this.state[i][j],
j * this.cellSize + this.cellSize / 2,
i * this.cellSize + this.cellSize / 2,
);
}
}
}
};
getNullCell = () => {
for (let i = 0; i < this.nowstate; i += 1) {
for (let j = 0; j < this.nowstate; j += 1) {
if (this.state[j][i] === 0) {
return { x: i, y: j };
}
}
}
return false;
};
move = (x, y) => {
const nullCell = this.getNullCell();
const canMoveVertical = (x - 1 === nullCell.x || x + 1 === nullCell.x) && y === nullCell.y;
const canMoveHorizontal = (y - 1 === nullCell.y || y + 1 === nullCell.y) && x === nullCell.x;
if (canMoveVertical || canMoveHorizontal) {
this.state[nullCell.y][nullCell.x] = this.state[y][x];
this.state[y][x] = 0;
this.clicks += 1;
}
};
victory = () => {
const combination = this.checkSize(this.nowstate);
let res = true;
for (let i = 0; i < this.nowstate; i += 1) {
for (let j = 0; j < this.nowstate; i += 1) {
if (combination[i][j] !== this.state[i][j]) {
res = false;
break;
}
}
}
return res;
};
static getRandomBool = () => {
if (Math.floor(Math.random() * 2) === 0) {
return true;
}
return false;
};
mix = (count, n) => {
let x;
let y;
for (let i = 0; i < count; i += 1) {
const nullCell = this.getNullCell();
const verticalMove = Game.getRandomBool();
const upLeft = Game.getRandomBool();
if (verticalMove) {
x = nullCell.x;
if (upLeft) {
y = nullCell.y - 1;
} else {
y = nullCell.y + 1;
}
} else {
y = nullCell.y;
if (upLeft) {
x = nullCell.x - 1;
} else {
x = nullCell.x + 1;
}
}
if (x >= 0 && x <= n - 1 && y >= 0 && y <= n - 1) {
this.move(x, y);
}
}
this.clicks = 0;
};
static createPuzzle = (n) => {
let canvas = document.getElementById('puzzle');
if (canvas) {
canvas.remove();
console.log('remove');
}
const wrapper = document.querySelector('.wrapper');
canvas = document.createElement('canvas');
canvas.setAttribute('id', 'puzzle');
canvas.classList.add('game');
console.log(canvas);
wrapper.insertBefore(canvas, document.querySelector('.frame'));
// canvas = document.getElementById('puzzle');
canvas.width = 280;
canvas.height = 280;
const context = canvas.getContext('2d');
context.fillRect(0, 0, canvas.width, canvas.height);
const cellSize = canvas.width / n;
const game = new Game(context, cellSize, n);
game.mix(300, n);
game.draw();
};
}
class Application {
constructor() {
new GeneratePage;
Game.createPuzzle(defaultFrame);
}
}
new Application();/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:rgba(0,0,0,0)}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline;-webkit-text-decoration:underline dotted currentColor;text-decoration:underline dotted currentColor}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
@font-face{font-family:"Montserrat";src:url(fonts/Montserrat-Light.ttf) format("truetype");font-weight:300;font-style:normal;font-display:swap}@font-face{font-family:"Montserrat";src:url(fonts/Montserrat-Regular.ttf) format("truetype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:"Montserrat";src:url(fonts/Montserrat-Medium.ttf) format("truetype");font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:"Montserrat";src:url(fonts/Montserrat-Bold.ttf) format("truetype");font-weight:700;font-style:normal;font-display:swap}.btn{background-image:linear-gradient(135deg, #008aff, #86d472);border-radius:6px;box-sizing:border-box;color:#fff;display:block;height:50px;font-size:1em;font-weight:700;padding:4px;position:relative;text-decoration:none;width:7em;z-index:2;border:none;outline:none;cursor:pointer}.btn span{align-items:center;background:#0e0e10;border-radius:6px;display:flex;justify-content:center;height:100%;transition:background .5s ease;width:100%}.btn span:hover{background:rgba(0,0,0,0)}.btn:hover{color:#fff}html{scroll-behavior:smooth}*{padding:0;margin:0;box-sizing:border-box}body{font-family:"Montserrat",sans-serif;display:flex;flex-direction:column;align-items:center;padding:40px 20px 20px 20px}.wrapper{margin:0 auto;display:flex;flex-direction:column;align-items:center}.control{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}.info{display:flex;gap:50px;margin-top:15px;font-weight:500;font-size:1.3rem}.game{margin-top:15px;border:1px solid #000}.frame{margin-top:15px;display:flex;flex-direction:column;align-items:center}.frame__title{font-weight:400;font-size:1.1rem}.frame__wrapper{margin-top:15px;display:flex;align-items:baseline}.frame__list{border:none;outline:none;max-width:100%;font-size:1.1rem;font-weight:400;padding:8px 10px 8px 10px;background-color:rgba(0,0,0,0);cursor:pointer}.frame__list:active,.frame__list:focus{outline:none;box-shadow:none}.frame__select{display:inline;margin-right:10px;font-size:1.1rem;font-weight:400}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="gem puzzle game">
<meta name="keywords" content="rsschool puzzle">
<title>Gem Puzzle</title>
</head>
<body>
</body>
</html>Advertisement
Answer
Because you don’t deleting the event from frame__list.
You can delete event by using EventTarget.removeEventListener()
Or
By replacing node box.replaceWith(box.cloneNode(true));
