```tsx import React, { useState, useEffect, useRef } from 'react'; // 类型定义 type MotionDataPoint = { t: number; // 时间 (s) v: number; // 速度 (m/s) a: number; // 加速度 (m/s²) }; type MotionProfile = 'constantAcceleration' | 'zeroAcceleration' | 'decelerating'; const AccelerationVelocityChart: React.FC = () => { // 状态管理 const [timeRange, setTimeRange] = useState(10); // 总时间 (s) const [acceleration, setAcceleration] = useState(2); // 加速度 (m/s²) const [initialVelocity, setInitialVelocity] = useState(0); // 初速度 (m/s) const [profile, setProfile] = useState('constantAcceleration'); const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [chartData, setChartData] = useState([]); const animationRef = useRef(0); const startTimeRef = useRef(0); const durationRef = useRef(0); // 生成图表数据(静态) const generateData = (): MotionDataPoint[] => { const data: MotionDataPoint[] = []; const dt = 0.2; for (let t = 0; t <= timeRange; t += dt) { let v = 0; let a = 0; switch (profile) { case 'constantAcceleration': a = acceleration; v = initialVelocity + a * t; break; case 'zeroAcceleration': a = 0; v = initialVelocity; break; case 'decelerating': a = -Math.abs(acceleration); v = Math.max(0, initialVelocity + a * t); break; } data.push({ t, v, a }); } return data; }; // 初始化图表数据 useEffect(() => { setChartData(generateData()); setCurrentTime(0); }, [timeRange, acceleration, initialVelocity, profile]); // 动画逻辑 useEffect(() => { if (!isPlaying) return; const animate = (timestamp: number) => { if (!startTimeRef.current) startTimeRef.current = timestamp; const elapsed = timestamp - startTimeRef.current; durationRef.current = timeRange * 1000; // 转换为毫秒 const progress = (elapsed % durationRef.current) / durationRef.current; const t = progress * timeRange; setCurrentTime(t); animationRef.current = requestAnimationFrame(animate); }; animationRef.current = requestAnimationFrame(animate); return () => { cancelAnimationFrame(animationRef.current); startTimeRef.current = 0; }; }, [isPlaying, timeRange]); // 停止动画 const stopAnimation = () => { setIsPlaying(false); cancelAnimationFrame(animationRef.current); startTimeRef.current = 0; }; // 重置动画 const resetAnimation = () => { stopAnimation(); setCurrentTime(0); }; // 获取当前时刻的数据点(用于高亮) const getCurrentPoint = (): MotionDataPoint | null => { if (chartData.length === 0) return null; // 找到最接近 currentTime 的点 const closest = chartData.reduce((prev, curr) => Math.abs(curr.t - currentTime) < Math.abs(prev.t - currentTime) ? curr : prev ); return closest; }; const currentPoint = getCurrentPoint(); // 图表尺寸 const width = 800; const height = 400; const margin = { top: 40, right: 30, bottom: 60, left: 60 }; const chartWidth = width - margin.left - margin.right; const chartHeight = height - margin.top - margin.bottom; // 坐标映射函数 const xScale = (t: number): number => margin.left + (t / timeRange) * chartWidth; const vScale = (v: number): number => { const maxV = Math.max(...chartData.map(d => d.v), initialVelocity + acceleration * timeRange, 0); const minV = Math.min(...chartData.map(d => d.v), 0); const range = Math.max(maxV - minV, 1); return margin.top + chartHeight - ((v - minV) / range) * chartHeight; }; const aScale = (a: number): number => { const maxA = Math.max(...chartData.map(d => d.a), acceleration, 0); const minA = Math.min(...chartData.map(d => d.a), -Math.abs(acceleration), 0); const range = Math.max(maxA - minA, 1); return margin.top + chartHeight - ((a - minA) / range) * chartHeight; }; // 绘制速度曲线 const velocityPath = chartData.map((d, i) => { if (i === 0) return `M ${xScale(d.t)} ${vScale(d.v)}`; return `L ${xScale(d.t)} ${vScale(d.v)}`; }).join(' '); // 绘制加速度曲线 const accelerationPath = chartData.map((d, i) => { if (i === 0) return `M ${xScale(d.t)} ${aScale(d.a)}`; return `L ${xScale(d.t)} ${aScale(d.a)}`; }).join(' '); // 获取图例颜色 const getVelocityColor = () => '#3b82f6'; // blue-500 const getAccelerationColor = () => '#ef4444'; // red-500 return (

加速度与速度的函数图像对比

七年级数学 · 运动学基础

{/* 控制面板 */}
setTimeRange(parseFloat(e.target.value))} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600" />
2 {timeRange.toFixed(1)} 20
setAcceleration(parseFloat(e.target.value))} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600" />
-5 {acceleration.toFixed(1)} 5
setInitialVelocity(parseFloat(e.target.value))} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600" />
0 {initialVelocity.toFixed(1)} 20
{/* 动画控制按钮 */}
当前时间: {currentTime.toFixed(1)} s
{/* 当前状态显示 */} {currentPoint && (

当前时刻状态

时间: {currentPoint.t.toFixed(1)} s
速度: {currentPoint.v.toFixed(1)} m/s
加速度: {currentPoint.a.toFixed(1)} m/s²
运动类型: {profile.replace(/([A-Z])/g, ' $1').trim()}
)}
{/* 图表区域 */}

速度与加速度随时间变化图

{/* 坐标轴 */} {/* X轴刻度和标签 */} {Array.from({ length: Math.floor(timeRange) + 1 }).map((_, i) => ( {i} ))} {/* Y轴刻度和标签 - 速度 */} {(() => { const maxV = Math.max(...chartData.map(d => d.v), initialVelocity + acceleration * timeRange, 0); const minV = Math.min(...chartData.map(d => d.v), 0); const range = Math.max(maxV - minV, 1); const step = Math.ceil(range / 5); const labels = Array.from( { length: 6 }, (_, i) => minV + i * step ).filter(v => v <= maxV); return labels.map((v, i) => ( {v.toFixed(0)} )); })()} {/* Y轴刻度和标签 - 加速度 */} {(() => { const maxA = Math.max(...chartData.map(d => d.a), acceleration, 0); const minA = Math.min(...chartData.map(d => d.a), -Math.abs(acceleration), 0); const range = Math.max(maxA - minA, 1); const step = Math.ceil(range / 5); const labels = Array.from( { length: 6 }, (_, i) => minA + i * step ).filter(a => a <= maxA); return labels.map((a, i) => ( {a.toFixed(0)} )); })()} {/* 速度曲线 */} {/* 加速度曲线 */} {/* 当前时刻垂直线 */} {currentPoint && ( <> {/* 当前速度点 */} {/* 当前加速度点 */} )} {/* 坐标轴标签 */} 时间 t (秒) 速度 v (m/s) 和 加速度 a (m/s²) {/* 图例 */}
速度 v(t)
加速度 a(t)
{/* 解释说明 */}

📊 图像解读

速度图像

速度是位移对时间的变化率。图像中斜率为正表示加速,水平线表示匀速,斜率为负表示减速。

加速度图像

加速度是速度对时间的变化率。图像中水平线表示加速度恒定,值为零表示匀速运动。

两者关系

加速度图像的纵坐标值等于速度图像在该时刻的斜率。加速度为零时,速度图像为水平直线。

); }; export default AccelerationVelocityChart; ```