登录B站账号访问本页即可注册本Wiki,点击登录
背景色更换功能请前往PC端使用
本Wiki注册达5000、20000人时,有额外奖励,快分享给好友吧!
Widget:小游戏消消乐
<body>
<button id="startButton" class="start-button">开始游戏</button>
分数: 0
剩余时间: 60秒
游戏结束
你的得分: 0
<button id="restartButton" class="restart-button">再来一局</button>
<style>
body {
font-family: 'Arial', sans-serif; margin: 0; padding: 0; /*display: flex;*/ justify-content: center; align-items: center; min-height: 100vh; background-color: #f0f0f0;
} .game-container {
text-align: center; background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.game-board {
display: grid; grid-template-columns: repeat(8, 1fr); gap: 5px; margin: 20px auto; width: 420px; position: relative; min-height: 420px;
}
.tile {
width: 50px; height: 50px; background-color: #ddd; border-radius: 5px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: transform 0.2s, background-color 0.2s;
}
.tile:hover {
transform: scale(1.05);
}
.tile.selected {
background-color: #ffeb3b; transform: scale(1.2); transition: transform 0.2s ease; z-index: 10;
}
.tile.swapping {
transition: all 0.3s ease;
}
.tile.shake {
animation: shake 0.5s ease;
}
@keyframes shake {
0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-5px); } 40%, 80% { transform: translateX(5px); }
}
.particle {
position: absolute; width: 8px; height: 8px; border-radius: 50%; pointer-events: none; animation: particle-fly 0.5s ease-out forwards; z-index: 100; will-change: transform, opacity;
}
@keyframes particle-fly {
0% { transform: translate(0, 0); opacity: 1; } 100% { transform: translate(var(--tx), var(--ty)); opacity: 0; }
}
.tile.matched {
visibility: hidden;
}
.game-info {
margin-top: 20px; font-size: 1.2em;
}
.start-button {
padding: 10px 20px; font-size: 1.2em; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; margin-bottom: 20px;
}
.start-button:hover {
background-color: #45a049;
}
.game-over-panel {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.2); text-align: center; display: none; z-index: 100;
}
.restart-button {
padding: 10px 20px; font-size: 1em; background-color: #2196F3; color: white; border: none; border-radius: 5px; cursor: pointer; margin-top: 20px;
}
.restart-button:hover {
background-color: #0b7dda;
}
.tile.falling {
transition: transform 0.3s ease-out; transform: translateY(100%);
}
.tile.new {
animation: appear 0.3s ease-out;
}
@keyframes appear {
from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); }
}
</style> <script>
// 游戏配置 const config = {
rows: 8, cols: 8, tileTypes: 8, timeLimit: 60,
};
// 游戏状态 let state = {
board: [], selectedTile: null, score: 0, timeLeft: config.timeLimit, timer: null, gameOver: false
};
// 初始化游戏 function initGame() {
createBoard(); renderBoard(); addEventListeners(); // 添加开始按钮事件 document.getElementById('startButton').addEventListener('click', startGame); // 添加重新开始按钮事件 document.getElementById('restartButton').addEventListener('click', restartGame); // 隐藏游戏结束面板 document.getElementById('gameOverPanel').style.display = 'none'; // 禁用游戏板交互 document.getElementById('gameBoard').style.pointerEvents = 'none';
}
// 开始游戏 function startGame() {
state.score = 0; state.timeLeft = config.timeLimit; state.gameOver = false; updateScore(); updateTimer(); startTimer(); // 启用游戏板交互 document.getElementById('gameBoard').style.pointerEvents = 'auto'; // 隐藏开始按钮 document.getElementById('startButton').style.display = 'none';
}
// 创建游戏板 function createBoard() {
state.board = []; for (let i = 0; i < config.rows; i++) { state.board[i] = []; for (let j = 0; j < config.cols; j++) { state.board[i][j] = Math.floor(Math.random() * config.tileTypes); } } // 确保初始没有匹配 while (checkAllMatches().length > 0) { createBoard(); }
}
// 渲染游戏板 function renderBoard() {
const gameBoard = document.getElementById('gameBoard'); gameBoard.innerHTML = ;
for (let i = 0; i < config.rows; i++) { for (let j = 0; j < config.cols; j++) { const tile = document.createElement('div'); tile.className = 'tile'; tile.dataset.row = i; tile.dataset.col = j;
const img = document.createElement('img'); const imageUrls = [ 'https://patchwiki.biligame.com/images/ssjj2/3/3a/o0s5wo6c8q3b8yv1wd3f0ubm3610ugh.png', 'https://patchwiki.biligame.com/images/ssjj2/5/5b/spouyw399ah45k8yf3begza9lmvluwf.png',/*瑞秋*/ 'https://patchwiki.biligame.com/images/ssjj2/9/94/agav5py0sknjc6wn3qgqeeahep8s2y9.png',/*梦魇猎人*/ 'https://patchwiki.biligame.com/images/ssjj2/e/e9/cm79rw31uv9gxvbwqgheckdhe46miht.png', 'https://patchwiki.biligame.com/images/ssjj2/6/69/5gpd9iz7u3f2btaqn8gu8nekctli9qb.png',/*方块A*/ 'https://patchwiki.biligame.com/images/ssjj2/9/9c/b88jcdwq7p86vr8mok43lm029kswdwz.png', 'https://patchwiki.biligame.com/images/ssjj2/d/dc/38ct7onhztvefyo7k8wd39c5o0dk0c7.png', 'https://patchwiki.biligame.com/images/ssjj2/6/66/g5zv1ynxoq29ffwqsph9ylrem2msat9.png' /*噩梦*/ ]; img.src = imageUrls[state.board[i][j]]; img.style.width = '100%'; img.style.height = '100%'; img.style.objectFit = 'cover'; img.style.willChange = 'transform'; tile.appendChild(img);
gameBoard.appendChild(tile); } }
}
// 添加事件监听器 function addEventListeners() {
document.getElementById('gameBoard').addEventListener('click', handleTileClick);
}
// 处理点击事件 async function handleTileClick(event) {
if (state.gameOver) return;
const tile = event.target.closest('.tile'); if (!tile) return;
const row = parseInt(tile.dataset.row); const col = parseInt(tile.dataset.col);
if (state.selectedTile === null) { // 第一次选择 state.selectedTile = { row, col }; tile.classList.add('selected'); } else { // 第二次选择 const firstTile = state.selectedTile; const secondTile = { row, col };
// 检查是否相邻 if (isAdjacent(firstTile, secondTile)) { // 交换位置 const swapSuccess = await swapTiles(firstTile, secondTile); if (swapSuccess) { // 检查交换后是否形成匹配 const matches = checkMatchesAfterSwap(firstTile, secondTile); if (matches.length > 0) { // 有匹配,显示粒子特效 const tile1El = document.querySelector(`[data-row="${firstTile.row}"][data-col="${firstTile.col}"]`); const tile2El = document.querySelector(`[data-row="${secondTile.row}"][data-col="${secondTile.col}"]`); if (tile1El && tile2El) { const rect1 = tile1El.getBoundingClientRect(); const rect2 = tile2El.getBoundingClientRect(); createParticles(rect1.left + rect1.width/2, rect1.top + rect1.height/2); createParticles(rect2.left + rect2.width/2, rect2.top + rect2.height/2); // 消除并计分 setTimeout(() => { removeMatches(matches); fillBoard(); }, 500); } } else { // 无匹配,抖动效果 const tile1El = document.querySelector(`[data-row="${firstTile.row}"][data-col="${firstTile.col}"]`); const tile2El = document.querySelector(`[data-row="${secondTile.row}"][data-col="${secondTile.col}"]`); if (tile1El && tile2El) { tile1El.classList.add('shake'); tile2El.classList.add('shake'); setTimeout(() => { tile1El.classList.remove('shake'); tile2El.classList.remove('shake'); // 撤销交换 swapTiles(firstTile, secondTile); }, 500); } } } }
// 重置选择 document.querySelector('.selected')?.classList.remove('selected'); state.selectedTile = null; }
}
// 检查两个方块是否相邻 function isAdjacent(tile1, tile2) {
const rowDiff = Math.abs(tile1.row - tile2.row); const colDiff = Math.abs(tile1.col - tile2.col); return (rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1);
}
// 创建粒子特效 function createParticles(x, y, count = 20) {
const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']; for (let i = 0; i < count; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.left = `${x}px`; particle.style.top = `${y}px`; particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; const angle = Math.random() * Math.PI * 2; const distance = 20 + Math.random() * 50; particle.style.setProperty('--tx', `${Math.cos(angle) * distance}px`); particle.style.setProperty('--ty', `${Math.sin(angle) * distance}px`); document.body.appendChild(particle); particle.addEventListener('animationend', () => particle.remove()); }
}
// 交换两个方块 async function swapTiles(tile1, tile2) {
// 临时保存原始位置 const originalTile1 = {row: tile1.row, col: tile1.col}; const originalTile2 = {row: tile2.row, col: tile2.col}; // 获取DOM元素 const tile1El = document.querySelector(`[data-row="${tile1.row}"][data-col="${tile1.col}"]`); const tile2El = document.querySelector(`[data-row="${tile2.row}"][data-col="${tile2.col}"]`); if (!tile1El || !tile2El) return false;
// 禁用交互 tile1El.style.pointerEvents = 'none'; tile2El.style.pointerEvents = 'none'; // 添加交换动画 tile1El.classList.add('swapping'); tile2El.classList.add('swapping'); // 获取位置信息 const rect1 = tile1El.getBoundingClientRect(); const rect2 = tile2El.getBoundingClientRect(); // 计算移动距离 const dx = rect2.left - rect1.left; const dy = rect2.top - rect1.top; // 执行动画 tile1El.style.transform = `translate(${dx}px, ${dy}px)`; tile2El.style.transform = `translate(${-dx}px, ${-dy}px)`; try { // 等待动画完成 await new Promise(resolve => setTimeout(resolve, 300)); // 实际交换数据 const temp = state.board[tile1.row][tile1.col]; state.board[tile1.row][tile1.col] = state.board[tile2.row][tile2.col]; state.board[tile2.row][tile2.col] = temp; // 更新DOM属性 tile1El.dataset.row = originalTile2.row; tile1El.dataset.col = originalTile2.col; tile2El.dataset.row = originalTile1.row; tile2El.dataset.col = originalTile1.col; return true; } catch (error) { console.error('交换出错:', error); return false; } finally { // 重置样式 tile1El.style.transform = ; tile2El.style.transform = ; tile1El.classList.remove('swapping'); tile2El.classList.remove('swapping'); // 重新启用交互 tile1El.style.pointerEvents = 'auto'; tile2El.style.pointerEvents = 'auto'; // 重新渲染 renderBoard(); }
}
// 检查全盘所有匹配 function checkAllMatches() {
const matches = []; // 检查水平匹配 for (let i = 0; i < config.rows; i++) { for (let j = 0; j < config.cols - 2; j++) { const type = state.board[i][j]; if (type === state.board[i][j + 1] && type === state.board[i][j + 2]) { let k = j + 2; while (k < config.cols && state.board[i][k] === type) k++; if (k - j >= 3) { matches.push({ row: i, startCol: j, endCol: k - 1, direction: 'horizontal' }); } j = k - 1; } } } // 检查垂直匹配 for (let j = 0; j < config.cols; j++) { for (let i = 0; i < config.rows - 2; i++) { const type = state.board[i][j]; if (type === state.board[i + 1][j] && type === state.board[i + 2][j]) { let k = i + 2; while (k < config.rows && state.board[k][j] === type) k++; if (k - i >= 3) { matches.push({ col: j, startRow: i, endRow: k - 1, direction: 'vertical' }); } i = k - 1; } } } return matches;
}
// 检查交换后是否形成匹配 function checkMatchesAfterSwap(tile1, tile2) {
const matches = []; // 检查tile1周围 checkAroundTile(tile1.row, tile1.col, matches); // 检查tile2周围 checkAroundTile(tile2.row, tile2.col, matches); return matches;
}
// 检查指定方块周围的匹配 function checkAroundTile(row, col, matches) {
const type = state.board[row][col]; // 检查水平方向 let left = col; while (left > 0 && state.board[row][left-1] === type) left--; let right = col; while (right < config.cols-1 && state.board[row][right+1] === type) right++; if (right - left >= 2) { // 至少3个连续 matches.push({ row: row, startCol: left, endCol: right, direction: 'horizontal' }); } // 检查垂直方向 let top = row; while (top > 0 && state.board[top-1][col] === type) top--; let bottom = row; while (bottom < config.rows-1 && state.board[bottom+1][col] === type) bottom++; if (bottom - top >= 2) { // 至少3个连续 matches.push({ col: col, startRow: top, endRow: bottom, direction: 'vertical' }); }
}
// 消除匹配的方块 function removeMatches(matches) {
let totalRemoved = 0; for (const match of matches) { if (match.direction === 'horizontal') { const count = match.endCol - match.startCol + 1; // 计分规则:3个=3分,每多1个加2分 const points = 3 + (count - 3) * 2; state.score += points; for (let j = match.startCol; j <= match.endCol; j++) { state.board[match.row][j] = -1; // 标记为待消除 totalRemoved++; } } else { const count = match.endRow - match.startRow + 1; const points = 3 + (count - 3) * 2; state.score += points; for (let i = match.startRow; i <= match.endRow; i++) { state.board[i][match.col] = -1; // 标记为待消除 totalRemoved++; } } } updateScore(); return totalRemoved;
}
// 填充空白位置 function fillBoard() {
// 下落现有方块 for (let j = 0; j < config.cols; j++) { let emptyRow = config.rows - 1; for (let i = config.rows - 1; i >= 0; i--) { if (state.board[i][j] !== -1) { state.board[emptyRow][j] = state.board[i][j]; if (emptyRow !== i) { state.board[i][j] = -1; // 添加下落动画 const tile = document.querySelector(`[data-row="${i}"][data-col="${j}"]`); if (tile) { tile.classList.add('falling'); setTimeout(() => { tile.classList.remove('falling'); }, 300); } } emptyRow--; } } // 从顶部补充新方块 for (let i = emptyRow; i >= 0; i--) { state.board[i][j] = Math.floor(Math.random() * config.tileTypes); // 添加出现动画 setTimeout(() => { const tile = document.querySelector(`[data-row="${i}"][data-col="${j}"]`); if (tile) { tile.classList.add('new'); setTimeout(() => { tile.classList.remove('new'); }, 300); } }, (emptyRow - i) * 100); } } // 检查是否有新的匹配 const newMatches = checkAllMatches(); if (newMatches.length > 0) { setTimeout(() => { removeMatches(newMatches); fillBoard(); renderBoard(); }, 500); } else { renderBoard(); }
}
// 更新分数 function updateScore() {
document.getElementById('score').textContent = state.score;
}
// 开始计时器 function startTimer() {
updateTimer(); state.timer = setInterval(() => { state.timeLeft--; updateTimer();
if (state.timeLeft <= 0) { endGame(); } }, 1000);
}
// 更新计时器显示 function updateTimer() {
document.getElementById('time').textContent = state.timeLeft;
}
// 结束游戏 function endGame() {
clearInterval(state.timer); state.gameOver = true; // 显示游戏结束面板 document.getElementById('finalScore').textContent = state.score; document.getElementById('gameOverPanel').style.display = 'block'; // 禁用游戏板交互 document.getElementById('gameBoard').style.pointerEvents = 'none';
}
// 重新开始游戏 function restartGame() {
// 隐藏游戏结束面板 document.getElementById('gameOverPanel').style.display = 'none'; // 重新初始化游戏 initGame(); startGame();
}
// 初始化游戏 document.addEventListener('DOMContentLoaded', initGame);
</script>
</body>