2026年世界杯还有8个月左右,让我们来讲讲
中国足球圈知名梗盘点
五大联赛都不够排的,中国足球的梗才是最多的
黑色三分钟
兵败吉隆坡
永久退出足球(万达)
恐韩症,黄金一代,超白金一代,断子绝孙脚,郑智化,谢天谢地谢亚龙,髂腰肌,护球像亨利,脸都不要了,还有算错净胜球的离谱操作...
下面让我隆重介绍移动端足球射门的网页小游戏
实现效果如下图
页面布局
- 包含三个主要区域:游戏主界面、规则说明、幸运榜单
- 使用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>
每天分享一个开发知识点,关注前端老宋,走入不一样的代码世界!!!