火柴人踢任意球

📅 2026/6/20 4:19:27 👤 管理员 👁 次浏览
火柴人踢任意球
!DOCTYPE htmlhtml langzh-CNheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0, user-scalablenotitle⚽ 火柴人踢任意球 - 精准射门挑战/titlestyle* {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/headbodydivdiv classgame-containercanvas idgameCanvas width1000 height400/canvasdiv classinfo-paneldiv classscore-box⚽ 任意球得分 span idscoreValue0/span/divdiv classmessage-area idgameMessage✨ 按住并拖动 → 踢出弧线 ✨/divdiv classcontrols-hint️ 鼠标拖拽方向力度 nbsp;|nbsp;button idresetGameBtn 重置比赛/button/div/divfooter⚡ 按住鼠标从足球位置向外拖动 → 释放射门! 力度看拖拽距离 (越远越暴力)/footer/div/divscript(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, // 身体中心xbodyY: 280, // 身体中心yfootX: 195, // 踢球脚位置XfootY: 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(x9, y-31);ctx.lineTo(x3, y-30);ctx.stroke();// 身体线ctx.beginPath();ctx.moveTo(x, y-7);ctx.lineTo(x, y17);ctx.stroke();// 腿if(isKicking){// 踢球动作: 右腿高抬ctx.beginPath();ctx.moveTo(x, y17);ctx.lineTo(x20, y32);ctx.stroke();ctx.beginPath();ctx.moveTo(x, y17);ctx.lineTo(x-12, y30);ctx.stroke();// 手臂摆动ctx.beginPath();ctx.moveTo(x, y);ctx.lineTo(x-15, y-5);ctx.stroke();ctx.beginPath();ctx.moveTo(x, y);ctx.lineTo(x18, y2);ctx.stroke();} else {// 普通站立ctx.beginPath();ctx.moveTo(x, y17);ctx.lineTo(x-8, y34);ctx.stroke();ctx.beginPath();ctx.moveTo(x, y17);ctx.lineTo(x8, y34);ctx.stroke();// 手臂自然ctx.beginPath();ctx.moveTo(x, y);ctx.lineTo(x-12, y6);ctx.stroke();ctx.beginPath();ctx.moveTo(x, y);ctx.lineTo(x12, y6);ctx.stroke();}// 球衣号码 (帅气)ctx.font bold 12px Segoe UI;ctx.fillStyle #E63946;ctx.shadowBlur 0;ctx.fillText(10, x-5, y5);}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(x3, y-2);ctx.lineTo(x4, y3);ctx.lineTo(x, y6);ctx.lineTo(x-4, y3);ctx.fillStyle #33333399;ctx.fill();ctx.beginPath();ctx.moveTo(x, y-5);ctx.lineTo(x3, y-1);ctx.lineTo(x, y1);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.xGOAL_AREA.w, offY4);ctx.stroke();ctx.beginPath();ctx.moveTo(GOAL_AREA.xGOAL_AREA.w, offY);ctx.lineTo(GOAL_AREA.x, offY4);ctx.stroke();}ctx.fillStyle #FFE48466;for(let i0;i3;i){ctx.fillRect(GOAL_AREA.x8, GOAL_AREA.y20 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.y16);ctx.stroke();ctx.beginPath();ctx.moveTo(w.x, w.y1);ctx.lineTo(w.x-8, w.y12);ctx.stroke();ctx.beginPath();ctx.moveTo(w.x, w.y1);ctx.lineTo(w.x8, w.y12);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.x7, 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 i0;i180;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.3powerPercent*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.touches[0].clientX;clientY e.touches[0].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