凌云志久 发表于 2025-4-15 11:26:44

编写一个html页面,一个小球在旋转的六边形内弹跳,小球要受到重力、摩擦力的影响

GLM-4-32B

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>六边形弹跳小球</title>
    <style>
      body {
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
            font-family: 'Arial', sans-serif;
            overflow: hidden;
      }
      
      .container {
            position: relative;
            width: 100%;
            max-width: 800px;
            text-align: center;
      }
      
      canvas {
            background-color: #fff;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            border-radius: 8px;
            margin-bottom: 20px;
      }
      
      .controls {
            margin-top: 20px;
            display: flex;
            justify-content: center;
            gap: 15px;
      }
      
      button {
            padding: 8px 16px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
      }
      
      button:hover {
            background-color: #45a049;
      }
      
      .slider-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-top: 10px;
      }
      
      .slider-container label {
            width: 120px;
            text-align: right;
      }
      
      input {
            width: 200px;
      }
      
      .value-display {
            width: 50px;
            text-align: left;
      }
    </style>
</head>
<body>
    <div class="container">
      <h1>六边形弹跳小球模拟</h1>
      <canvas id="canvas" width="600" height="600"></canvas>
      
      <div class="controls">
            <button id="resetBtn">重置</button>
            <button id="pauseBtn">暂停/继续</button>
      </div>
      
      <div class="slider-container">
            <label for="gravitySlider">重力大小:</label>
            <input type="range" id="gravitySlider" min="0" max="1" step="0.01" value="0.2">
            <span class="value-display" id="gravityValue">0.2</span>
      </div>
      
      <div class="slider-container">
            <label for="frictionSlider">摩擦系数:</label>
            <input type="range" id="frictionSlider" min="0" max="0.1" step="0.001" value="0.01">
            <span class="value-display" id="frictionValue">0.01</span>
      </div>
      
      <div class="slider-container">
            <label for="rotationSlider">旋转速度:</label>
            <input type="range" id="rotationSlider" min="0" max="0.05" step="0.001" value="0.01">
            <span class="value-display" id="rotationValue">0.01</span>
      </div>
    </div>

    <script>
      // 获取Canvas元素和上下文
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      
      // 获取控制元素
      const resetBtn = document.getElementById('resetBtn');
      const pauseBtn = document.getElementById('pauseBtn');
      const gravitySlider = document.getElementById('gravitySlider');
      const frictionSlider = document.getElementById('frictionSlider');
      const rotationSlider = document.getElementById('rotationSlider');
      const gravityValue = document.getElementById('gravityValue');
      const frictionValue = document.getElementById('frictionValue');
      const rotationValue = document.getElementById('rotationValue');
      
      // 初始化参数
      let isPaused = false;
      let gravity = parseFloat(gravitySlider.value);
      let friction = parseFloat(frictionSlider.value);
      let rotationSpeed = parseFloat(rotationSlider.value);
      let angle = 0;
      
      // 六边形参数
      const hexRadius = 200;
      const hexCenterX = canvas.width / 2;
      const hexCenterY = canvas.height / 2;
      
      // 小球参数
      let ball = {
            x: hexCenterX,
            y: hexCenterY,
            radius: 15,
            velocityX: 3,
            velocityY: 0,
            color: '#3498db'
      };
      
      // 计算六边形的顶点
      function calculateHexVertices() {
            const vertices = [];
            for (let i = 0; i < 6; i++) {
                const vertexAngle = angle + i * Math.PI / 3;
                const x = hexCenterX + hexRadius * Math.cos(vertexAngle);
                const y = hexCenterY + hexRadius * Math.sin(vertexAngle);
                vertices.push({ x, y });
            }
            return vertices;
      }
      
      // 绘制六边形
      function drawHexagon() {
            const vertices = calculateHexVertices();
            
            ctx.beginPath();
            ctx.moveTo(vertices.x, vertices.y);
            for (let i = 1; i < 6; i++) {
                ctx.lineTo(vertices.x, vertices.y);
            }
            ctx.closePath();
            ctx.strokeStyle = '#2c3e50';
            ctx.lineWidth = 3;
            ctx.stroke();
            
            // 填充六边形(半透明)
            ctx.fillStyle = 'rgba(236, 240, 241, 0.5)';
            ctx.fill();
            
            return vertices;
      }
      
      // 绘制小球
      function drawBall() {
            ctx.beginPath();
            ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
            ctx.fillStyle = ball.color;
            ctx.fill();
            ctx.strokeStyle = '#2980b9';
            ctx.lineWidth = 2;
            ctx.stroke();
      }
      
      // 检测小球与六边形的碰撞
      function checkCollision(vertices) {
            // 检查小球是否在六边形内部
            function isPointInHexagon(point, vertices) {
                let inside = false;
                for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
                  const xi = vertices.x, yi = vertices.y;
                  const xj = vertices.x, yj = vertices.y;
                  
                  const intersect = ((yi > point.y) !== (yj > point.y))
                        && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
                  if (intersect) inside = !inside;
                }
                return inside;
            }
            
            // 如果小球在六边形外部,找到最近的边并反弹
            if (!isPointInHexagon(ball, vertices)) {
                // 找到最近的边
                let minDistance = Infinity;
                let closestEdge = null;
               
                for (let i = 0; i < 6; i++) {
                  const j = (i + 1) % 6;
                  const edge = {
                        start: vertices,
                        end: vertices
                  };
                  
                  // 计算小球到边的距离
                  const distance = distanceToLine(ball, edge);
                  if (distance < minDistance) {
                        minDistance = distance;
                        closestEdge = edge;
                  }
                }
               
                if (closestEdge) {
                  // 计算边的法向量
                  const edgeVector = {
                        x: closestEdge.end.x - closestEdge.start.x,
                        y: closestEdge.end.y - closestEdge.start.y
                  };
                  
                  // 法向量(垂直于边)
                  const normal = {
                        x: -edgeVector.y,
                        y: edgeVector.x
                  };
                  
                  // 单位化法向量
                  const normalLength = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
                  normal.x /= normalLength;
                  normal.y /= normalLength;
                  
                  // 确保法向量指向六边形内部
                  const centerToBall = {
                        x: ball.x - hexCenterX,
                        y: ball.y - hexCenterY
                  };
                  const dotProduct = normal.x * centerToBall.x + normal.y * centerToBall.y;
                  
                  if (dotProduct > 0) {
                        normal.x = -normal.x;
                        normal.y = -normal.y;
                  }
                  
                  // 计算反弹后的速度
                  const dot = ball.velocityX * normal.x + ball.velocityY * normal.y;
                  ball.velocityX = ball.velocityX - 2 * dot * normal.x;
                  ball.velocityY = ball.velocityY - 2 * dot * normal.y;
                  
                  // 应用摩擦力
                  ball.velocityX *= (1 - friction);
                  ball.velocityY *= (1 - friction);
                  
                  // 将小球推回六边形内部
                  const overlap = ball.radius - minDistance;
                  if (overlap > 0) {
                        ball.x += normal.x * overlap;
                        ball.y += normal.y * overlap;
                  }
                }
            }
      }
      
      // 计算点到线段的距离
      function distanceToLine(point, line) {
            const A = point.x - line.start.x;
            const B = point.y - line.start.y;
            const C = line.end.x - line.start.x;
            const D = line.end.y - line.start.y;
            
            const dot = A * C + B * D;
            const lenSq = C * C + D * D;
            let param = -1;
            
            if (lenSq !== 0) {
                param = dot / lenSq;
            }
            
            let xx, yy;
            
            if (param < 0) {
                xx = line.start.x;
                yy = line.start.y;
            } else if (param > 1) {
                xx = line.end.x;
                yy = line.end.y;
            } else {
                xx = line.start.x + param * C;
                yy = line.start.y + param * D;
            }
            
            const dx = point.x - xx;
            const dy = point.y - yy;
            
            return Math.sqrt(dx * dx + dy * dy);
      }
      
      // 更新小球位置
      function updateBall() {
            // 应用重力
            ball.velocityY += gravity;
            
            // 更新位置
            ball.x += ball.velocityX;
            ball.y += ball.velocityY;
            
            // 检测碰撞
            const vertices = calculateHexVertices();
            checkCollision(vertices);
      }
      
      // 动画循环
      function animate() {
            if (!isPaused) {
                // 清除画布
                ctx.clearRect(0, 0, canvas.width, canvas.height);
               
                // 更新旋转角度
                angle += rotationSpeed;
               
                // 绘制六边形
                const vertices = drawHexagon();
               
                // 更新和绘制小球
                updateBall();
                drawBall();
            }
            
            requestAnimationFrame(animate);
      }
      
      // 重置小球
      function resetBall() {
            ball.x = hexCenterX;
            ball.y = hexCenterY;
            ball.velocityX = 3;
            ball.velocityY = 0;
            angle = 0;
      }
      
      // 事件监听器
      resetBtn.addEventListener('click', resetBall);
      
      pauseBtn.addEventListener('click', function() {
            isPaused = !isPaused;
      });
      
      gravitySlider.addEventListener('input', function() {
            gravity = parseFloat(this.value);
            gravityValue.textContent = gravity.toFixed(2);
      });
      
      frictionSlider.addEventListener('input', function() {
            friction = parseFloat(this.value);
            frictionValue.textContent = friction.toFixed(3);
      });
      
      rotationSlider.addEventListener('input', function() {
            rotationSpeed = parseFloat(this.value);
            rotationValue.textContent = rotationSpeed.toFixed(3);
      });
      
      // 初始化显示值
      gravityValue.textContent = gravity.toFixed(2);
      frictionValue.textContent = friction.toFixed(3);
      rotationValue.textContent = rotationSpeed.toFixed(3);
      
      // 开始动画
      animate();
    </script>
</body>
</html>
页: [1]
查看完整版本: 编写一个html页面,一个小球在旋转的六边形内弹跳,小球要受到重力、摩擦力的影响