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