DS制作的小球动画
本帖最后由 马黑黑 于 2025-2-26 13:17 编辑 <br /><br /><svg id="container" width="500" height="500" style="border: 1px solid #ccc"></svg><div style="margin-top: 10px">
<button id="pauseBtn">暂停</button>
<button id="resumeBtn">继续</button>
</div>
<script>
const svg = document.getElementById('container');
const centerX = 250;
const centerY = 250;
const R = 200; // 大球半径
const r = 5; // 小球半径
const balls = [];
const numBalls = 50;
// 物理参数
const physics = {
boundaryDampening: 0.95,// 边界碰撞能量损失
collisionDampening: 0.98, // 小球碰撞能量损失
maxSpeed: 4, // 最大速度限制
repelDistance: 2*r + 2 // 碰撞检测距离
};
// 动画控制变量
let isAnimating = true;
let animationFrameId = null;
// 创建大球容器
const container = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
container.setAttribute('cx', centerX);
container.setAttribute('cy', centerY);
container.setAttribute('r', R);
container.setAttribute('fill', 'none');
container.setAttribute('stroke', '#333');
svg.appendChild(container);
// 创建小球
function createBall() {
const angle = Math.random() * Math.PI * 2;
const radius = Math.random() * (R - r - 10);
const ball = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
ball.setAttribute('cx', x);
ball.setAttribute('cy', y);
ball.setAttribute('r', r);
ball.setAttribute('fill', `hsl(${Math.random()*360}, 70%, 50%)`);
return {
element: ball,
x,
y,
vx: 0,
vy: 0
};
}
// 初始化小球
for (let i = 0; i < numBalls; i++) {
const ball = createBall();
svg.appendChild(ball.element);
// 设置随机初始速度
const speed = 1 + Math.random() * 2;
const dir = Math.random() * Math.PI * 2;
ball.vx = Math.cos(dir) * speed;
ball.vy = Math.sin(dir) * speed;
balls.push(ball);
}
// 碰撞响应函数
function handleCollision(b1, b2) {
// 计算相对位置
const dx = b2.x - b1.x;
const dy = b2.y - b1.y;
const distance = Math.sqrt(dx*dx + dy*dy);
// 碰撞法线方向
const nx = dx / distance;
const ny = dy / distance;
// 相对速度
const relativeVelocity = {
x: b1.vx - b2.vx,
y: b1.vy - b2.vy
};
// 速度沿法线方向的分量
const speedAlongNormal = relativeVelocity.x * nx + relativeVelocity.y * ny;
// 确保小球正在接近(避免重复处理)
if (speedAlongNormal > 0) return;
// 弹性系数
const e = physics.collisionDampening;
// 冲量计算
const impulse = -(1 + e) * speedAlongNormal / (1/1 + 1/1); // 质量都为1
// 应用冲量
b1.vx -= impulse * nx;
b1.vy -= impulse * ny;
b2.vx += impulse * nx;
b2.vy += impulse * ny;
// 位置修正(防止重叠)
const penetration = 2*r - distance;
const correction = penetration / (1/1 + 1/1) * 0.8;
b1.x -= nx * correction;
b1.y -= ny * correction;
b2.x += nx * correction;
b2.y += ny * correction;
}
// 边界碰撞检测
function checkBoundary(ball) {
const dx = ball.x - centerX;
const dy = ball.y - centerY;
const distance = Math.sqrt(dx*dx + dy*dy);
const maxDistance = R - r;
if (distance > maxDistance) {
const nx = dx / distance;
const ny = dy / distance;
// 位置修正
ball.x = centerX + nx * maxDistance;
ball.y = centerY + ny * maxDistance;
// 速度反射
const dot = ball.vx * nx + ball.vy * ny;
ball.vx = (ball.vx - 2 * dot * nx) * physics.boundaryDampening;
ball.vy = (ball.vy - 2 * dot * ny) * physics.boundaryDampening;
}
}
// 速度限制
function clampSpeed(ball) {
const speed = Math.sqrt(ball.vx*ball.vx + ball.vy*ball.vy);
if (speed > physics.maxSpeed) {
ball.vx = ball.vx / speed * physics.maxSpeed;
ball.vy = ball.vy / speed * physics.maxSpeed;
}
}
// 动画循环
function animate() {
// 更新位置
balls.forEach(ball => {
ball.x += ball.vx;
ball.y += ball.vy;
});
// 检测小球碰撞(双重循环优化版)
for (let i = 0; i < balls.length; i++) {
const b1 = balls[ i ];
// 先检测边界碰撞
checkBoundary(b1);
// 检测与其他小球的碰撞
for (let j = i + 1; j < balls.length; j++) {
const b2 = balls;
// 快速距离平方检测
const dx = b2.x - b1.x;
const dy = b2.y - b1.y;
const distSq = dx*dx + dy*dy;
if (distSq < physics.repelDistance*physics.repelDistance) {
handleCollision(b1, b2);
}
}
// 限制最大速度
clampSpeed(b1);
// 更新显示
balls.forEach(b => {
b.element.setAttribute('cx', b.x);
b.element.setAttribute('cy', b.y);
});
}
// 条件请求动画帧
if (isAnimating) {
animationFrameId = requestAnimationFrame(animate);
}
}
// 初始化启动动画
animate();
// 添加控制事件监听
document.getElementById('pauseBtn').addEventListener('click', () => {
isAnimating = false;
cancelAnimationFrame(animationFrameId); // 立即停止当前帧
});
document.getElementById('resumeBtn').addEventListener('click', () => {
if (!isAnimating) {
isAnimating = true;
animate(); // 重新启动动画循环
}
});
</script> 马黑黑 发表于 2025-2-7 20:00
就是出不了高速路口呀
添堵完毕,路上还好{:4_173:} 花飞飞 发表于 2025-2-15 12:47
添堵完毕,路上还好
坐飞机的省事,空中木有服务区和出口啥的 马黑黑 发表于 2025-2-15 13:47
坐飞机的省事,空中木有服务区和出口啥的
嘿嘿,那是我时间点选得好。。 花飞飞 发表于 2025-2-16 20:06
嘿嘿,那是我时间点选得好。。
{:4_191:} 马黑黑 发表于 2025-2-16 20:15
得意一个{:4_205:} 花飞飞 发表于 2025-2-16 20:23
得意一个
必须的