井字棋游戏-半透明旋转动画风格edit icon

创建者:
用户fkxIv10d
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>井字棋游戏</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
      color: white;
      overflow: hidden;
    }

    .container {
      text-align: center;
    }

    h1 {
      font-size: 2.8em;
      margin-bottom: 10px;
      text-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
      letter-spacing: 4px;
    }

    .status {
      font-size: 1.4em;
      margin-bottom: 25px;
      min-height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
    }

    .status .player-icon {
      display: inline-block;
      font-weight: bold;
      font-size: 1.2em;
      width: 32px;
      height: 32px;
      line-height: 32px;
      border-radius: 6px;
      text-align: center;
    }

    .player-x {
      color: #ff6b9d;
      text-shadow: 0 0 10px rgba(255, 107, 157, 0.5);
    }

    .player-o {
      color: #4ecdc4;
      text-shadow: 0 0 10px rgba(78, 205, 196, 0.5);
    }

    .board {
      display: grid;
      grid-template-columns: repeat(3, 120px);
      grid-template-rows: repeat(3, 120px);
      gap: 8px;
      margin: 0 auto 30px;
      perspective: 800px;
    }

    .cell {
      width: 120px;
      height: 120px;
      background: rgba(255, 255, 255, 0.05);
      border: 2px solid rgba(255, 255, 255, 0.1);
      border-radius: 16px;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 3em;
      font-weight: bold;
      cursor: pointer;
      transition: all 0.3s ease;
      user-select: none;
      position: relative;
      overflow: hidden;
    }

    .cell::before {
      content: '';
      position: absolute;
      inset: 0;
      background: radial-gradient(circle at center, rgba(255, 255, 255, 0.1), transparent);
      opacity: 0;
      transition: opacity 0.3s;
    }

    .cell:hover:not(.taken) {
      background: rgba(255, 255, 255, 0.1);
      border-color: rgba(255, 255, 255, 0.3);
      transform: scale(1.05);
      box-shadow: 0 0 25px rgba(255, 255, 255, 0.1);
    }

    .cell:hover:not(.taken)::before {
      opacity: 1;
    }

    .cell.taken {
      cursor: default;
    }

    .cell.x-cell {
      color: #ff6b9d;
      text-shadow: 0 0 15px rgba(255, 107, 157, 0.6);
      animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .cell.o-cell {
      color: #4ecdc4;
      text-shadow: 0 0 15px rgba(78, 205, 196, 0.6);
      animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .cell.win-cell {
      animation: winPulse 0.6s ease-in-out infinite alternate;
      background: rgba(255, 255, 255, 0.15);
      border-color: rgba(255, 255, 255, 0.5);
    }

    @keyframes popIn {
      0% {
        transform: scale(0) rotate(-180deg);
        opacity: 0;
      }
      100% {
        transform: scale(1) rotate(0deg);
        opacity: 1;
      }
    }

    @keyframes winPulse {
      0% {
        transform: scale(1);
        box-shadow: 0 0 15px rgba(255, 255, 255, 0.2);
      }
      100% {
        transform: scale(1.08);
        box-shadow: 0 0 30px rgba(255, 255, 255, 0.4);
      }
    }

    .score-board {
      display: flex;
      justify-content: center;
      gap: 40px;
      margin-bottom: 25px;
    }

    .score-item {
      text-align: center;
    }

    .score-label {
      font-size: 0.9em;
      opacity: 0.7;
      margin-bottom: 4px;
    }

    .score-value {
      font-size: 2em;
      font-weight: bold;
    }

    .score-x .score-value {
      color: #ff6b9d;
    }

    .score-o .score-value {
      color: #4ecdc4;
    }

    .score-draw .score-value {
      color: #a0a0a0;
    }

    .btn-restart {
      padding: 14px 40px;
      font-size: 1.1em;
      font-weight: 600;
      border: 2px solid rgba(255, 255, 255, 0.3);
      border-radius: 50px;
      background: rgba(255, 255, 255, 0.08);
      color: white;
      cursor: pointer;
      transition: all 0.3s ease;
      letter-spacing: 2px;
      backdrop-filter: blur(10px);
    }

    .btn-restart:hover {
      background: rgba(255, 255, 255, 0.2);
      border-color: rgba(255, 255, 255, 0.6);
      transform: translateY(-2px);
      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
    }

    .btn-restart:active {
      transform: translateY(0);
    }

    .winner-overlay {
      position: fixed;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      background: rgba(0, 0, 0, 0.6);
      backdrop-filter: blur(5px);
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.4s ease;
      z-index: 100;
    }

    .winner-overlay.show {
      opacity: 1;
      pointer-events: all;
    }

    .winner-card {
      background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
      border: 1px solid rgba(255, 255, 255, 0.2);
      border-radius: 24px;
      padding: 50px 60px;
      text-align: center;
      transform: scale(0.8);
      transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      backdrop-filter: blur(20px);
    }

    .winner-overlay.show .winner-card {
      transform: scale(1);
    }

    .winner-emoji {
      font-size: 4em;
      margin-bottom: 15px;
    }

    .winner-text {
      font-size: 2em;
      font-weight: bold;
      margin-bottom: 8px;
    }

    .winner-sub {
      font-size: 1.1em;
      opacity: 0.7;
      margin-bottom: 30px;
    }

    .btn-play-again {
      padding: 12px 36px;
      font-size: 1em;
      font-weight: 600;
      border: 2px solid rgba(255, 255, 255, 0.4);
      border-radius: 50px;
      background: rgba(255, 255, 255, 0.15);
      color: white;
      cursor: pointer;
      transition: all 0.3s ease;
      letter-spacing: 1px;
    }

    .btn-play-again:hover {
      background: rgba(255, 255, 255, 0.25);
      transform: translateY(-2px);
    }

    @media (max-width: 480px) {
      .board {
        grid-template-columns: repeat(3, 90px);
        grid-template-rows: repeat(3, 90px);
        gap: 6px;
      }

      .cell {
        width: 90px;
        height: 90px;
        font-size: 2.2em;
        border-radius: 12px;
      }

      h1 {
        font-size: 2em;
      }

      .winner-card {
        padding: 35px 40px;
        margin: 0 20px;
      }
    }
  </style>
</head>

<body>
  <div class="container">
    <h1>✦ 井字棋 ✦</h1>
    <div class="score-board">
      <div class="score-item score-x">
        <div class="score-label">玩家 X</div>
        <div class="score-value" id="scoreX">0</div>
      </div>
      <div class="score-item score-draw">
        <div class="score-label">平局</div>
        <div class="score-value" id="scoreDraw">0</div>
      </div>
      <div class="score-item score-o">
        <div class="score-label">玩家 O</div>
        <div class="score-value" id="scoreO">0</div>
      </div>
    </div>
    <div class="status" id="status">
      轮到 <span class="player-icon player-x">X</span> 下棋
    </div>
    <div class="board" id="board"></div>
    <button class="btn-restart" id="btnRestart">重新开始</button>
  </div>

  <div class="winner-overlay" id="winnerOverlay">
    <div class="winner-card">
      <div class="winner-emoji" id="winnerEmoji">🎉</div>
      <div class="winner-text" id="winnerText">玩家 X 获胜!</div>
      <div class="winner-sub" id="winnerSub">太棒了!再来一局?</div>
      <button class="btn-play-again" id="btnPlayAgain">再来一局</button>
    </div>
  </div>

  <script>
    const board = document.getElementById('board');
    const statusEl = document.getElementById('status');
    const btnRestart = document.getElementById('btnRestart');
    const winnerOverlay = document.getElementById('winnerOverlay');
    const winnerEmoji = document.getElementById('winnerEmoji');
    const winnerText = document.getElementById('winnerText');
    const winnerSub = document.getElementById('winnerSub');
    const btnPlayAgain = document.getElementById('btnPlayAgain');
    const scoreXEl = document.getElementById('scoreX');
    const scoreOEl = document.getElementById('scoreO');
    const scoreDrawEl = document.getElementById('scoreDraw');

    let cells = Array(9).fill('');
    let currentPlayer = 'X';
    let gameActive = true;
    let scores = { X: 0, O: 0, draw: 0 };

    const winPatterns = [
      [0, 1, 2], [3, 4, 5], [6, 7, 8],
      [0, 3, 6], [1, 4, 7], [2, 5, 8],
      [0, 4, 8], [2, 4, 6]
    ];

    function createBoard() {
      board.innerHTML = '';
      for (let i = 0; i < 9; i++) {
        const cell = document.createElement('div');
        cell.classList.add('cell');
        cell.dataset.index = i;
        cell.addEventListener('click', () => handleCellClick(i));
        board.appendChild(cell);
      }
    }

    function handleCellClick(index) {
      if (!gameActive || cells[index] !== '') return;

      cells[index] = currentPlayer;
      const cell = board.children[index];
      cell.textContent = currentPlayer;
      cell.classList.add('taken', currentPlayer === 'X' ? 'x-cell' : 'o-cell');

      const winResult = checkWin();
      if (winResult) {
        gameActive = false;
        highlightWin(winResult);
        scores[currentPlayer]++;
        updateScores();
        setTimeout(() => showWinner(currentPlayer), 600);
        return;
      }

      if (cells.every(c => c !== '')) {
        gameActive = false;
        scores.draw++;
        updateScores();
        setTimeout(() => showWinner('draw'), 400);
        return;
      }

      currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
      updateStatus();
    }

    function checkWin() {
      for (const pattern of winPatterns) {
        const [a, b, c] = pattern;
        if (cells[a] && cells[a] === cells[b] && cells[a] === cells[c]) {
          return pattern;
        }
      }
      return null;
    }

    function highlightWin(pattern) {
      pattern.forEach(i => {
        board.children[i].classList.add('win-cell');
      });
    }

    function updateStatus() {
      const playerClass = currentPlayer === 'X' ? 'player-x' : 'player-o';
      statusEl.innerHTML = `轮到 <span class="player-icon ${playerClass}">${currentPlayer}</span> 下棋`;
    }

    function updateScores() {
      scoreXEl.textContent = scores.X;
      scoreOEl.textContent = scores.O;
      scoreDrawEl.textContent = scores.draw;
    }

    function showWinner(result) {
      if (result === 'draw') {
        winnerEmoji.textContent = '🤝';
        winnerText.textContent = '平局!';
        winnerText.style.color = '#a0a0a0';
        winnerSub.textContent = '势均力敌,再来一局?';
      } else {
        winnerEmoji.textContent = '🏆';
        winnerText.textContent = `玩家 ${result} 获胜!`;
        winnerText.style.color = result === 'X' ? '#ff6b9d' : '#4ecdc4';
        winnerSub.textContent = '太厉害了!再来一局?';
      }
      winnerOverlay.classList.add('show');
    }

    function resetGame() {
      cells = Array(9).fill('');
      currentPlayer = 'X';
      gameActive = true;
      winnerOverlay.classList.remove('show');
      updateStatus();
      createBoard();
    }

    btnRestart.addEventListener('click', resetGame);
    btnPlayAgain.addEventListener('click', resetGame);

    createBoard();
  </script>
</body>

</html>
        
编辑器加载中
预览
控制台