代码: 全选
目前你的動畫位移公式是:
```js
let startY = player === 1 ? 600 : -600;
useNode.setAttribute(
'transform',
`translate(0, ${
startY * (1 - Math.pow(1 - progress, 3))
})`
);
```
當:
* `progress = 0`
→ `translate(0, 0)`
* `progress = 1`
→ `translate(0, startY)`
因此棋子一開始在 `(0,0)`(棋盤中央),最後跑到 `±600`,所以看起來是「從中心飛出去」。
如果要「從外面掉到中心」,起點應該是 `startY`,終點是 `0`。
改成:
```js
function dropAnim(time) {
let progress = (time - startTime) / 3600;
if (progress > 1) progress = 1;
let y = startY * Math.pow(1 - progress, 3);
useNode.setAttribute(
'transform',
`translate(0, ${y.toFixed(3)})`
);
if (progress < 1)
requestAnimationFrame(dropAnim);
else {
occupied.add('1,0');
occupied.add('-1,0');
piecesLeft[player][0]--;
isFirstMove = false;
let notation = '0';
moveHistory.push({ notation, pid: 0, t1, t2, px: 0, py: 0, player });
historyIndex++;
cleanUpSelection();
showNotification(getText('secondRedPrompt'));
}
}
```
此時:
| progress | y |
| -------- | -------------- |
| 0 | startY |
| 0.5 | startY × 0.125 |
| 1 | 0 |
例如:
* 紅方:`startY = 600`
→ `600 → 0`
* 藍方:`startY = -600`
→ `-600 → 0`
棋子就會從畫面外往中央落下。
如果你想保留原本的 easing 寫法,也可以直接把公式反過來:
```js
let y = startY * (1 - progress) ** 3;
```
甚至最簡單:
```js
let y = startY * (1 - progress);
```
* 線性下降:`1-progress`
* 慢慢停下:`(1-progress)^2`
* 更明顯的減速:`(1-progress)^3`
你目前的動畫其實只差把:
```js
1 - (1 - progress) ** 3
```
改成:
```js
(1 - progress) ** 3
```
即可。
一,導入棋譜及回退時中心棋子消失
當導入棋譜時,或者點擊回退時,位於中心的第一手棋子消失,查找原因並修復問題。
二,進入第二局的問題
當進入第二局,雙方所顯示的分數應清零,上一局的分數應先記在某個變量中,等第二局結束,再統計總分。
需要明確給出所要修改的地方和所要替換的代碼。
代码: 全选
修復與改進:
一,導入棋譜及回退時中心棋子消失
當導入棋譜時,或者點擊回退時,位於中心的第一手棋子消失,查找原因並修復問題。
二,進入第二局的問題
當進入第二局,雙方所顯示的分數應清零,上一局的分數應先記在某個變量中,等第二局結束,再統計總分。
需要明確給出所要修改的地方和所要替換的代碼。代码: 全选
我先讀取上傳的文件來分析代碼。讓我查看截斷的部分,特別是初始化棋盤和回退相關的代碼:現在我已充分了解代碼的問題所在。讓我分析兩個 Bug:
---
## Bug 分析與修復方案
### Bug 一:導入棋譜及回退時中心棋子消失
**根本原因**:`redrawFromHistory()` 函數(第 1357–1382 行)在重繪前清除棋子的邏輯有問題:
```js
document.querySelectorAll('.tiledropped').forEach(el => {
if (el.getAttribute('fill') !== '#00BFFF' || el.getAttribute('transform')) el.remove();
});
```
這段邏輯試圖保留「沒有 `transform` 屬性且顏色為藍色的中心棋子(初始靜態藍棋)」,但問題是:
- 初始靜態藍棋(HTML 裡的 `<use href="#tile0" fill="#00BFFF" transform="translate(0,0)" ...>`)**有 `transform` 屬性**,所以也被刪除了。
- 之後雖然把 `occupied` 設為 `new Set(['1,0', '-1,0'])`,但沒有重新繪製那顆中心藍棋的 SVG 元素。
**修復**:把整個 `etanidrop` 內的 `.tiledropped` 全部清除,然後從 `historyIndex >= 0` 的第一手(`moveHistory[0]`,即中心藍棋)開始重繪,而不是跳過 index 0。
---
### Bug 二:進入第二局分數應清零
**根本原因**:`updateUI()` 直接顯示 `calculateScores()` 的當前棋盤分數,第二局開始時棋盤雖是空的(分數為 0),但**問題在 `initBoardState(true)` 的 `historyIndex = 35`**——這讓 `redrawFromHistory` 從頭 replay 第一局棋子,使得棋盤分數反映的是第一局殘局,而非清零。
更準確說:第二局開局時 UI 顯示的 `score-1` / `score-2` 應顯示 `0`(當前局得分),而第一局的得分應存入 `p1TotalScore` / `p2TotalScore` 等待合算。目前 `initBoardState(true)` 雖設置了 `historyIndex = 35`,但 **`piecesLeft` 被重置為 `{1:[6,6,6], 2:[6,6,6]}`** 且沒有 replay,導致 UI 分數混亂。
**修復**:在 `startRound2()` 調用 `initBoardState(true)` 前,**先把第一局分數存入 `p1TotalScore` / `p2TotalScore`**;並在 `initBoardState(true)` 裡確保 `historyIndex = -1`(第二局從空棋盤開始,有自己的 history),不再嘗試依賴第一局的 historyIndex。
---
## 具體修改位置與替換代碼
### 修改一:`redrawFromHistory` 函數(第 1357–1382 行)
**找到這段代碼:**
```js
function redrawFromHistory() {
document.querySelectorAll('.tiledropped').forEach(el => {
if (el.getAttribute('fill') !== '#00BFFF' || el.getAttribute('transform')) el.remove();
});
occupied = new Set(['1,0', '-1,0']);
piecesLeft = { 1: [5, 6, 6], 2: [6, 6, 6] };
currentPlayer = 2;
isFirstMove = true;
for (let i = 1; i <= historyIndex; i++) {
let m = moveHistory[i];
occupied.add(`${m.t1.idx},${m.t1.N}`);
occupied.add(`${m.t2.idx},${m.t2.N}`);
piecesLeft[m.player][m.pid]--;
currentPlayer = m.player === 1 ? 2 : 1;
isFirstMove = false;
let useNode = document.createElementNS('http://www.w3.org/2000/svg', 'use');
useNode.setAttribute('href', `#tile${m.pid}`);
useNode.setAttribute('class', 'tiledropped');
useNode.setAttribute('fill', TILE_COLORS[m.pid]);
useNode.setAttribute('transform', `translate(${m.px.toFixed(3)}, ${m.py.toFixed(3)})`);
document.getElementById('etanidrop').appendChild(useNode);
}
updateUI();
}
```
**替換為:**
```js
function redrawFromHistory() {
// 清除所有已落子的棋子元素(含中心藍棋)
document.querySelectorAll('.tiledropped').forEach(el => el.remove());
// 重置狀態:以局的起點為準
let roundStart = currentRound === 2 ? 36 : 0;
occupied = new Set();
piecesLeft = { 1: [6, 6, 6], 2: [6, 6, 6] };
currentPlayer = currentFirstPlayer;
isFirstMove = true;
// 從本局第一手開始重繪到 historyIndex
for (let i = roundStart; i <= historyIndex; i++) {
let m = moveHistory[i];
occupied.add(`${m.t1.idx},${m.t1.N}`);
occupied.add(`${m.t2.idx},${m.t2.N}`);
piecesLeft[m.player][m.pid]--;
currentPlayer = m.player === 1 ? 2 : 1;
isFirstMove = false;
let useNode = document.createElementNS('http://www.w3.org/2000/svg', 'use');
useNode.setAttribute('href', `#tile${m.pid}`);
useNode.setAttribute('class', 'tiledropped');
useNode.setAttribute('fill', TILE_COLORS[m.pid]);
useNode.setAttribute('transform', `translate(${m.px.toFixed(3)}, ${m.py.toFixed(3)})`);
document.getElementById('etanidrop').appendChild(useNode);
}
updateUI();
}
```
**說明**:
- 全部清除 `.tiledropped`(不再有漏掉靜態元素的問題)。
- 從 `roundStart`(第一局從 index 0,第二局從 index 36)開始重繪,這樣中心藍棋(index 0 / index 36)自然被重繪出來,不會消失。
- `currentFirstPlayer` 記錄本局先手,確保 currentPlayer 初始正確。
---
### 修改二:`initBoardState` 函數(第 1019–1042 行)
**找到這段代碼:**
```js
function initBoardState(isRound2 = false) {
document.getElementById('etanidrop').innerHTML =
'<g class="etboard"><circle r="6" cx="0" cy="0" fill="purple" stroke="none" stroke-width="0" class="bcenter" /></g>';
drawGrid();
occupied = new Set(); // 一開始完全空白
piecesLeft = { 1: [6, 6, 6], 2: [6, 6, 6] }; // 恢復雙方各6顆藍棋
if (!isRound2) {
moveHistory = [];
historyIndex = -1;
currentRound = 1;
currentFirstPlayer = 1; // 第一局我方先手
} else {
// 如果是第二局,保留前面的歷史,重置棋盤狀態
historyIndex = 35; // 第一局共下了36手 (0-35)
currentRound = 2;
currentFirstPlayer = 2; // 第二局對方先手
}
currentPlayer = currentFirstPlayer;
isFirstMove = true;
updateUI();
}
```
**替換為:**
```js
function initBoardState(isRound2 = false) {
document.getElementById('etanidrop').innerHTML =
'<g class="etboard"><circle r="6" cx="0" cy="0" fill="purple" stroke="none" stroke-width="0" class="bcenter" /></g>';
drawGrid();
occupied = new Set();
piecesLeft = { 1: [6, 6, 6], 2: [6, 6, 6] };
if (!isRound2) {
moveHistory = [];
historyIndex = -1;
currentRound = 1;
currentFirstPlayer = 1; // 第一局我方先手
p1TotalScore = 0;
p2TotalScore = 0;
} else {
// 第二局:保留第一局 history,第二局 history 從 index 36 開始疊加
// 先手改為對方
currentFirstPlayer = 2;
currentRound = 2;
historyIndex = moveHistory.length - 1; // 第二局起始點緊接第一局末尾
}
currentPlayer = currentFirstPlayer;
isFirstMove = true;
updateUI();
}
```
---
### 修改三:`startRound2` 函數(第 2189–2194 行)
**找到這段代碼:**
```js
function startRound2() {
document.getElementById('game-over-screen').style.display = 'none';
gameState = 'playing';
initBoardState(true); // 保留歷史,清空棋盤開啟第二局
showNotification(getText('firstBluePrompt'));
}
```
**替換為:**
```js
function startRound2() {
document.getElementById('game-over-screen').style.display = 'none';
gameState = 'playing';
// 先存第一局分數,再開始第二局(此時 calculateScores 仍反映第一局結算)
let round1Scores = calculateScores();
p1TotalScore = round1Scores.s1;
p2TotalScore = round1Scores.s2;
initBoardState(true);
// 第二局 UI 分數顯示應歸零(當前局)
document.getElementById('score-1').innerText = '0';
document.getElementById('score-2').innerText = '0';
showNotification(getText('firstBluePrompt'));
}
```
**說明**:
- 在 `initBoardState(true)` 調用前,先用 `calculateScores()` 把第一局分數存入 `p1TotalScore` / `p2TotalScore`。
- `initBoardState` 不再對這兩個變量清零(因為第二局需要它們)。
- 強制把 UI 分數顯示歸零,避免第一局棋盤殘局分數干擾顯示。
---
### 修改四:`selectPiece` 裡 `movesInRound` 計算(第 1564 行)
**找到這段代碼:**
```js
let movesInRound = currentRound === 1 ? moveHistory.length : moveHistory.length - 36;
```
**替換為:**
```js
let roundStart = currentRound === 2 ? 36 : 0;
let movesInRound = historyIndex - roundStart + 1;
```
同樣,`attemptDrop` 裡第 1220 行也有同樣的計算:
**找到:**
```js
let movesInRound = currentRound === 1 ? moveHistory.length : moveHistory.length - 36;
```
**替換為:**
```js
let roundStart = currentRound === 2 ? 36 : 0;
let movesInRound = historyIndex - roundStart + 1;
```
**說明**:原本用 `moveHistory.length` 判斷本局手數,回退後 `moveHistory.length` 不變,所以判斷邏輯錯誤。改用 `historyIndex` 才能正確反映「當前實際進行到第幾手」。
---
以上四處修改即可完整修復兩個問題。如需我直接輸出修改好的完整 HTML 文件,請告知。
