移动端小游戏之~疯狂世界杯

12 阅读9分钟

2026年世界杯还有8个月左右,让我们来讲讲

中国足球圈知名梗盘点

五大联赛都不够排的,中国足球的梗才是最多的

6367a76c6179a08430369033a4fd55f088873e32.jpg 黑色三分钟

兵败吉隆坡

永久退出足球(万达)

恐韩症,黄金一代,超白金一代,断子绝孙脚,郑智化,谢天谢地谢亚龙,髂腰肌,护球像亨利,脸都不要了,还有算错净胜球的离谱操作...

下面让我隆重介绍移动端足球射门的网页小游戏

实现效果如下图

1761459734007.jpg

页面布局

  • 包含三个主要区域:游戏主界面、规则说明、幸运榜单
  • 使用rem单位进行移动端适配
  • 采用固定尺寸设计(9.946667rem高度)

动画效果

  • 使用SVGA实现箭头动画
  • CSS动画实现奖品闪烁效果
  • 滚动显示中奖记录

游戏玩法

  • 玩家可以拖动足球调整位置
  • 点击"射门"按钮进行射门
  • 可选择"自动射门"模式(最多30次)
  • 射中不同区域获得不同奖品

附代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>足球游戏 - Vue版本</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
      background: linear-gradient(135deg, #1a0933 0%, #2d1b4e 100%);
      color: #fff;
      min-height: 100vh;
      overflow-x: hidden;
    }
    
    .container {
      max-width: 750px;
      margin: 0 auto;
      padding: 20px;
    }
    
    .header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
    }
    
    .btn {
      background: linear-gradient(135deg, #5bb200 0%, #7cd500 100%);
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 20px;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.3s;
    }
    
    .btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 5px 15px rgba(91, 178, 0, 0.4);
    }
    
    .btn:active {
      transform: translateY(0);
    }
    
    .game-area {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 20px;
      padding: 20px;
      margin-bottom: 20px;
      backdrop-filter: blur(10px);
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
    }
    
    .game-title {
      text-align: center;
      margin-bottom: 20px;
      font-size: 24px;
      color: #5bb200;
    }
    
    .goal-area {
      position: relative;
      width: 100%;
      height: 400px;
      background: linear-gradient(135deg, #0a3d0a 0%, #1a6b1a 100%);
      border-radius: 10px;
      margin-bottom: 20px;
      overflow: hidden;
    }
    
    .goal-posts {
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);
      width: 300px;
      height: 200px;
      border: 5px solid #fff;
      border-top: none;
    }
    
    .goal-net {
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);
      width: 290px;
      height: 190px;
      background: repeating-linear-gradient(
        0deg,
        transparent,
        transparent 10px,
        rgba(255, 255, 255, 0.2) 10px,
        rgba(255, 255, 255, 0.2) 20px
      );
    }
    
    .goal-targets {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-template-rows: repeat(3, 1fr);
      gap: 10px;
      position: absolute;
      top: 50px;
      left: 50%;
      transform: translateX(-50%);
      width: 280px;
      height: 150px;
    }
    
    .target {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 8px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 5px;
      transition: all 0.3s;
      cursor: pointer;
    }
    
    .target:hover {
      background: rgba(255, 255, 255, 0.2);
    }
    
    .target-icon {
      width: 40px;
      height: 40px;
      background: #5bb200;
      border-radius: 50%;
      margin-bottom: 5px;
    }
    
    .target-name {
      font-size: 12px;
      text-align: center;
    }
    
    .football {
      position: absolute;
      width: 50px;
      height: 50px;
      background: #fff;
      border-radius: 50%;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      cursor: pointer;
      transition: all 0.3s;
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
      z-index: 10;
    }
    
    .football::before {
      content: '';
      position: absolute;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 10px,
        #000 10px,
        #000 20px
      );
      opacity: 0.2;
    }
    
    .controls {
      display: flex;
      justify-content: center;
      gap: 20px;
      margin-top: 20px;
    }
    
    .control-btn {
      background: linear-gradient(135deg, #5bb200 0%, #7cd500 100%);
      color: white;
      border: none;
      padding: 15px 30px;
      border-radius: 30px;
      font-size: 16px;
      font-weight: bold;
      cursor: pointer;
      transition: all 0.3s;
      min-width: 150px;
    }
    
    .control-btn:hover {
      transform: translateY(-3px);
      box-shadow: 0 8px 20px rgba(91, 178, 0, 0.6);
    }
    
    .control-btn:active {
      transform: translateY(0);
    }
    
    .control-btn.auto {
      background: linear-gradient(135deg, #ff9900 0%, #ffcc00 100%);
    }
    
    .control-btn.auto.active {
      background: linear-gradient(135deg, #ff3366 0%, #ff6699 100%);
    }
    
    .records {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 10px;
      padding: 15px;
      margin-top: 20px;
      max-height: 200px;
      overflow-y: auto;
    }
    
    .record-item {
      padding: 8px 0;
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
      font-size: 14px;
    }
    
    .record-item:last-child {
      border-bottom: none;
    }
    
    .lucky-list {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 10px;
      padding: 15px;
      margin-top: 20px;
      max-height: 400px;
      overflow-y: auto;
    }
    
    .lucky-item {
      display: flex;
      align-items: center;
      padding: 10px 0;
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }
    
    .lucky-item:last-child {
      border-bottom: none;
    }
    
    .lucky-avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      margin-right: 10px;
      background: #5bb200;
    }
    
    .lucky-info {
      flex: 1;
    }
    
    .lucky-name {
      font-weight: bold;
    }
    
    .lucky-details {
      font-size: 12px;
      opacity: 0.8;
    }
    
    .rules {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 10px;
      padding: 20px;
      margin-top: 20px;
      max-height: 500px;
      overflow-y: auto;
    }
    
    .rule-section {
      margin-bottom: 20px;
    }
    
    .rule-title {
      font-size: 18px;
      margin-bottom: 10px;
      color: #5bb200;
    }
    
    .rule-text {
      margin-bottom: 10px;
      line-height: 1.5;
    }
    
    .rule-table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 10px;
    }
    
    .rule-table th, .rule-table td {
      padding: 10px;
      text-align: left;
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }
    
    .rule-table th {
      background: rgba(91, 178, 0, 0.2);
    }
    
    .navigation {
      display: flex;
      justify-content: center;
      gap: 10px;
      margin-top: 20px;
    }
    
    .nav-btn {
      background: rgba(255, 255, 255, 0.1);
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 20px;
      cursor: pointer;
      transition: all 0.3s;
    }
    
    .nav-btn.active {
      background: linear-gradient(135deg, #5bb200 0%, #7cd500 100%);
    }
    
    .result-modal {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.8);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 100;
    }
    
    .result-content {
      background: linear-gradient(135deg, #1a0933 0%, #2d1b4e 100%);
      border-radius: 20px;
      padding: 30px;
      width: 90%;
      max-width: 500px;
      text-align: center;
      position: relative;
    }
    
    .result-close {
      position: absolute;
      top: 15px;
      right: 15px;
      width: 30px;
      height: 30px;
      background: rgba(255, 255, 255, 0.1);
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    }
    
    .result-title {
      font-size: 24px;
      margin-bottom: 20px;
      color: #ffcc00;
    }
    
    .result-gift {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 15px;
      margin: 20px 0;
    }
    
    .gift-icon {
      width: 60px;
      height: 60px;
      background: #5bb200;
      border-radius: 10px;
    }
    
    .gift-details {
      text-align: left;
    }
    
    .gift-name {
      font-size: 18px;
      font-weight: bold;
    }
    
    .gift-count {
      color: #ffcc00;
      font-weight: bold;
    }
    
    .loading {
      text-align: center;
      padding: 20px;
    }
    
    .game-info {
      display: flex;
      justify-content: space-between;
      margin-bottom: 20px;
    }
    
    .info-item {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 10px;
      padding: 10px 15px;
      text-align: center;
    }
    
    .info-label {
      font-size: 12px;
      opacity: 0.8;
    }
    
    .info-value {
      font-size: 18px;
      font-weight: bold;
      color: #5bb200;
    }
    
    .arrow-indicator {
      position: absolute;
      bottom: 80px;
      left: 50%;
      transform: translateX(-50%);
      width: 200px;
      height: 30px;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 5;
    }
    
    .arrow {
      width: 0;
      height: 0;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      border-bottom: 15px solid #ffcc00;
      animation: bounce 1s infinite;
    }
    
    @keyframes bounce {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-10px); }
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <header class="header">
        <button class="btn" @click="currentView = 'lucky'" v-if="currentView === 'game'">幸运榜单</button>
        <button class="btn" @click="currentView = 'game'" v-else>返回游戏</button>
        
        <h1>足球射门游戏</h1>
        
        <button class="btn" @click="currentView = 'rules'" v-if="currentView === 'game'">游戏规则</button>
        <button class="btn" @click="currentView = 'game'" v-else-if="currentView === 'rules'">返回游戏</button>
        <button class="btn" @click="refreshLuckyList" v-else>刷新榜单</button>
      </header>

      <!-- 游戏主界面 -->
      <div v-if="currentView === 'game'" class="game-area">
        <h2 class="game-title">足球射门挑战</h2>
        
        <div class="game-info">
          <div class="info-item">
            <div class="info-label">当前余额</div>
            <div class="info-value">{{ balance }}钻石</div>
          </div>
          <div class="info-item">
            <div class="info-label">免费次数</div>
            <div class="info-value">{{ freeChances }}次</div>
          </div>
        </div>
        
        <div class="goal-area">
          <div class="goal-posts"></div>
          <div class="goal-net"></div>
          
          <div class="goal-targets">
            <div v-for="target in targets" :key="target.id" class="target">
              <div class="target-icon"></div>
              <div class="target-name">{{ target.name }}</div>
            </div>
          </div>
          
          <div class="arrow-indicator" v-if="showArrow">
            <div class="arrow"></div>
          </div>
          
          <div 
            class="football" 
            :style="{ left: footballPosition + 'px' }"
            @touchstart="startDrag"
            @touchmove="dragBall"
            @touchend="endDrag"
          ></div>
        </div>
        
        <div class="controls">
          <button class="control-btn" @click="shoot" :disabled="isShooting">
            {{ isShooting ? '射门中...' : '射门 (200钻石)' }}
          </button>
          <button 
            class="control-btn auto" 
            :class="{ active: isAutoMode }"
            @click="toggleAutoMode"
          >
            {{ isAutoMode ? '关闭自动' : '自动射门' }}
          </button>
        </div>
        
        <div class="records">
          <h3>中奖记录</h3>
          <div class="loading" v-if="records.length === 0">加载中...</div>
          <div v-else>
            <div v-for="record in records" :key="record.id" class="record-item">
              <span>{{ record.user }}</span>
              <b>{{ record.action }}</b>
              <em>{{ record.gift }}x{{ record.count }}</em>
            </div>
          </div>
        </div>
      </div>

      <!-- 幸运榜单 -->
      <div v-if="currentView === 'lucky'" class="game-area">
        <h2 class="game-title">幸运榜单</h2>
        
        <div class="lucky-list">
          <div class="loading" v-if="luckyList.length === 0">加载中...</div>
          <div v-else>
            <div v-for="item in luckyList" :key="item.id" class="lucky-item">
              <div class="lucky-avatar"></div>
              <div class="lucky-info">
                <div class="lucky-name">恭喜 {{ item.user }}</div>
                <div class="lucky-details">
                  消耗{{ item.cost }}钻石获得价值{{ item.value }}钻石的{{ item.gift }}x{{ item.count }}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 规则说明 -->
      <div v-if="currentView === 'rules'" class="game-area">
        <h2 class="game-title">游戏规则</h2>
        
        <div class="rules">
          <div class="rule-section">
            <h3 class="rule-title">玩法说明</h3>
            <p class="rule-text">1. 点击空白区域或移动足球即可左右移动;</p>
            <p class="rule-text">2. 点击射门即成功购买并射门;</p>
            <p class="rule-text">3. 所获的礼物自动进入背包,礼物有效期为90天;</p>
            <p class="rule-text">4. 自动射门次数最多为30次;</p>
          </div>
          
          <div class="rule-section">
            <h3 class="rule-title">奖品概率</h3>
            <table class="rule-table">
              <thead>
                <tr>
                  <th>礼物名称</th>
                  <th>概率</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="prize in prizes" :key="prize.name">
                  <td>{{ prize.name }}</td>
                  <td>{{ prize.probability }}</td>
                </tr>
              </tbody>
            </table>
          </div>
          
          
        </div>
      </div>

      <!-- 中奖结果弹窗 -->
      <div v-if="showResult" class="result-modal">
        <div class="result-content">
          <div class="result-close" @click="showResult = false">×</div>
          <h2 class="result-title">恭喜您获得</h2>
          
          <div class="result-gift">
            <div class="gift-icon"></div>
            <div class="gift-details">
              <div class="gift-name">{{ resultGift.name }}</div>
              <div class="gift-count">x{{ resultGift.count }}</div>
            </div>
          </div>
          
          <p>获得的礼物已存入您的背包</p>
          <button class="control-btn" @click="continueGame">继续游戏</button>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, reactive, computed, onMounted, watch } = Vue;
    
    createApp({
      setup() {
        // 当前视图:game, lucky, rules
        const currentView = ref('game');
        
        // 游戏状态
        const balance = ref(10000);
        const freeChances = ref(5);
        const isShooting = ref(false);
        const isAutoMode = ref(false);
        const footballPosition = ref(200);
        const showArrow = ref(true);
        
        // 数据
        const records = ref([]);
        const luckyList = ref([]);
        
        // 中奖结果
        const showResult = ref(false);
        const resultGift = ref({ name: '', count: 0 });
        
        // 配置
        const targets = ref([
          { id: 1, name: '星愿棒' },
          { id: 2, name: '甜筒' },
          { id: 3, name: '胜利时刻' },
          { id: 4, name: '纯白玫瑰' },
          { id: 5, name: '彩虹蛋糕' },
          { id: 6, name: '杀马特' },
          { id: 7, name: '冠军奖杯' },
          { id: 8, name: '10000钻石' },
          { id: 9, name: '大力神' }
        ]);
        
        const prizes = ref([
          { name: '星愿棒', probability: '28.90%' },
          { name: '甜筒', probability: '26.90%' },
          { name: '纯白玫瑰', probability: '12.50%' },
          { name: '彩虹蛋糕', probability: '10.10%' },
          { name: '杀马特', probability: '9.10%' },
          { name: '冠军奖杯', probability: '7.30%' },
          { name: '胜利时刻', probability: '5.10%' },
          { name: '10000钻石', probability: '0.04%' },
          { name: '大力神', probability: '0.06%' }
        ]);
        
        // 模拟数据
        const mockRecords = [
          { id: 1, user: '用户A', action: '射门获得', gift: '星愿棒', count: 1 },
          { id: 2, user: '用户B', action: '自动射门获得', gift: '甜筒', count: 2 },
          { id: 3, user: '用户C', action: '射门获得', gift: '纯白玫瑰', count: 1 },
          { id: 4, user: '用户D', action: '射门获得', gift: '彩虹蛋糕', count: 1 },
          { id: 5, user: '用户E', action: '射门获得', gift: '冠军奖杯', count: 1 }
        ];
        
        const mockLuckyList = [
          { id: 1, user: '玩家一号', cost: '2800', value: '10000', gift: '大力神', count: 1 },
          { id: 2, user: '足球小将', cost: '1500', value: '5000', gift: '胜利时刻', count: 1 },
          { id: 3, user: '射门高手', cost: '2000', value: '8000', gift: '10000钻石', count: 1 },
          { id: 4, user: '绿茵传奇', cost: '1200', value: '3000', gift: '冠军奖杯', count: 1 },
          { id: 5, user: '球场之星', cost: '800', value: '2000', gift: '彩虹蛋糕', count: 2 }
        ];
        
        // 拖拽相关变量
        let isDragging = false;
        let startX = 0;
        let startLeft = 0;
        
        // 方法
        const startDrag = (e) => {
          isDragging = true;
          startX = e.touches[0].clientX;
          startLeft = footballPosition.value;
          showArrow.value = false;
        };
        
        const dragBall = (e) => {
          if (!isDragging) return;
          
          const currentX = e.touches[0].clientX;
          const deltaX = currentX - startX;
          let newPosition = startLeft + deltaX;
          
          // 限制足球移动范围
          if (newPosition < 50) newPosition = 50;
          if (newPosition > 350) newPosition = 350;
          
          footballPosition.value = newPosition;
        };
        
        const endDrag = () => {
          isDragging = false;
        };
        
        const shoot = () => {
          if (isShooting.value) return;
          
          isShooting.value = true;
          
          // 模拟API请求
          setTimeout(() => {
            // 随机选择一个奖品
            const randomIndex = Math.floor(Math.random() * targets.value.length);
            const target = targets.value[randomIndex];
            
            // 更新余额
            balance.value -= 200;
            
            // 显示结果
            resultGift.value = {
              name: target.name,
              count: Math.floor(Math.random() * 3) + 1
            };
            showResult.value = true;
            isShooting.value = false;
            
            // 添加记录
            records.value.unshift({
              id: Date.now(),
              user: '您',
              action: '射门获得',
              gift: target.name,
              count: resultGift.value.count
            });
          }, 1000);
        };
        
        const toggleAutoMode = () => {
          isAutoMode.value = !isAutoMode.value;
          
          if (isAutoMode.value) {
            // 开始自动射门
            autoShoot();
          }
        };
        
        const autoShoot = () => {
          if (!isAutoMode.value) return;
          
          shoot();
          
          // 设置下一次自动射门
          setTimeout(() => {
            if (isAutoMode.value) {
              autoShoot();
            }
          }, 2000);
        };
        
        const continueGame = () => {
          showResult.value = false;
          
          // 重置足球位置
          footballPosition.value = 200;
          showArrow.value = true;
        };
        
        const refreshLuckyList = () => {
          // 模拟API请求
          setTimeout(() => {
            luckyList.value = [...mockLuckyList].sort(() => Math.random() - 0.5);
          }, 500);
        };
        
        // 初始化
        onMounted(() => {
          // 加载初始数据
          records.value = mockRecords;
          luckyList.value = mockLuckyList;
        });
        
        return {
          currentView,
          balance,
          freeChances,
          isShooting,
          isAutoMode,
          footballPosition,
          showArrow,
          records,
          luckyList,
          showResult,
          resultGift,
          targets,
          prizes,
          startDrag,
          dragBall,
          endDrag,
          shoot,
          toggleAutoMode,
          continueGame,
          refreshLuckyList
        };
      }
    }).mount('#app');
  </script>
</body>
</html>

每天分享一个开发知识点,关注前端老宋,走入不一样的代码世界!!!