马黑黑 发表于 2025-2-7 20:00

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-15 12:47

马黑黑 发表于 2025-2-7 20:00
就是出不了高速路口呀

添堵完毕,路上还好{:4_173:}

马黑黑 发表于 2025-2-15 13:47

花飞飞 发表于 2025-2-15 12:47
添堵完毕,路上还好

坐飞机的省事,空中木有服务区和出口啥的

花飞飞 发表于 2025-2-16 20:06

马黑黑 发表于 2025-2-15 13:47
坐飞机的省事,空中木有服务区和出口啥的

嘿嘿,那是我时间点选得好。。

马黑黑 发表于 2025-2-16 20:15

花飞飞 发表于 2025-2-16 20:06
嘿嘿,那是我时间点选得好。。

{:4_191:}

花飞飞 发表于 2025-2-16 20:23

马黑黑 发表于 2025-2-16 20:15


得意一个{:4_205:}

马黑黑 发表于 2025-2-16 20:24

花飞飞 发表于 2025-2-16 20:23
得意一个

必须的
页: 1 2 3 4 5 6 7 [8]
查看完整版本: 红米14C售馨