From a30aa16691c85a5be4b81398f2d1566519b6557b Mon Sep 17 00:00:00 2001 From: minislon <151672858+minislon@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:30:57 +0300 Subject: [PATCH] feat: replace tetris with simple browser counter UI --- index.html | 17 +++- script.js | 247 +++++------------------------------------------------ style.css | 90 ++++++++++++++++--- 3 files changed, 114 insertions(+), 240 deletions(-) diff --git a/index.html b/index.html index f131de6..b9c5d0b 100644 --- a/index.html +++ b/index.html @@ -1,14 +1,23 @@ - + - Tetris + Солнечный счётчик - -
0
+
+

Простой браузерный счётчик

+

0

+ +
+ + + +
+
+ diff --git a/script.js b/script.js index 2b20d00..a018f77 100644 --- a/script.js +++ b/script.js @@ -1,233 +1,30 @@ -const canvas = document.getElementById('board'); -const context = canvas.getContext('2d'); -const scoreElem = document.getElementById('score'); +const counterValue = document.getElementById('counterValue'); +const increaseBtn = document.getElementById('increaseBtn'); +const decreaseBtn = document.getElementById('decreaseBtn'); +const resetBtn = document.getElementById('resetBtn'); -const scale = 24; -context.scale(scale, scale); +let value = 0; -function arenaSweep() { - outer: for (let y = arena.length - 1; y > 0; --y) { - for (let x = 0; x < arena[y].length; ++x) { - if (arena[y][x] === 0) { - continue outer; - } - } - const row = arena.splice(y, 1)[0].fill(0); - arena.unshift(row); - ++y; - player.score += 10; - } +function renderCounter() { + counterValue.textContent = value; + counterValue.classList.remove('counter__value--pulse'); + void counterValue.offsetWidth; + counterValue.classList.add('counter__value--pulse'); } -function collide(arena, player) { - const [m, o] = [player.matrix, player.pos]; - for (let y = 0; y < m.length; ++y) { - for (let x = 0; x < m[y].length; ++x) { - if (m[y][x] !== 0 && - (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) { - return true; - } - } - } - return false; -} - -function createMatrix(w, h) { - const matrix = []; - while (h--) { - matrix.push(new Array(w).fill(0)); - } - return matrix; -} - -function createPiece(type) { - switch (type) { - case 'T': - return [ - [0, 0, 0], - [1, 1, 1], - [0, 1, 0], - ]; - case 'O': - return [ - [2, 2], - [2, 2], - ]; - case 'L': - return [ - [0, 3, 0], - [0, 3, 0], - [0, 3, 3], - ]; - case 'J': - return [ - [0, 4, 0], - [0, 4, 0], - [4, 4, 0], - ]; - case 'I': - return [ - [0, 5, 0, 0], - [0, 5, 0, 0], - [0, 5, 0, 0], - [0, 5, 0, 0], - ]; - case 'S': - return [ - [0, 6, 6], - [6, 6, 0], - [0, 0, 0], - ]; - case 'Z': - return [ - [7, 7, 0], - [0, 7, 7], - [0, 0, 0], - ]; - default: - return []; - } -} - -function drawMatrix(matrix, offset) { - matrix.forEach((row, y) => { - row.forEach((value, x) => { - if (value !== 0) { - context.fillStyle = colors[value]; - context.fillRect(x + offset.x, y + offset.y, 1, 1); - } - }); - }); -} - -function draw() { - context.fillStyle = '#000'; - context.fillRect(0, 0, canvas.width, canvas.height); - drawMatrix(arena, { x: 0, y: 0 }); - drawMatrix(player.matrix, player.pos); -} - -function merge(arena, player) { - player.matrix.forEach((row, y) => { - row.forEach((value, x) => { - if (value !== 0) { - arena[y + player.pos.y][x + player.pos.x] = value; - } - }); - }); -} - -function playerDrop() { - player.pos.y++; - if (collide(arena, player)) { - player.pos.y--; - merge(arena, player); - playerReset(); - arenaSweep(); - updateScore(); - } - dropCounter = 0; -} - -function playerMove(dir) { - player.pos.x += dir; - if (collide(arena, player)) { - player.pos.x -= dir; - } -} - -function playerReset() { - const pieces = 'ILJOTSZ'; - player.matrix = createPiece(pieces[(pieces.length * Math.random()) | 0]); - player.pos.y = 0; - player.pos.x = ((arena[0].length / 2) | 0) - - ((player.matrix[0].length / 2) | 0); - if (collide(arena, player)) { - arena.forEach(row => row.fill(0)); - player.score = 0; - updateScore(); - } -} - -function rotate(matrix, dir) { - for (let y = 0; y < matrix.length; ++y) { - for (let x = 0; x < y; ++x) { - [matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]]; - } - } - if (dir > 0) { - matrix.forEach(row => row.reverse()); - } else { - matrix.reverse(); - } -} - -function playerRotate(dir) { - const pos = player.pos.x; - let offset = 1; - rotate(player.matrix, dir); - while (collide(arena, player)) { - player.pos.x += offset; - offset = -(offset + (offset > 0 ? 1 : -1)); - if (offset > player.matrix[0].length) { - rotate(player.matrix, -dir); - player.pos.x = pos; - return; - } - } -} - -let dropCounter = 0; -let dropInterval = 1000; - -let lastTime = 0; -function update(time = 0) { - const deltaTime = time - lastTime; - lastTime = time; - dropCounter += deltaTime; - if (dropCounter > dropInterval) { - playerDrop(); - } - draw(); - requestAnimationFrame(update); -} - -function updateScore() { - scoreElem.innerText = player.score; -} - -const colors = [ - null, - '#FF0D72', - '#0DC2FF', - '#0DFF72', - '#F538FF', - '#FF8E0D', - '#FFE138', - '#3877FF', -]; +increaseBtn.addEventListener('click', () => { + value += 1; + renderCounter(); +}); -const arena = createMatrix(10, 20); -const player = { - pos: { x: 0, y: 0 }, - matrix: null, - score: 0, -}; +decreaseBtn.addEventListener('click', () => { + value -= 1; + renderCounter(); +}); -document.addEventListener('keydown', event => { - if (event.key === 'ArrowLeft') { - playerMove(-1); - } else if (event.key === 'ArrowRight') { - playerMove(1); - } else if (event.key === 'ArrowDown') { - playerDrop(); - } else if (event.key === 'q') { - playerRotate(-1); - } else if (event.key === 'w') { - playerRotate(1); - } +resetBtn.addEventListener('click', () => { + value = 0; + renderCounter(); }); -playerReset(); -updateScore(); -update(); +renderCounter(); diff --git a/style.css b/style.css index 1508695..8d07116 100644 --- a/style.css +++ b/style.css @@ -1,18 +1,86 @@ +:root { + --bg-start: #f4f2ee; + --bg-end: #e4ddcf; + --card: #fefcf6; + --text-main: #5c5951; + --text-soft: #848076; + --accent: #d8b75f; + --accent-hover: #cba84a; + --shadow: rgba(125, 113, 86, 0.2); +} + +* { + box-sizing: border-box; +} + body { + margin: 0; + min-height: 100vh; + display: grid; + place-items: center; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + color: var(--text-main); + background: radial-gradient(circle at top, #fff9e9 0%, var(--bg-start) 45%, var(--bg-end) 100%); +} + +.counter { + width: min(92vw, 420px); + padding: 2rem; + border-radius: 20px; + text-align: center; + background: linear-gradient(180deg, #fffef9, var(--card)); + box-shadow: 0 18px 45px -20px var(--shadow); +} + +.counter__title { + margin: 0; + font-size: 1.4rem; + color: var(--text-soft); +} + +.counter__value { + margin: 1.2rem 0; + font-size: 4rem; + font-weight: 700; + color: var(--text-main); + transition: transform 220ms ease, color 220ms ease; +} + +.counter__value--pulse { + transform: scale(1.08); + color: #76673f; +} + +.counter__controls { display: flex; - flex-direction: column; - align-items: center; - margin-top: 20px; - background: #111; - color: #fff; - font-family: sans-serif; + gap: 0.7rem; + justify-content: center; +} + +.counter__button { + border: none; + border-radius: 12px; + min-width: 64px; + padding: 0.8rem 1rem; + font-size: 1.2rem; + font-weight: 600; + cursor: pointer; + color: #4f4a3d; + background: linear-gradient(180deg, #f7dc97, var(--accent)); + transition: transform 170ms ease, box-shadow 170ms ease, background 170ms ease; +} + +.counter__button:hover { + transform: translateY(-1px); + background: linear-gradient(180deg, #f3d487, var(--accent-hover)); + box-shadow: 0 10px 18px -12px var(--shadow); } -#board { - border: 2px solid #555; - background: #000; +.counter__button:active { + transform: translateY(1px); } -#score { - margin-top: 10px; +.counter__button--reset { + min-width: 96px; + background: linear-gradient(180deg, #e6e1d6, #d6cec0); }