|
|

楼主 |
发表于 2025-2-26 13:18
|
显示全部楼层
代码
- <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[j];
-
- // 快速距离平方检测
- 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>
复制代码
|
评分
-
| 参与人数 1 | 威望 +30 |
金钱 +60 |
经验 +30 |
收起
理由
|
花飞飞
| + 30 |
+ 60 |
+ 30 |
很给力! |
查看全部评分
|