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));