js实现投篮
使用Canvas绘制投篮动画
创建一个HTML文件并添加Canvas元素,通过JavaScript绘制篮球和篮筐的动画效果。以下是一个简单的投篮动画实现:
<!DOCTYPE html>
<html>
<head>
<title>投篮动画</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="basketballCanvas" width="800" height="500"></canvas>
<script>
const canvas = document.getElementById('basketballCanvas');
const ctx = canvas.getContext('2d');
// 篮球属性
let ball = {
x: 100,
y: 400,
radius: 20,
color: 'orange',
speedX: 5,
speedY: -15,
gravity: 0.5
};
// 篮筐属性
const hoop = {
x: 600,
y: 200,
width: 80,
height: 5
};
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
// 篮球纹理
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI, false);
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
ctx.closePath();
}
function drawHoop() {
ctx.fillStyle = 'red';
ctx.fillRect(hoop.x, hoop.y, hoop.width, hoop.height);
// 篮筐支撑
ctx.fillRect(hoop.x + hoop.width - 5, hoop.y, 5, 30);
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新篮球位置
ball.x += ball.speedX;
ball.y += ball.speedY;
ball.speedY += ball.gravity;
// 边界检测
if (ball.y + ball.radius > canvas.height) {
ball.y = canvas.height - ball.radius;
ball.speedY *= -0.7; // 反弹
}
// 篮筐碰撞检测
if (ball.x + ball.radius > hoop.x &&
ball.x - ball.radius < hoop.x + hoop.width &&
ball.y + ball.radius > hoop.y &&
ball.y - ball.radius < hoop.y + hoop.height) {
ball.speedY *= -0.5;
}
drawHoop();
drawBall();
requestAnimationFrame(update);
}
update();
</script>
</body>
</html>
使用物理引擎实现更真实的投篮
对于更真实的物理效果,可以使用Matter.js物理引擎:

<!DOCTYPE html>
<html>
<head>
<title>物理投篮</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="physicsCanvas"></canvas>
<button id="shoot">投篮</button>
<script>
const Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies;
const engine = Engine.create();
const render = Render.create({
canvas: document.getElementById('physicsCanvas'),
engine: engine,
options: {
width: 800,
height: 500,
wireframes: false
}
});
// 创建地面
const ground = Bodies.rectangle(400, 490, 810, 20, { isStatic: true });
// 创建篮筐
const hoop = Bodies.rectangle(600, 200, 80, 10, { isStatic: true });
const backboard = Bodies.rectangle(680, 215, 10, 50, { isStatic: true });
// 篮球
const ball = Bodies.circle(100, 400, 20, {
restitution: 0.8,
render: {
fillStyle: 'orange',
strokeStyle: 'black',
lineWidth: 2
}
});
World.add(engine.world, [ground, hoop, backboard, ball]);
Engine.run(engine);
Render.run(render);
document.getElementById('shoot').addEventListener('click', function() {
Matter.Body.setPosition(ball, { x: 100, y: 400 });
Matter.Body.setVelocity(ball, { x: 15, y: -20 });
Matter.Body.setAngularVelocity(ball, 0.1);
});
</script>
</body>
</html>
添加投篮抛物线计算
投篮时需要考虑抛物线轨迹,可以使用以下公式计算投篮角度和力度:
$$ y = x \tanθ - \frac{gx^2}{2v^2 \cos^2θ} $$

其中θ是投篮角度,v是初始速度,g是重力加速度。
function calculateTrajectory(angle, power) {
const g = 0.5; // 重力
const angleRad = angle * Math.PI / 180; // 角度转弧度
const v = power; // 初始速度
let points = [];
for (let x = 0; x < 800; x += 10) {
const y = x * Math.tan(angleRad) - (g * x * x) / (2 * v * v * Math.cos(angleRad) * Math.cos(angleRad));
points.push({ x: ball.x + x, y: ball.y - y });
}
return points;
}
添加用户交互控制
让用户可以通过鼠标或触摸屏控制投篮力度和角度:
let isDragging = false;
let startPos = { x: 0, y: 0 };
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
startPos = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
isDragging = true;
});
canvas.addEventListener('mouseup', (e) => {
if (!isDragging) return;
const rect = canvas.getBoundingClientRect();
const endPos = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
// 计算力度和角度
const dx = startPos.x - endPos.x;
const dy = startPos.y - endPos.y;
const power = Math.min(Math.sqrt(dx * dx + dy * dy) / 10, 30);
const angle = Math.atan2(dy, dx);
// 设置篮球速度
ball.speedX = power * Math.cos(angle);
ball.speedY = power * Math.sin(angle);
isDragging = false;
});





