登录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>

沪公网安备 31011002002714 号