抛物体运动模拟器——速度角度效果反馈

前言

今天在豆包的AI应用上看到了一个抛物线的功能,其实不仅仅是抛物线,其它的效果也是可以的,我根据基础的示例做了个效果,效果再下图中能看到,使用的是纯HTML+Canvas来完成的,效果还不错,我们用来学习数学和物理都还不错,那么接下来我们来具体看看吧。

目录

前言

在线实操效果

抛物线模型特点

对照表

完整代码块


在线实操效果

运行可以直接看到效果。

抛物线模型特点

直观的用户界面:采用现代卡片式设计,清晰分离控制面板和模拟区域,提供舒适的视觉体验。

完整的物理模拟

支持调整初速度 (5-50 m/s) 和发射角度 (10-80 度)

实时计算并显示最大高度、飞行时间和水平距离

模拟真实的重力加速度效果

丰富的视觉元素

清晰的坐标轴和网格线

平滑的轨迹绘制

物体落地时的爆炸动画效果

渐变色彩和阴影效果增强深度感

轨迹方程展示

显示标准参数方程

实时计算并更新当前轨迹方程

对照表

从初速度10m/s,角度10°开始递增变化,下方是整个对照表。

初速度 (m/s)角度 (°)最大高度 (m)飞行时间 (s)水平距离 (m)轨迹特点
10100.150.353.42低而短的轨迹,几乎是水平飞行
10150.340.535.00略微上扬的短轨迹
10200.600.706.43中等高度的短轨迹
10250.920.867.66明显的抛物线形状,中等距离
10301.281.028.84典型的抛物线轨迹,距离适中
10351.681.189.92高度增加,距离接近最大值
10402.111.3210.88接近 45 度角的最大距离
10452.551.4511.70最大水平距离(理论上 45 度角时最大)
10502.991.5712.35高度继续增加,距离略有下降
10553.411.6812.81高度增加明显,距离开始减少
10603.831.7713.06高度高但距离开始减少
10654.211.8413.07更高的轨迹,水平距离几乎不变
10704.551.8912.82垂直方向分量更大,水平距离减少
10754.831.9212.29几乎是垂直向上,水平距离较小
10805.031.9311.46接近垂直向上,水平距离最小
20100.610.7013.89低而远的轨迹,水平分量大
20151.381.0720.00略微上扬的远距离轨迹
20202.421.4125.71中等高度的远距离轨迹
20253.711.7330.65明显的抛物线形状,远距离
20305.142.0535.36典型的抛物线轨迹,远距离
20356.732.3739.68高度增加,距离接近最大值
20408.462.6543.52接近 45 度角的最大距离
204510.202.9046.80最大水平距离(理论上 45 度角时最大)
205011.953.1549.41高度继续增加,距离略有下降
205513.653.3651.23高度增加明显,距离开始减少
206015.303.5552.25高度高但距离开始减少
206516.853.6952.29更高的轨迹,水平距离几乎不变
207018.203.7951.27垂直方向分量更大,水平距离减少
207519.333.8549.17几乎是垂直向上,水平距离较小
208020.133.8645.83接近垂直向上,水平距离最小
30101.371.0531.25低而远的轨迹,水平分量大
30153.101.6045.00略微上扬的远距离轨迹
30205.452.1257.89中等高度的远距离轨迹
30258.352.6069.09明显的抛物线形状,远距离
303011.573.0779.56典型的抛物线轨迹,远距离
303515.143.5589.27高度增加,距离接近最大值
304019.043.9798.17接近 45 度角的最大距离
304523.054.35105.31最大水平距离(理论上 45 度角时最大)
305027.104.72111.18高度继续增加,距离略有下降
305531.165.04115.26高度增加明显,距离开始减少
306035.175.32117.56高度高但距离开始减少
306539.025.54117.65更高的轨迹,水平距离几乎不变
307042.685.69115.35垂直方向分量更大,水平距离减少
307546.045.78110.64几乎是垂直向上,水平距离较小
308048.995.80103.12接近垂直向上,水平距离最小
40102.431.4055.56低而远的轨迹,水平分量大
40155.502.1380.00略微上扬的远距离轨迹
40209.732.83102.86中等高度的远距离轨迹
402515.103.47124.44明显的抛物线形状,远距离
403021.544.10144.65典型的抛物线轨迹,远距离
403528.994.73163.30高度增加,距离接近最大值
404037.405.30180.34接近 45 度角的最大距离
404546.725.80195.65最大水平距离(理论上 45 度角时最大)
405056.896.30209.12高度继续增加,距离略有下降
405567.896.72220.50高度增加明显,距离开始减少
406079.687.10229.62高度高但距离开始减少
406592.077.39236.33更高的轨迹,水平距离几乎不变
4070104.987.58240.47垂直方向分量更大,水平距离减少
4075118.307.70241.83几乎是垂直向上,水平距离较小
4080131.977.73239.85接近垂直向上,水平距离最小
50103.801.7586.81低而远的轨迹,水平分量大
50158.592.67125.00略微上扬的远距离轨迹
502015.203.54160.71中等高度的远距离轨迹
502523.604.34194.01明显的抛物线形状,远距离
503033.815.13225.79典型的抛物线轨迹,远距离
503545.815.91255.92高度增加,距离接近最大值
504059.606.62284.34接近 45 度角的最大距离
504575.167.25310.93最大水平距离(理论上 45 度角时最大)
505092.347.90335.59高度继续增加,距离略有下降
5055111.188.40358.28高度增加明显,距离开始减少
5060131.578.88378.86高度高但距离开始减少
5065153.399.24397.24更高的轨迹,水平距离几乎不变
5070176.539.48413.23垂直方向分量更大,水平距离减少
5075200.879.63426.66几乎是垂直向上,水平距离较小
5080226.369.67437.37接近垂直向上,水平距离最小

完整代码块

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>抛物体运动模拟器</title>
  <!-- 引入Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- 引入Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  
  <!-- Tailwind配置 -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3B82F6',
            secondary: '#10B981',
            accent: '#F59E0B',
            dark: '#1E293B',
            light: '#F8FAFC'
          },
          fontFamily: {
            inter: ['Inter', 'sans-serif'],
          },
        },
      }
    }
  </script>
  
  <!-- 自定义工具类 -->
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .simulator-shadow {
        box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.1), 0 8px 10px -6px rgba(59, 130, 246, 0.1);
      }
      .slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #3B82F6;
        cursor: pointer;
        box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
      }
      .slider-thumb:hover {
        box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.3);
      }
      .equation-card {
        backdrop-filter: blur(8px);
        background-color: rgba(255, 255, 255, 0.85);
      }
    }
  </style>
  
  <!-- 引入Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>

<body class="bg-gradient-to-br from-light to-gray-100 min-h-screen font-inter text-dark">
  <!-- 页面头部 -->
  <header class="bg-white shadow-md py-4 px-6 fixed w-full z-10 transition-all duration-300" id="header">
    <div class="container mx-auto flex justify-between items-center">
      <div class="flex items-center space-x-2">
        <i class="fa fa-rocket text-primary text-2xl"></i>
        <h1 class="text-xl md:text-2xl font-bold text-dark">抛物体运动模拟器</h1>
      </div>
      <div class="flex items-center space-x-4">
        <button id="info-btn" class="p-2 rounded-full hover:bg-gray-100 transition-colors">
          <i class="fa fa-info-circle text-primary"></i>
        </button>
        <button id="reset-btn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-lg shadow-md hover:shadow-lg transition-all">
          <i class="fa fa-refresh mr-1"></i> 重置
        </button>
      </div>
    </div>
  </header>

  <!-- 主内容区 -->
  <main class="container mx-auto pt-24 pb-16 px-4 md:px-6">
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
      <!-- 控制面板 -->
      <div class="lg:col-span-1 order-2 lg:order-1">
        <div class="bg-white rounded-xl p-6 simulator-shadow h-full">
          <h2 class="text-xl font-semibold mb-4 flex items-center">
            <i class="fa fa-sliders text-primary mr-2"></i> 控制面板
          </h2>
          
          <div class="space-y-6">
            <!-- 初速度控制 -->
            <div class="space-y-2">
              <div class="flex justify-between items-center">
                <label for="velocity" class="text-sm font-medium">初速度 (m/s)</label>
                <span id="velocity-value" class="text-sm font-semibold text-primary">20</span>
              </div>
              <input 
                type="range" 
                id="velocity" 
                min="5" 
                max="50" 
                step="1" 
                value="20" 
                class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
              >
            </div>
            
            <!-- 发射角度控制 -->
            <div class="space-y-2">
              <div class="flex justify-between items-center">
                <label for="angle" class="text-sm font-medium">发射角度 (°)</label>
                <span id="angle-value" class="text-sm font-semibold text-primary">45</span>
              </div>
              <input 
                type="range" 
                id="angle" 
                min="10" 
                max="80" 
                step="1" 
                value="45" 
                class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
              >
            </div>
            
            <!-- 物理参数 -->
            <div class="space-y-2">
              <h3 class="text-sm font-medium">物理参数</h3>
              <div class="grid grid-cols-2 gap-3">
                <div class="bg-gray-50 p-3 rounded-lg">
                  <p class="text-xs text-gray-500">重力加速度</p>
                  <p class="text-sm font-semibold" id="gravity-value">9.8 m/s²</p>
                </div>
                <div class="bg-gray-50 p-3 rounded-lg">
                  <p class="text-xs text-gray-500">最大高度</p>
                  <p class="text-sm font-semibold" id="max-height">--</p>
                </div>
                <div class="bg-gray-50 p-3 rounded-lg">
                  <p class="text-xs text-gray-500">飞行时间</p>
                  <p class="text-sm font-semibold" id="flight-time">--</p>
                </div>
                <div class="bg-gray-50 p-3 rounded-lg">
                  <p class="text-xs text-gray-500">水平距离</p>
                  <p class="text-sm font-semibold" id="horizontal-distance">--</p>
                </div>
              </div>
            </div>
            
            <!-- 操作按钮 -->
            <div class="flex space-x-3 pt-2">
              <button id="start-btn" class="flex-1 bg-secondary hover:bg-secondary/90 text-white py-2 rounded-lg shadow-md hover:shadow-lg transition-all">
                <i class="fa fa-play mr-1"></i> 开始
              </button>
              <button id="pause-btn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-dark py-2 rounded-lg shadow-md hover:shadow-lg transition-all" disabled>
                <i class="fa fa-pause mr-1"></i> 暂停
              </button>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 模拟器和轨迹方程 -->
      <div class="lg:col-span-2 order-1 lg:order-2">
        <div class="relative">
          <!-- 模拟器画布 -->
          <div class="bg-white rounded-xl overflow-hidden simulator-shadow mb-6 relative">
            <canvas id="simulator" class="w-full aspect-[16/9]"></canvas>
            
            <!-- 坐标轴和网格线 -->
            <div class="absolute inset-0 pointer-events-none">
              <div class="absolute bottom-0 left-0 w-full h-0.5 bg-gray-300"></div>
              <div class="absolute bottom-0 left-0 w-0.5 h-full bg-gray-300"></div>
              
              <!-- 刻度标记 -->
              <div class="absolute bottom-0 left-0 w-full flex justify-between px-4 py-2 text-xs text-gray-500">
                <span>0</span>
                <span>10m</span>
                <span>20m</span>
                <span>30m</span>
                <span>40m</span>
                <span>50m</span>
              </div>
              <div class="absolute bottom-0 left-0 h-full flex flex-col justify-between px-2 py-4 text-xs text-gray-500">
                <span class="rotate-90">0</span>
                <span class="rotate-90">5m</span>
                <span class="rotate-90">10m</span>
                <span class="rotate-90">15m</span>
                <span class="rotate-90">20m</span>
              </div>
            </div>
          </div>
          
          <!-- 轨迹方程卡片 -->
          <div class="equation-card p-4 rounded-xl border border-gray-200 shadow-lg">
            <h2 class="text-lg font-semibold mb-2 flex items-center">
              <i class="fa fa-line-chart text-primary mr-2"></i> 轨迹方程
            </h2>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div class="bg-gray-50 p-3 rounded-lg">
                <p class="text-xs text-gray-500">参数方程</p>
                <p class="text-sm font-medium" id="parametric-equation">x = v₀·cos(θ)·t<br>y = v₀·sin(θ)·t - ½·g·t²</p>
              </div>
              <div class="bg-gray-50 p-3 rounded-lg">
                <p class="text-xs text-gray-500">标准方程</p>
                <p class="text-sm font-medium" id="standard-equation">y = tan(θ)·x - (g·x²)/(2·v₀²·cos²(θ))</p>
              </div>
              <div class="bg-gray-50 p-3 rounded-lg md:col-span-2">
                <p class="text-xs text-gray-500">当前轨迹方程</p>
                <p class="text-sm font-semibold" id="current-equation">y = 0.0000x² + 0.0000x + 0.0000</p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </main>

  <!-- 信息模态框 -->
  <div id="info-modal" class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 hidden">
    <div class="bg-white rounded-xl p-6 max-w-md w-full mx-4 transform transition-all duration-300 scale-95 opacity-0" id="modal-content">
      <div class="flex justify-between items-center mb-4">
        <h2 class="text-xl font-bold">关于抛物体运动</h2>
        <button id="close-modal" class="text-gray-500 hover:text-gray-700">
          <i class="fa fa-times text-xl"></i>
        </button>
      </div>
      <div class="space-y-4 text-sm">
        <p>抛物体运动是物理学中研究物体在重力作用下的运动轨迹的现象。当物体以一定初速度和角度抛出时,它会沿抛物线轨迹运动。</p>
        <p>本模拟器展示了抛物体运动的基本原理,您可以通过调整初速度和发射角度来观察不同条件下的运动轨迹。</p>
        <div class="bg-gray-50 p-3 rounded-lg">
          <h3 class="font-semibold mb-2">物理公式:</h3>
          <ul class="list-disc pl-5 space-y-1">
            <li>水平位移: x = v₀·cos(θ)·t</li>
            <li>垂直位移: y = v₀·sin(θ)·t - ½·g·t²</li>
            <li>最大高度: h = (v₀·sin(θ))²/(2·g)</li>
            <li>飞行时间: T = 2·v₀·sin(θ)/g</li>
            <li>水平距离: R = v₀²·sin(2·θ)/g</li>
          </ul>
        </div>
      </div>
    </div>
  </div>

  <!-- 页脚 -->
  <footer class="bg-white py-4 px-6 shadow-inner">
    <div class="container mx-auto text-center text-sm text-gray-500">
      <p>抛物体运动模拟器 &copy; 2025 | 使用 HTML, Canvas, Tailwind CSS 构建·teacher_fan引用于豆包AI</p>
    </div>
  </footer>

  <!-- JavaScript -->
  <script>
    // 获取DOM元素
    const simulator = document.getElementById('simulator');
    const ctx = simulator.getContext('2d');
    const velocitySlider = document.getElementById('velocity');
    const angleSlider = document.getElementById('angle');
    const velocityValue = document.getElementById('velocity-value');
    const angleValue = document.getElementById('angle-value');
    const maxHeightValue = document.getElementById('max-height');
    const flightTimeValue = document.getElementById('flight-time');
    const horizontalDistanceValue = document.getElementById('horizontal-distance');
    const startBtn = document.getElementById('start-btn');
    const pauseBtn = document.getElementById('pause-btn');
    const resetBtn = document.getElementById('reset-btn');
    const infoBtn = document.getElementById('info-btn');
    const infoModal = document.getElementById('info-modal');
    const modalContent = document.getElementById('modal-content');
    const closeModal = document.getElementById('close-modal');
    const currentEquation = document.getElementById('current-equation');
    const header = document.getElementById('header');
    
    // 设置Canvas尺寸
    function resizeCanvas() {
      const container = simulator.parentElement;
      simulator.width = container.clientWidth;
      simulator.height = container.clientWidth * 9 / 16;
    }
    
    // 监听窗口大小变化
    window.addEventListener('resize', resizeCanvas);
    resizeCanvas();
    
    // 物理参数
    const GRAVITY = 9.8; // 重力加速度 (m/s²)
    let velocity = 20;   // 初速度 (m/s)
    let angle = 45;      // 发射角度 (度)
    let time = 0;        // 时间 (s)
    let dt = 0.05;       // 时间步长
    let animationId = null;
    let isRunning = false;
    let trajectoryPoints = [];
    let explosionParticles = [];
    
    // 坐标转换函数 (物理坐标 -> 画布坐标)
    function physicsToCanvas(x, y) {
      const scaleX = simulator.width / 60; // 60m 映射到画布宽度
      const scaleY = simulator.height / 25; // 25m 映射到画布高度
      return {
        x: x * scaleX + 20, // 偏移20像素以适应坐标轴
        y: simulator.height - (y * scaleY + 20) // y轴翻转并偏移
      };
    }
    
    // 坐标转换函数 (画布坐标 -> 物理坐标)
    function canvasToPhysics(x, y) {
      const scaleX = simulator.width / 60;
      const scaleY = simulator.height / 25;
      return {
        x: (x - 20) / scaleX,
        y: (simulator.height - y - 20) / scaleY
      };
    }
    
    // 计算抛物体位置
    function calculatePosition(time) {
      const rad = angle * Math.PI / 180;
      const x = velocity * Math.cos(rad) * time;
      const y = velocity * Math.sin(rad) * time - 0.5 * GRAVITY * time * time;
      return { x, y };
    }
    
    // 计算最大高度
    function calculateMaxHeight() {
      const rad = angle * Math.PI / 180;
      return (velocity * Math.sin(rad)) ** 2 / (2 * GRAVITY);
    }
    
    // 计算飞行时间
    function calculateFlightTime() {
      const rad = angle * Math.PI / 180;
      return 2 * velocity * Math.sin(rad) / GRAVITY;
    }
    
    // 计算水平距离
    function calculateHorizontalDistance() {
      const rad = angle * Math.PI / 180;
      return velocity ** 2 * Math.sin(2 * rad) / GRAVITY;
    }
    
    // 更新物理参数显示
    function updatePhysicsValues() {
      maxHeightValue.textContent = calculateMaxHeight().toFixed(2) + " m";
      flightTimeValue.textContent = calculateFlightTime().toFixed(2) + " s";
      horizontalDistanceValue.textContent = calculateHorizontalDistance().toFixed(2) + " m";
      
      // 更新当前轨迹方程
      updateTrajectoryEquation();
    }
    
    // 更新轨迹方程
    function updateTrajectoryEquation() {
      const rad = angle * Math.PI / 180;
      const a = -GRAVITY / (2 * velocity ** 2 * Math.cos(rad) ** 2);
      const b = Math.tan(rad);
      const c = 0;
      
      currentEquation.textContent = `y = ${a.toFixed(4)}x² + ${b.toFixed(4)}x + ${c.toFixed(4)}`;
    }
    
    // 绘制背景和坐标轴
    function drawBackground() {
      ctx.fillStyle = '#F8FAFC';
      ctx.fillRect(0, 0, simulator.width, simulator.height);
      
      // 绘制坐标轴
      ctx.strokeStyle = '#94A3B8';
      ctx.lineWidth = 1;
      
      // x轴
      ctx.beginPath();
      ctx.moveTo(20, simulator.height - 20);
      ctx.lineTo(simulator.width, simulator.height - 20);
      ctx.stroke();
      
      // y轴
      ctx.beginPath();
      ctx.moveTo(20, 0);
      ctx.lineTo(20, simulator.height - 20);
      ctx.stroke();
      
      // 绘制网格
      ctx.strokeStyle = '#E2E8F0';
      ctx.lineWidth = 0.5;
      
      // 垂直网格
      for (let x = 0; x <= 60; x += 10) {
        const canvasX = physicsToCanvas(x, 0).x;
        ctx.beginPath();
        ctx.moveTo(canvasX, 0);
        ctx.lineTo(canvasX, simulator.height - 20);
        ctx.stroke();
      }
      
      // 水平网格
      for (let y = 0; y <= 25; y += 5) {
        const canvasY = physicsToCanvas(0, y).y;
        ctx.beginPath();
        ctx.moveTo(20, canvasY);
        ctx.lineTo(simulator.width, canvasY);
        ctx.stroke();
      }
    }
    
    // 绘制轨迹
    function drawTrajectory() {
      if (trajectoryPoints.length < 2) return;
      
      ctx.strokeStyle = '#3B82F6';
      ctx.lineWidth = 2;
      ctx.beginPath();
      
      // 绘制轨迹线
      ctx.moveTo(trajectoryPoints[0].x, trajectoryPoints[0].y);
      for (let i = 1; i < trajectoryPoints.length; i++) {
        ctx.lineTo(trajectoryPoints[i].x, trajectoryPoints[i].y);
      }
      ctx.stroke();
      
      // 绘制当前位置
      const currentPos = trajectoryPoints[trajectoryPoints.length - 1];
      ctx.fillStyle = '#3B82F6';
      ctx.beginPath();
      ctx.arc(currentPos.x, currentPos.y, 6, 0, Math.PI * 2);
      ctx.fill();
    }
    
    // 绘制爆炸效果
    function drawExplosion() {
      if (explosionParticles.length === 0) return;
      
      for (let i = 0; i < explosionParticles.length; i++) {
        const p = explosionParticles[i];
        
        // 更新粒子位置
        p.x += p.vx;
        p.y += p.vy;
        p.vy += 0.1; // 重力影响
        p.life -= 0.02;
        
        // 绘制粒子
        const gradient = ctx.createRadialGradient(
          p.x, p.y, 0,
          p.x, p.y, p.size
        );
        gradient.addColorStop(0, `rgba(${p.r}, ${p.g}, ${p.b}, ${p.life})`);
        gradient.addColorStop(1, `rgba(${p.r}, ${p.g}, ${p.b}, 0)`);
        
        ctx.fillStyle = gradient;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
        ctx.fill();
      }
      
      // 移除已消失的粒子
      explosionParticles = explosionParticles.filter(p => p.life > 0);
    }
    
    // 创建爆炸效果
    function createExplosion(x, y) {
      explosionParticles = [];
      
      for (let i = 0; i < 50; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = 1 + Math.random() * 3;
        const size = 2 + Math.random() * 4;
        const life = 0.8 + Math.random() * 0.2;
        
        explosionParticles.push({
          x,
          y,
          vx: Math.cos(angle) * speed,
          vy: Math.sin(angle) * speed,
          size,
          life,
          r: 255,
          g: 100 + Math.random() * 155,
          b: 0
        });
      }
    }
    
    // 主动画循环
    function animate() {
      drawBackground();
      drawTrajectory();
      drawExplosion();
      
      if (isRunning) {
        time += dt;
        const pos = calculatePosition(time);
        
        // 保存轨迹点
        const canvasPos = physicsToCanvas(pos.x, pos.y);
        trajectoryPoints.push(canvasPos);
        
        // 检查是否落地
        if (pos.y <= 0) {
          isRunning = false;
          startBtn.disabled = false;
          pauseBtn.disabled = true;
          createExplosion(canvasPos.x, canvasPos.y);
        }
      }
      
      animationId = requestAnimationFrame(animate);
    }
    
    // 开始动画
    function startAnimation() {
      if (!isRunning) {
        isRunning = true;
        startBtn.disabled = true;
        pauseBtn.disabled = false;
      }
    }
    
    // 暂停动画
    function pauseAnimation() {
      if (isRunning) {
        isRunning = false;
        startBtn.disabled = false;
        pauseBtn.disabled = true;
      }
    }
    
    // 重置动画
    function resetAnimation() {
      pauseAnimation();
      time = 0;
      trajectoryPoints = [];
      explosionParticles = [];
      updatePhysicsValues();
    }
    
    // 显示信息模态框
    function showInfoModal() {
      infoModal.classList.remove('hidden');
      setTimeout(() => {
        modalContent.classList.remove('scale-95', 'opacity-0');
        modalContent.classList.add('scale-100', 'opacity-100');
      }, 10);
    }
    
    // 隐藏信息模态框
    function hideInfoModal() {
      modalContent.classList.remove('scale-100', 'opacity-100');
      modalContent.classList.add('scale-95', 'opacity-0');
      setTimeout(() => {
        infoModal.classList.add('hidden');
      }, 300);
    }
    
    // 滚动效果
    function handleScroll() {
      if (window.scrollY > 10) {
        header.classList.add('py-2', 'shadow-lg');
        header.classList.remove('py-4', 'shadow-md');
      } else {
        header.classList.add('py-4', 'shadow-md');
        header.classList.remove('py-2', 'shadow-lg');
      }
    }
    
    // 事件监听
    velocitySlider.addEventListener('input', () => {
      velocity = parseInt(velocitySlider.value);
      velocityValue.textContent = velocity;
      resetAnimation();
    });
    
    angleSlider.addEventListener('input', () => {
      angle = parseInt(angleSlider.value);
      angleValue.textContent = angle;
      resetAnimation();
    });
    
    startBtn.addEventListener('click', startAnimation);
    pauseBtn.addEventListener('click', pauseAnimation);
    resetBtn.addEventListener('click', resetAnimation);
    infoBtn.addEventListener('click', showInfoModal);
    closeModal.addEventListener('click', hideInfoModal);
    infoModal.addEventListener('click', (e) => {
      if (e.target === infoModal) hideInfoModal();
    });
    window.addEventListener('scroll', handleScroll);
    
    // 初始化
    updatePhysicsValues();
    drawBackground();
    animate();
  </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值