火柴人踢任意球

<!DOCTYPE html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

<title>⚽ 火柴人踢任意球 - 精准射门挑战</title>

<style>

* {

user-select: none;

-webkit-tap-highlight-color: transparent;

}

body {

background: linear-gradient(145deg, #1a472a 0%, #0e2a1a 100%);

min-height: 100vh;

display: flex;

justify-content: center;

align-items: center;

font-family: 'Segoe UI', 'Poppins', 'Fredoka One', 'Courier New', monospace;

margin: 0;

padding: 20px;

}

.game-container {

background: #2e7d32;

border-radius: 48px;

padding: 20px 25px 25px 25px;

box-shadow: 0 20px 35px rgba(0, 0, 0, 0.5), inset 0 1px 4px rgba(255, 255, 255, 0.3);

}

canvas {

display: block;

margin: 0 auto;

border-radius: 28px;

box-shadow: 0 12px 28px black;

cursor: crosshair;

background-color: #3ba53b;

background-image: radial-gradient(circle at 10% 30%, rgba(255,255,200,0.15) 2%, transparent 2.5%);

background-size: 28px 28px;

}

.info-panel {

display: flex;

justify-content: space-between;

align-items: baseline;

margin-top: 20px;

gap: 20px;

flex-wrap: wrap;

background: rgba(0, 0, 0, 0.55);

backdrop-filter: blur(8px);

border-radius: 60px;

padding: 8px 25px;

color: #f9f3c1;

text-shadow: 2px 2px 0 #2c5e1a;

}

.score-box {

background: #000000aa;

padding: 5px 18px;

border-radius: 40px;

font-size: 1.8rem;

font-weight: bold;

letter-spacing: 2px;

}

.score-box span {

font-size: 2.2rem;

color: #ffd966;

margin-right: 6px;

}

.message-area {

background: #1e2a1ae0;

padding: 6px 20px;

border-radius: 32px;

font-size: 1.1rem;

font-weight: bold;

font-family: monospace;

backdrop-filter: blur(4px);

transition: 0.2s;

}

.controls-hint {

background: #3c280fe0;

border-radius: 32px;

padding: 6px 20px;

font-size: 0.85rem;

font-weight: bold;

display: flex;

align-items: center;

gap: 12px;

}

button {

background: #ffb347;

border: none;

font-weight: bold;

font-size: 1rem;

padding: 6px 20px;

border-radius: 40px;

cursor: pointer;

font-family: inherit;

transition: all 0.1s ease;

box-shadow: 0 3px 0 #a45d2e;

color: #2c1a0a;

}

button:active {

transform: translateY(2px);

box-shadow: 0 1px 0 #a45d2e;

}

@keyframes pulse {

0% { opacity: 0.7; transform: scale(1);}

100% { opacity: 1; transform: scale(1.05);}

}

.power-indicator {

background: #00000077;

border-radius: 20px;

padding: 2px 12px;

font-size: 0.9rem;

}

footer {

font-size: 0.7rem;

text-align: center;

margin-top: 12px;

color: #cfe6cf;

}

</style>

</head>

<body>

<div>

<div class="game-container">

<canvas id="gameCanvas" width="1000" height="400"></canvas>

<div class="info-panel">

<div class="score-box">⚽ 任意球得分 <span id="scoreValue">0</span></div>

<div class="message-area" id="gameMessage">✨ 按住并拖动 → 踢出弧线 ✨</div>

<div class="controls-hint">

🖱️ 鼠标拖拽方向+力度 &nbsp;|&nbsp;

<button id="resetGameBtn">🔄 重置比赛</button>

</div>

</div>

<footer>⚡ 按住鼠标从足球位置向外拖动 → 释放射门! 力度看拖拽距离 (越远越暴力)</footer>

</div>

</div>

<script>

(function(){

// ---------- CANVAS 元素 ----------

const canvas = document.getElementById('gameCanvas');

const ctx = canvas.getContext('2d');

// ---------- 游戏配置参数 ----------

// 球场边界 (球超出即出界)

const BOUNDS = {

left: 20,

right: 980,

top: 25,

bottom: 375

};

// 火柴人固定位置 (脚部附近)

const STICKMAN = {

bodyX: 180, // 身体中心x

bodyY: 280, // 身体中心y

footX: 195, // 踢球脚位置X

footY: 305 // 踢球脚位置Y

};

// 足球初始位置 (火柴人脚下)

const INIT_BALL_POS = { x: 200, y: 305 };

// 球门区域 (右侧漂亮球门)

const GOAL_AREA = {

x: 870,

y: 215,

w: 60,

h: 110

};

// 物理参数

let ballPos = { x: INIT_BALL_POS.x, y: INIT_BALL_POS.y };

let ballVel = { x: 0, y: 0 };

const FRICTION = 0.985; // 空气摩擦,让球最终停下

const MIN_SPEED = 0.3; // 静止阈值

// 游戏状态

let score = 0;

let canKick = true; // 是否能发起新的一脚 (球静止在脚下)

let isDragging = false; // 是否正在瞄准拖拽

let dragEndPoint = null; // 拖拽结束点 (用于踢球)

let dragCurrentPoint = null; // 当前鼠标位置 (绘制辅助线)

let maxDragDistance = 110; // 最大力度对应拖拽距离

let maxPower = 14.0; // 最大初速度

let gameMessage = "✨ 按住并拖动 → 踢出弧线 ✨";

let messageTimeout = null;

// 动画特效 (踢腿动画临时标记)

let kickSwing = false;

let kickTimer = null;

// 防止重复重置/进球触发标志

let processingReset = false;

// 得分元素

const scoreSpan = document.getElementById('scoreValue');

const msgDiv = document.getElementById('gameMessage');

// ---------- 辅助函数: 更新UI消息 ----------

function setMessage(msg, isError = false) {

gameMessage = msg;

msgDiv.innerText = msg;

if(isError){

msgDiv.style.background = "#a1221ae0";

if(messageTimeout) clearTimeout(messageTimeout);

messageTimeout = setTimeout(() => {

if(!isDragging) msgDiv.style.background = "#1e2a1ae0";

if(gameMessage === msg) setMessage("⚡ 再次拖动射门!", false);

}, 1500);

} else {

msgDiv.style.background = "#1e2a1ae0";

}

}

// 更新分数显示

function updateScoreUI() {

scoreSpan.innerText = score;

}

// ---------- 重置球到脚下 (不改变分数) ----------

function resetBallToFoot(showMsg = true) {

if(processingReset) return;

processingReset = true;

ballPos.x = INIT_BALL_POS.x;

ballPos.y = INIT_BALL_POS.y;

ballVel.x = 0;

ballVel.y = 0;

canKick = true;

if(showMsg){

setMessage("⚽ 球已复位,继续任意球挑战!");

}

// 取消任何踢腿动画

if(kickTimer) clearTimeout(kickTimer);

kickSwing = false;

processingReset = false;

}

// 进球逻辑

function goalScored() {

if(processingReset) return;

processingReset = true;

score++;

updateScoreUI();

setMessage("🎉 漂亮!进球啦! +1分 🎉");

// 进球特效: 闪烁一下 + 复位足球

ballVel.x = 0;

ballVel.y = 0;

ballPos.x = INIT_BALL_POS.x;

ballPos.y = INIT_BALL_POS.y;

canKick = true;

// 小小踢腿动画庆祝

kickSwing = true;

if(kickTimer) clearTimeout(kickTimer);

kickTimer = setTimeout(() => { kickSwing = false; }, 200);

processingReset = false;

}

// 出界 (球飞出场地)

function outOfBounds() {

if(processingReset) return;

processingReset = true;

setMessage("📤 出界了! 重新摆球", true);

ballVel.x = 0;

ballVel.y = 0;

ballPos.x = INIT_BALL_POS.x;

ballPos.y = INIT_BALL_POS.y;

canKick = true;

if(kickTimer) clearTimeout(kickTimer);

kickSwing = false;

processingReset = false;

}

// 检查球是否进入球门区域

function checkGoal() {

// 球心进入门区矩形即算进球 (稍留一点余量让进球判定舒适)

if(ballPos.x + 6 > GOAL_AREA.x && ballPos.x - 6 < GOAL_AREA.x + GOAL_AREA.w &&

ballPos.y + 6 > GOAL_AREA.y && ballPos.y - 6 < GOAL_AREA.y + GOAL_AREA.h) {

// 避免连续触发 (且必须速度不为零或刚进门时)

if(Math.abs(ballVel.x) > 0.2 || Math.abs(ballVel.y) > 0.2){

goalScored();

return true;

}

}

return false;

}

// 检查边界出界 (超出上下左右)

function checkBoundary() {

if(ballPos.x - 7 < BOUNDS.left || ballPos.x + 7 > BOUNDS.right ||

ballPos.y - 7 < BOUNDS.top || ballPos.y + 7 > BOUNDS.bottom) {

outOfBounds();

return true;

}

return false;

}

// 当球几乎静止且不在脚下时, 自动复位到脚下 (方便连续踢)

function checkStillAndReset() {

if(!canKick && Math.abs(ballVel.x) < MIN_SPEED && Math.abs(ballVel.y) < MIN_SPEED && !processingReset) {

// 静止且不在进球/出界复位过程中, 把球重置回发球点

// 前提: 球不在门内 (如果进门早就触发进球了, 不会在这)

if(!(ballPos.x + 6 > GOAL_AREA.x && ballPos.x - 6 < GOAL_AREA.x + GOAL_AREA.w &&

ballPos.y + 6 > GOAL_AREA.y && ballPos.y - 6 < GOAL_AREA.y + GOAL_AREA.h)) {

resetBallToFoot(true);

} else {

// 如果非常规卡进门区但没触发进球?二次保险

if(ballPos.x > GOAL_AREA.x - 10 && ballPos.x < GOAL_AREA.x + GOAL_AREA.w + 10 &&

ballPos.y > GOAL_AREA.y - 10 && ballPos.y < GOAL_AREA.y + GOAL_AREA.h + 10){

goalScored();

} else {

resetBallToFoot(true);

}

}

}

}

// ---------- 踢球核心: 根据拖拽的方向+力度 赋予速度 ----------

function kickBall(dragFrom, dragTo) {

if(!canKick) {

setMessage("⏳ 等球停稳再射门!", true);

return false;

}

// 起始点必须是球当前的位置 (但视觉上起始点应该是足球坐标)

const start = { x: ballPos.x, y: ballPos.y };

let end = { x: dragTo.x, y: dragTo.y };

// 计算方向向量

let dx = end.x - start.x;

let dy = end.y - start.y;

let distance = Math.hypot(dx, dy);

if(distance < 0.1) return false;

// 限制最大拖拽距离来决定力量系数

let powerRatio = Math.min(1.0, distance / maxDragDistance);

let power = 3.0 + powerRatio * (maxPower - 3.0); // 最小也有3的力度保证动感

// 方向单位向量

let dirX = dx / distance;

let dirY = dy / distance;

// 赋予速度

ballVel.x = dirX * power;

ballVel.y = dirY * power;

// 开启踢腿动画

kickSwing = true;

if(kickTimer) clearTimeout(kickTimer);

kickTimer = setTimeout(() => { kickSwing = false; }, 180);

canKick = false;

setMessage("💥 射门! 球飞出去了!");

return true;

}

// ---------- 物理更新每帧 ----------

function updatePhysics() {

if(!canKick){

// 更新位置

ballPos.x += ballVel.x;

ballPos.y += ballVel.y;

// 摩擦力减速

ballVel.x *= FRICTION;

ballVel.y *= FRICTION;

// 边界碰撞前先检测进球 (优先级最高)

if(!checkGoal()){

checkBoundary();

}

// 避免球卡在边界外 (二次矫正)

ballPos.x = Math.min(Math.max(ballPos.x, BOUNDS.left + 5), BOUNDS.right - 5);

ballPos.y = Math.min(Math.max(ballPos.y, BOUNDS.top + 5), BOUNDS.bottom - 5);

// 自然静止后复位

checkStillAndReset();

}

}

// ---------- 绘图模块 (火柴人 + 足球 + 球场 + 球门 + 人墙装饰) ----------

function drawStickman(x, y, isKicking) {

// 主体位置 (x,y) 是躯干中心点

const headR = 13;

// 头部

ctx.beginPath();

ctx.arc(x, y - 20, headR, 0, Math.PI * 2);

ctx.fillStyle = "#FDD7A4";

ctx.fill();

ctx.strokeStyle = "#2C1A0A";

ctx.lineWidth = 2;

ctx.stroke();

// 眼睛 + 表情 (专注)

ctx.fillStyle = "#2F1B0C";

ctx.beginPath();

ctx.arc(x - 5, y - 25, 2, 0, Math.PI*2);

ctx.arc(x + 5, y - 25, 2, 0, Math.PI*2);

ctx.fill();

ctx.fillStyle = "white";

ctx.beginPath();

ctx.arc(x - 5.5, y - 26, 0.7, 0, Math.PI*2);

ctx.arc(x + 4.5, y - 26, 0.7, 0, Math.PI*2);

ctx.fill();

// 眉毛 (斗志)

ctx.beginPath();

ctx.moveTo(x-9, y-31);

ctx.lineTo(x-3, y-30);

ctx.moveTo(x+9, y-31);

ctx.lineTo(x+3, y-30);

ctx.stroke();

// 身体线

ctx.beginPath();

ctx.moveTo(x, y-7);

ctx.lineTo(x, y+17);

ctx.stroke();

// 腿

if(isKicking){

// 踢球动作: 右腿高抬

ctx.beginPath();

ctx.moveTo(x, y+17);

ctx.lineTo(x+20, y+32);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(x, y+17);

ctx.lineTo(x-12, y+30);

ctx.stroke();

// 手臂摆动

ctx.beginPath();

ctx.moveTo(x, y);

ctx.lineTo(x-15, y-5);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(x, y);

ctx.lineTo(x+18, y+2);

ctx.stroke();

} else {

// 普通站立

ctx.beginPath();

ctx.moveTo(x, y+17);

ctx.lineTo(x-8, y+34);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(x, y+17);

ctx.lineTo(x+8, y+34);

ctx.stroke();

// 手臂自然

ctx.beginPath();

ctx.moveTo(x, y);

ctx.lineTo(x-12, y+6);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(x, y);

ctx.lineTo(x+12, y+6);

ctx.stroke();

}

// 球衣号码 (帅气)

ctx.font = "bold 12px 'Segoe UI'";

ctx.fillStyle = "#E63946";

ctx.shadowBlur = 0;

ctx.fillText("10", x-5, y+5);

}

function drawBall(x, y) {

ctx.shadowBlur = 8;

ctx.shadowColor = "black";

ctx.beginPath();

ctx.arc(x, y, 8, 0, Math.PI*2);

ctx.fillStyle = "#F2F2F2";

ctx.fill();

ctx.strokeStyle = "#222";

ctx.lineWidth = 1.5;

ctx.stroke();

// 经典五边形花纹

ctx.beginPath();

ctx.moveTo(x-3, y-2);

ctx.lineTo(x+3, y-2);

ctx.lineTo(x+4, y+3);

ctx.lineTo(x, y+6);

ctx.lineTo(x-4, y+3);

ctx.fillStyle = "#33333399";

ctx.fill();

ctx.beginPath();

ctx.moveTo(x, y-5);

ctx.lineTo(x+3, y-1);

ctx.lineTo(x, y+1);

ctx.lineTo(x-3, y-1);

ctx.fill();

ctx.fillStyle = "#1a1a1a";

ctx.beginPath();

ctx.arc(x-2, y-1, 1, 0, Math.PI*2);

ctx.fill();

ctx.shadowBlur = 0;

}

function drawGoal() {

ctx.save();

ctx.shadowBlur = 0;

ctx.strokeStyle = "#F5E56B";

ctx.lineWidth = 4;

ctx.fillStyle = "#3F7E3Dcc";

ctx.fillRect(GOAL_AREA.x, GOAL_AREA.y, GOAL_AREA.w, GOAL_AREA.h);

ctx.strokeRect(GOAL_AREA.x, GOAL_AREA.y, GOAL_AREA.w, GOAL_AREA.h);

// 球网效果

ctx.beginPath();

ctx.strokeStyle = "#DDDDAA";

ctx.lineWidth = 1;

for(let i = 0; i < 5; i++){

let offY = GOAL_AREA.y + i * (GOAL_AREA.h/5);

ctx.beginPath();

ctx.moveTo(GOAL_AREA.x, offY);

ctx.lineTo(GOAL_AREA.x+GOAL_AREA.w, offY+4);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(GOAL_AREA.x+GOAL_AREA.w, offY);

ctx.lineTo(GOAL_AREA.x, offY+4);

ctx.stroke();

}

ctx.fillStyle = "#FFE48466";

for(let i=0;i<3;i++){

ctx.fillRect(GOAL_AREA.x+8, GOAL_AREA.y+20+ i*30, 8, 12);

}

ctx.restore();

}

// 装饰人墙 (三个简单火柴人剪影,增加任意球氛围)

function drawWall() {

ctx.save();

ctx.globalAlpha = 0.7;

ctx.lineWidth = 1.8;

ctx.strokeStyle = "#2c2e2c";

// 人墙位置在球门与球之间: x 550~650 区间

const wallPositions = [

{x: 560, y: 268}, {x: 610, y: 270}, {x: 660, y: 272}

];

wallPositions.forEach(w => {

ctx.beginPath();

ctx.arc(w.x, w.y-16, 9, 0, Math.PI*2);

ctx.fillStyle = "#38281290";

ctx.fill();

ctx.stroke();

ctx.beginPath();

ctx.moveTo(w.x, w.y-7);

ctx.lineTo(w.x, w.y+16);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(w.x, w.y+1);

ctx.lineTo(w.x-8, w.y+12);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(w.x, w.y+1);

ctx.lineTo(w.x+8, w.y+12);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(w.x, w.y-2);

ctx.lineTo(w.x-7, w.y-10);

ctx.stroke();

ctx.beginPath();

ctx.moveTo(w.x, w.y-2);

ctx.lineTo(w.x+7, w.y-10);

ctx.stroke();

});

ctx.globalAlpha = 1;

ctx.restore();

}

function drawField() {

// 草坪细节

ctx.fillStyle = "#3ba53b";

ctx.fillRect(0,0,canvas.width,canvas.height);

// 草叶纹理

for(let i=0;i<180;i++){

ctx.beginPath();

ctx.moveTo(20 + i*23, 370);

ctx.lineTo(25 + i*23, 355);

ctx.lineTo(30 + i*23, 370);

ctx.fillStyle = "#398c2f";

ctx.fill();

}

// 罚球弧示意

ctx.beginPath();

ctx.arc(190, 305, 45, 0.2, Math.PI - 0.2);

ctx.strokeStyle = "#FFE2A4";

ctx.lineWidth = 2;

ctx.setLineDash(6, 12);

ctx.stroke();

ctx.setLineDash(\[\]);

// 中线装饰

ctx.beginPath();

ctx.moveTo(canvas.width/2, 20);

ctx.lineTo(canvas.width/2, canvas.height-20);

ctx.strokeStyle = "#ffffff90";

ctx.lineWidth = 2;

ctx.stroke();

}

// 拖拽辅助线 + 力量提示

function drawDragAim() {

if(isDragging && dragCurrentPoint && canKick){

const from = { x: ballPos.x, y: ballPos.y };

const to = dragCurrentPoint;

let dist = Math.hypot(to.x - from.x, to.y - from.y);

let limitedX = to.x, limitedY = to.y;

if(dist > maxDragDistance){

let angle = Math.atan2(to.y - from.y, to.x - from.x);

limitedX = from.x + Math.cos(angle) * maxDragDistance;

limitedY = from.y + Math.sin(angle) * maxDragDistance;

}

ctx.beginPath();

ctx.moveTo(from.x, from.y);

ctx.lineTo(limitedX, limitedY);

ctx.strokeStyle = "#FFE484";

ctx.lineWidth = 4;

ctx.shadowBlur = 6;

ctx.stroke();

ctx.beginPath();

ctx.arc(limitedX, limitedY, 8, 0, Math.PI*2);

ctx.fillStyle = "#ffd966cc";

ctx.fill();

// 力量指示圈

let powerPercent = Math.min(1, dist / maxDragDistance);

let radius = 12 + powerPercent * 14;

ctx.beginPath();

ctx.arc(from.x, from.y-15, radius, 0, Math.PI*2);

ctx.fillStyle = `rgba(255, 200, 80, ${0.3+powerPercent*0.4})`;

ctx.fill();

ctx.fillStyle = "white";

ctx.font = "bold 14 monospace";

ctx.shadowBlur = 2;

ctx.fillText(`⚡${Math.floor(powerPercent*100)}%`, from.x-22, from.y-28);

ctx.shadowBlur = 0;

}

}

// 主渲染

function render() {

drawField();

drawGoal();

drawWall();

// 绘制足球

drawBall(ballPos.x, ballPos.y);

// 火柴人 (踢腿动画影响)

drawStickman(STICKMAN.bodyX, STICKMAN.bodyY, kickSwing);

// 额外脚部位置显示足球关联效果

ctx.font = "12px monospace";

ctx.fillStyle = "#f9eebe";

ctx.shadowBlur = 0;

ctx.fillText("⚡任意球大师⚡", 40, 50);

// 如果是拖拽模式显示辅助

drawDragAim();

// 显示操作状态

if(!canKick && !isDragging){

ctx.fillStyle = "#f0e6a0";

ctx.font = "bold 14px monospace";

ctx.fillText("⚽ 球在空中...", ballPos.x-30, ballPos.y-15);

}

if(canKick && !isDragging){

ctx.fillStyle = "#FFF8E7";

ctx.font = "italic 16px 'Segoe UI'";

ctx.shadowBlur = 4;

ctx.fillText("👇 按住鼠标拖拽射门方向! 👇", canvas.width/2-170, 55);

}

}

// ---------- 鼠标/触摸交互 (实现任意球拖拽踢法) ----------

function getCanvasCoords(e) {

const rect = canvas.getBoundingClientRect();

const scaleX = canvas.width / rect.width;

const scaleY = canvas.height / rect.height;

let clientX, clientY;

if(e.touches) {

clientX = e.touches0.clientX;

clientY = e.touches0.clientY;

} else {

clientX = e.clientX;

clientY = e.clientY;

}

let canvasX = (clientX - rect.left) * scaleX;

let canvasY = (clientY - rect.top) * scaleY;

canvasX = Math.min(Math.max(canvasX, 10), canvas.width-10);

canvasY = Math.min(Math.max(canvasY, 20), canvas.height-20);

return { x: canvasX, y: canvasY };

}

function onPointerDown(e) {

e.preventDefault();

if(!canKick) {

setMessage("等球复位再踢下一球⚽", true);

return;

}

isDragging = true;

const pos = getCanvasCoords(e);

dragCurrentPoint = pos;

dragEndPoint = null;

}

function onPointerMove(e) {

if(!isDragging) return;

e.preventDefault();

const pos = getCanvasCoords(e);

dragCurrentPoint = pos;

}

function onPointerUp(e) {

if(!isDragging) return;

e.preventDefault();

if(canKick && dragCurrentPoint) {

const startPoint = { x: ballPos.x, y: ballPos.y };

const endPoint = dragCurrentPoint;

let distRaw = Math.hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y);

if(distRaw > 5) { // 有效拖拽

kickBall(startPoint, endPoint);

} else {

setMessage("➡️ 拖拽距离太短, 用力一点!", true);

}

}

isDragging = false;

dragCurrentPoint = null;

dragEndPoint = null;

}

// 重置游戏 (分数清零 + 球复位)

function fullReset() {

score = 0;

updateScoreUI();

resetBallToFoot(true);

canKick = true;

ballVel = { x: 0, y: 0 };

ballPos = { x: INIT_BALL_POS.x, y: INIT_BALL_POS.y };

setMessage("🔄 比分归零,重新挑战任意球之王!");

if(kickTimer) clearTimeout(kickTimer);

kickSwing = false;

processingReset = false;

}

// 添加事件监听

function initEvents() {

canvas.addEventListener('mousedown', onPointerDown);

window.addEventListener('mousemove', onPointerMove);

window.addEventListener('mouseup', onPointerUp);

canvas.addEventListener('touchstart', onPointerDown, {passive: false});

window.addEventListener('touchmove', onPointerMove, {passive: false});

window.addEventListener('touchend', onPointerUp);

document.getElementById('resetGameBtn').addEventListener('click', () => fullReset());

}

// 动画主循环

function animate() {

updatePhysics();

render();

requestAnimationFrame(animate);

}

// 初始化游戏

function init() {

initEvents();

resetBallToFoot(false);

canKick = true;

score = 0;

updateScoreUI();

animate();

}

init();

})();

</script>

</body>

</html>

相关推荐
晓得迷路了1 小时前
栗子前端技术周刊第 132 期 - date-fns 支持 Temporal、npm 攻击事件、VoidZero...
前端·javascript·css
ct9782 小时前
Promise
前端·javascript·vue.js
怕浪猫2 小时前
Electron 开发实战(十一):自动更新机制|服务架构、公私网更新、版本回滚全解
前端·javascript·electron
AI视觉网奇2 小时前
three-bvh-csg glb分割
开发语言·前端·javascript
很楠爱上2 小时前
TypeScript 核心知识精要
javascript·ubuntu·typescript
rising start2 小时前
五、Vue3 ref 用法 + Props 完整指南
前端·javascript·vue.js
web打印社区2 小时前
前端html转换pdf并静默打印pdf最佳实现路径
前端·javascript·vue.js·electron·html
Curvatureflight2 小时前
浏览器音频采集实践:麦克风权限、降噪、回声消除与 PCM 转换
前端·javascript·音视频·信息与通信·web·pcm
云水一下11 小时前
TypeScript 从零基础到精通(五):高级类型与泛型
前端·javascript·typescript