俄罗斯方块

一个完全响应式的俄罗斯方块游戏,能在任何屏幕尺寸上提供优秀的游戏体验。包含完整的游戏功能(移动、旋转、硬降、暂存方块)、触屏手势控制、键盘控制、自适应UI布局、Top3排行榜,以及现代化的紫蓝渐变设计风格。

Preview
Source CodeVersion 6
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // API routes
    if (url.pathname.startsWith('/api/')) {
      return handleAPI(request, env, url);
    }

    // Serve HTML
    return new Response(getHTML(), {
      headers: { 'Content-Type': 'text/html' }
    });
  }
};

async function handleAPI(request, env, url) {
  const corsHeaders = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST',
    'Access-Control-Allow-Headers': 'Content-Type'
  };

  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  if (url.pathname === '/api/scores' && request.method === 'GET') {
    try {
      const { results } = await env.DB.prepare(
        'SELECT name, score FROM leaderboard ORDER BY score DESC LIMIT 3'
      ).all();
      return new Response(JSON.stringify({ data: results }), { headers: corsHeaders });
    } catch (error) {
      return new Response(JSON.stringify({ error: 'Database error' }), { 
        status: 500, 
        headers: corsHeaders 
      });
    }
  }

  if (url.pathname === '/api/scores' && request.method === 'POST') {
    try {
      const { name, score } = await request.json();
      
      if (!name || !score || score < 0) {
        return new Response(JSON.stringify({ error: 'Invalid input' }), { 
          status: 400, 
          headers: corsHeaders 
        });
      }

      // Insert new score
      await env.DB.prepare(
        'INSERT INTO leaderboard (id, name, score, created_at) VALUES (?1, ?2, ?3, datetime("now"))'
      ).bind(crypto.randomUUID(), name, score).run();

      // Keep only top 10 scores to avoid database bloat
      await env.DB.prepare(
        'DELETE FROM leaderboard WHERE id NOT IN (SELECT id FROM leaderboard ORDER BY score DESC LIMIT 10)'
      ).run();

      return new Response(JSON.stringify({ ok: true }), { headers: corsHeaders });
    } catch (error) {
      return new Response(JSON.stringify({ error: 'Database error' }), { 
        status: 500, 
        headers: corsHeaders 
      });
    }
  }

  return new Response(JSON.stringify({ error: 'Not found' }), {
    status: 404,
    headers: corsHeaders
  });
}

function getHTML() {
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>俄罗斯方块 - Tetris</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
  <style>
    body { 
      font-family: 'Inter', sans-serif; 
      overflow-x: hidden;
      -webkit-touch-callout: none;
      -webkit-user-select: none;
      -webkit-tap-highlight-color: transparent;
      min-height: 100vh;
      min-height: 100dvh;
    }
    
    /* Responsive game grid */
    .game-grid {
      display: grid;
      grid-template-columns: repeat(10, 1fr);
      gap: 1px;
      background: rgba(0, 0, 0, 0.8);
      border: 2px solid rgba(139, 92, 246, 0.5);
      border-radius: 8px;
      padding: 2px;
      aspect-ratio: 1/2;
    }
    
    /* Mobile first - optimized for small screens */
    @media (max-width: 640px) {
      .game-grid {
        width: min(300px, 80vw);
        gap: 1px;
      }
    }
    
    /* Tablet */
    @media (min-width: 641px) and (max-width: 1023px) {
      .game-grid {
        width: min(360px, 60vw);
        gap: 1px;
      }
    }
    
    /* Desktop */
    @media (min-width: 1024px) {
      .game-grid {
        width: 400px;
        gap: 2px;
      }
    }
    
    .game-cell {
      aspect-ratio: 1;
      background: rgba(255, 255, 255, 0.05);
      border-radius: 1px;
      transition: background-color 0.1s ease;
    }
    
    .filled { 
      background: linear-gradient(135deg, #8b5cf6, #3b82f6) !important; 
      box-shadow: 0 0 4px rgba(139, 92, 246, 0.6);
    }
    
    /* Preview grid */
    .preview-grid {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 1px;
      background: rgba(0, 0, 0, 0.6);
      border: 1px solid rgba(139, 92, 246, 0.3);
      border-radius: 6px;
      padding: 4px;
      width: 80px;
      height: 80px;
    }
    
    .preview-cell {
      aspect-ratio: 1;
      background: rgba(255, 255, 255, 0.05);
      border-radius: 1px;
    }
    
    /* Control buttons */
    .control-btn {
      touch-action: manipulation;
      user-select: none;
      -webkit-user-select: none;
      transition: all 0.15s ease;
      font-family: 'JetBrains Mono', monospace;
      font-weight: 600;
    }
    
    .control-btn:active {
      transform: scale(0.95);
    }
    
    .control-btn:hover {
      transform: scale(1.05);
    }
    
    /* Game panels */
    .game-panel {
      background: rgba(0, 0, 0, 0.4);
      backdrop-filter: blur(10px);
      bord
...

Comments (0)

No comments yet. Be the first!