From 50c29c1743ee3611a8334dafca1b85728d68c258 Mon Sep 17 00:00:00 2001
From: kg0816 <211191696+kg0816@users.noreply.github.com>
Date: Wed, 12 Nov 2025 13:48:37 +0900
Subject: [PATCH 1/5] perf : optimize board rendering with DOM diffing
---
src/iframe/life-game.js | 79 ++++++++++++++++++++++++-----------------
src/routes/+page.svelte | 4 +--
2 files changed, 49 insertions(+), 34 deletions(-)
diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js
index 8ce2604..8493a98 100644
--- a/src/iframe/life-game.js
+++ b/src/iframe/life-game.js
@@ -30,41 +30,56 @@ function isNextAlive(around, self) {
//Boardの初期化
let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
const table = document.getElementById("game-board");
+//盤面をBoardに従って変更する関数(Boardを変更したら必ず実行する)
function renderBoard() {
- //盤面をBoardに従って変更する関数(Boardを変更したら必ず実行する)
- table.innerHTML = "";
- for (let i = 0; i < boardSize; i++) {
- const tr = document.createElement("tr");
- tr.style.padding = "0";
- for (let j = 0; j < boardSize; j++) {
- const td = document.createElement("td");
- td.style.padding = "0";
- const button = document.createElement("button");
- button.style.backgroundColor = board[i][j] ? "black" : "white"; //Boardの対応する値によって色を変更
- button.style.border = "0.5px solid black";
- button.style.width = `${cellSize}px`;
- button.style.height = `${cellSize}px`;
- button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ
- button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ
- button.onmousedown = (e) => {
- e.preventDefault();
- if (timer === "stop") {
- isDragging = true;
- board[i][j] = !board[i][j];
- dragMode = board[i][j];
- button.style.backgroundColor = board[i][j] ? "black" : "white";
- }
- };
- button.onmouseenter = () => {
- if (isDragging && timer === "stop" && board[i][j] !== dragMode) {
- board[i][j] = dragMode;
- button.style.backgroundColor = board[i][j] ? "black" : "white";
+ // 初回の処理
+ if (table.children.length === 0) {
+ table.innerHTML = "";
+ for (let i = 0; i < boardSize; i++) {
+ const tr = document.createElement("tr");
+ tr.style.padding = "0";
+ for (let j = 0; j < boardSize; j++) {
+ const td = document.createElement("td");
+ td.style.padding = "0";
+ const button = document.createElement("button");
+ button.style.backgroundColor = board[i][j] ? "black" : "white"; //Boardの対応する値によって色を変更
+ button.style.border = "0.5px solid black";
+ button.style.width = `${cellSize}px`;
+ button.style.height = `${cellSize}px`;
+ button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ
+ button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ
+ button.onmousedown = (e) => {
+ e.preventDefault();
+ if (timer === "stop") {
+ isDragging = true;
+ board[i][j] = !board[i][j];
+ dragMode = board[i][j];
+ button.style.backgroundColor = board[i][j] ? "black" : "white";
+ }
+ };
+ button.onmouseenter = () => {
+ if (isDragging && timer === "stop" && board[i][j] !== dragMode) {
+ board[i][j] = dragMode;
+ button.style.backgroundColor = board[i][j] ? "black" : "white";
+ }
+ };
+ td.appendChild(button);
+ tr.appendChild(td);
+ }
+ table.appendChild(tr);
+ }
+ } else {
+ // 2回目以降の処理
+ for (let i = 0; i < boardSize; i++) {
+ for (let j = 0; j < boardSize; j++) {
+ const button = table.children[i].children[j].children[0];
+ const shouldBeBlack = board[i][j];
+ const isBlack = button.style.backgroundColor === "black";
+ if (shouldBeBlack !== isBlack) {
+ button.style.backgroundColor = shouldBeBlack ? "black" : "white";
}
- };
- td.appendChild(button);
- tr.appendChild(td);
+ }
}
- table.appendChild(tr);
}
}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index f2ec61f..a712ccb 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -148,7 +148,7 @@
@@ -329,7 +329,7 @@
srcdoc={previewDoc}
title="Preview"
sandbox="allow-scripts"
- class="w-[80%] h-[90%] rounded-lg mx-auto my-5 shadow-lg"
+ class="w-[80%] h-[90%] rounded-lg mx-auto my-5 bg-white shadow-lg"
onload={() => {
setTimeout(() => {
sendEvent("state_update");
From 4e9226384d2129c8d2d114fa0402ffda745556d7 Mon Sep 17 00:00:00 2001
From: kg0816 <211191696+kg0816@users.noreply.github.com>
Date: Wed, 12 Nov 2025 14:15:52 +0900
Subject: [PATCH 2/5] fix: apply cellSize calculation when code is reloaded
---
src/iframe/life-game.js | 22 +++++++++++-----------
src/routes/+page.svelte | 4 +---
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js
index 8493a98..bbd8cd8 100644
--- a/src/iframe/life-game.js
+++ b/src/iframe/life-game.js
@@ -11,8 +11,8 @@ const DEFAULT_BOARD_SIZE = 20;
const DEFAULT_CELL_SIZE = 30;
//変数設定
-let boardSize = 20;
-let cellSize = 30;
+let boardSize = 20; //盤面の大きさ(20x20)
+let cellScale = 1.0; //セルの大きさの倍率
// around: 周囲の生きたセル数 self: 自身が生きているかどうか
function isNextAlive(around, self) {
@@ -32,6 +32,7 @@ let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSi
const table = document.getElementById("game-board");
//盤面をBoardに従って変更する関数(Boardを変更したら必ず実行する)
function renderBoard() {
+ const cellSize = Math.floor(cellScale * DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / boardSize));
// 初回の処理
if (table.children.length === 0) {
table.innerHTML = "";
@@ -73,11 +74,19 @@ function renderBoard() {
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
const button = table.children[i].children[j].children[0];
+
const shouldBeBlack = board[i][j];
const isBlack = button.style.backgroundColor === "black";
if (shouldBeBlack !== isBlack) {
button.style.backgroundColor = shouldBeBlack ? "black" : "white";
}
+
+ const currentCellsize = button.style.width;
+ const expectedCellsize = `${cellSize}px`;
+ if (currentCellsize !== expectedCellsize) {
+ button.style.width = expectedCellsize;
+ button.style.height = expectedCellsize;
+ }
}
}
}
@@ -162,15 +171,6 @@ on.pause = () => {
resetTimer();
};
-on.board_resize = (newSize) => {
- boardSize = newSize;
- cellSize = Math.floor(DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / newSize));
- board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
- renderBoard();
- generationChange(0);
- resetTimer();
-};
-
on.board_reset = () => {
//すべて白にBoardを変更
board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index a712ccb..07217a7 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -42,9 +42,7 @@
| "place_template"
| "save_board"
| "apply_board"
- | "request_sync"
- // unused events
- | "board_resize";
+ | "request_sync";
type IncomingEvent = "generation_change" | "sync" | "save_board";
From 0e3dd99a25241576cecef0070d0bbea472f97589 Mon Sep 17 00:00:00 2001
From: kg0816 <211191696+kg0816@users.noreply.github.com>
Date: Thu, 13 Nov 2025 10:57:03 +0900
Subject: [PATCH 3/5] add rerender function
---
src/iframe/life-game.js | 118 +++++++++++++++++++++-------------------
1 file changed, 63 insertions(+), 55 deletions(-)
diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js
index bbd8cd8..b9917f2 100644
--- a/src/iframe/life-game.js
+++ b/src/iframe/life-game.js
@@ -14,6 +14,8 @@ const DEFAULT_CELL_SIZE = 30;
let boardSize = 20; //盤面の大きさ(20x20)
let cellScale = 1.0; //セルの大きさの倍率
+let cellSize = Math.floor(cellScale * DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / boardSize)); //セルの大きさ(px)
+
// around: 周囲の生きたセル数 self: 自身が生きているかどうか
function isNextAlive(around, self) {
// 自身が生きている & 周囲が 2 か 3 で生存
@@ -30,63 +32,65 @@ function isNextAlive(around, self) {
//Boardの初期化
let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
const table = document.getElementById("game-board");
-//盤面をBoardに従って変更する関数(Boardを変更したら必ず実行する)
+
+//盤面をBoardに従って変更する関数達(Boardを変更したら実行する)
function renderBoard() {
- const cellSize = Math.floor(cellScale * DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / boardSize));
- // 初回の処理
- if (table.children.length === 0) {
- table.innerHTML = "";
- for (let i = 0; i < boardSize; i++) {
- const tr = document.createElement("tr");
- tr.style.padding = "0";
- for (let j = 0; j < boardSize; j++) {
- const td = document.createElement("td");
- td.style.padding = "0";
- const button = document.createElement("button");
- button.style.backgroundColor = board[i][j] ? "black" : "white"; //Boardの対応する値によって色を変更
- button.style.border = "0.5px solid black";
- button.style.width = `${cellSize}px`;
- button.style.height = `${cellSize}px`;
- button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ
- button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ
- button.onmousedown = (e) => {
- e.preventDefault();
- if (timer === "stop") {
- isDragging = true;
- board[i][j] = !board[i][j];
- dragMode = board[i][j];
- button.style.backgroundColor = board[i][j] ? "black" : "white";
- }
- };
- button.onmouseenter = () => {
- if (isDragging && timer === "stop" && board[i][j] !== dragMode) {
- board[i][j] = dragMode;
- button.style.backgroundColor = board[i][j] ? "black" : "white";
- }
- };
- td.appendChild(button);
- tr.appendChild(td);
- }
- table.appendChild(tr);
- }
- } else {
- // 2回目以降の処理
- for (let i = 0; i < boardSize; i++) {
- for (let j = 0; j < boardSize; j++) {
- const button = table.children[i].children[j].children[0];
-
- const shouldBeBlack = board[i][j];
- const isBlack = button.style.backgroundColor === "black";
- if (shouldBeBlack !== isBlack) {
- button.style.backgroundColor = shouldBeBlack ? "black" : "white";
+ // 初回の盤面生成
+ table.innerHTML = "";
+ for (let i = 0; i < boardSize; i++) {
+ const tr = document.createElement("tr");
+ tr.style.padding = "0";
+ for (let j = 0; j < boardSize; j++) {
+ const td = document.createElement("td");
+ td.style.padding = "0";
+ const button = document.createElement("button");
+ button.style.backgroundColor = board[i][j] ? "black" : "white"; //Boardの対応する値によって色を変更
+ button.style.border = "0.5px solid black";
+ button.style.width = `${cellSize}px`;
+ button.style.height = `${cellSize}px`;
+ button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ
+ button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ
+ button.onmousedown = (e) => {
+ e.preventDefault();
+ if (timer === "stop") {
+ isDragging = true;
+ board[i][j] = !board[i][j];
+ dragMode = board[i][j];
+ button.style.backgroundColor = board[i][j] ? "black" : "white";
}
-
- const currentCellsize = button.style.width;
- const expectedCellsize = `${cellSize}px`;
- if (currentCellsize !== expectedCellsize) {
- button.style.width = expectedCellsize;
- button.style.height = expectedCellsize;
+ };
+ button.onmouseenter = () => {
+ if (isDragging && timer === "stop" && board[i][j] !== dragMode) {
+ board[i][j] = dragMode;
+ button.style.backgroundColor = board[i][j] ? "black" : "white";
}
+ };
+ td.appendChild(button);
+ tr.appendChild(td);
+ }
+ table.appendChild(tr);
+ }
+}
+
+function rerender() {
+ // 2回目以降の盤面生成
+ for (let i = 0; i < boardSize; i++) {
+ for (let j = 0; j < boardSize; j++) {
+ const button = table.children[i].children[j].children[0];
+
+ // 色の更新
+ const currentCellColor = button.style.backgroundColor;
+ const expectedCellColor = getStyle(board[i][j]);
+ if (currentCellColor !== expectedCellColor) {
+ button.style.backgroundColor = expectedCellColor;
+ }
+
+ // セルサイズの更新
+ const currentCellsize = button.style.width;
+ const expectedCellsize = `${cellSize}px`;
+ if (currentCellsize !== expectedCellsize) {
+ button.style.width = expectedCellsize;
+ button.style.height = expectedCellsize;
}
}
}
@@ -96,6 +100,10 @@ document.addEventListener("mouseup", () => {
isDragging = false;
});
+function getStyle(cell) {
+ return cell ? "black" : "white";
+}
+
renderBoard();
progressBoard();
@@ -150,7 +158,7 @@ function progressBoard() {
}
board = newBoard;
generationChange(generationFigure + 1);
- renderBoard();
+ rerender();
}
function resetTimer() {
From 0e0eee78fc7c6bcadfe4eb8fbaf9b68e211ce4a9 Mon Sep 17 00:00:00 2001
From: kg0816 <211191696+kg0816@users.noreply.github.com>
Date: Thu, 13 Nov 2025 11:41:53 +0900
Subject: [PATCH 4/5] Refactor: Reorder functions
---
src/iframe/life-game.js | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js
index b9917f2..8b85edd 100644
--- a/src/iframe/life-game.js
+++ b/src/iframe/life-game.js
@@ -29,6 +29,12 @@ function isNextAlive(around, self) {
return false;
}
+// cellの状態に応じた色を返す関数
+function getStyle(cell) {
+ // cellがtrueなら黒、falseなら白を返す
+ return cell ? "black" : "white";
+}
+
//Boardの初期化
let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
const table = document.getElementById("game-board");
@@ -100,10 +106,6 @@ document.addEventListener("mouseup", () => {
isDragging = false;
});
-function getStyle(cell) {
- return cell ? "black" : "white";
-}
-
renderBoard();
progressBoard();
From a6c1e2d56dddbf7651b18f905f69be0d743eb5ed Mon Sep 17 00:00:00 2001
From: kg0816 <211191696+kg0816@users.noreply.github.com>
Date: Thu, 13 Nov 2025 11:48:44 +0900
Subject: [PATCH 5/5] improve cellsize calculation
---
src/iframe/life-game.js | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js
index 8b85edd..0d79283 100644
--- a/src/iframe/life-game.js
+++ b/src/iframe/life-game.js
@@ -7,14 +7,9 @@ let timerTime = 1000;
let isDragging = false;
let dragMode = false; // true: 黒にする, false: 白にする
-const DEFAULT_BOARD_SIZE = 20;
-const DEFAULT_CELL_SIZE = 30;
-
//変数設定
let boardSize = 20; //盤面の大きさ(20x20)
-let cellScale = 1.0; //セルの大きさの倍率
-
-let cellSize = Math.floor(cellScale * DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / boardSize)); //セルの大きさ(px)
+const cellSize = 600 / boardSize; //セルの大きさ(px)
// around: 周囲の生きたセル数 self: 自身が生きているかどうか
function isNextAlive(around, self) {