查看解析页源码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>牛顿第二定律:动力学解析</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$']]
},
startup: {
ready: () => {
MathJax.startup.defaultReady();
window.isMathJaxReady = true;
window.renderMath();
}
}
};
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
.math-font { font-family: 'Times New Roman', serif; font-style: italic; }
input[type=range] { cursor: pointer; }
</style>
</head>
<body class="bg-slate-50 min-h-screen flex flex-col items-center p-4 md:p-8">
<div class="max-w-5xl w-full bg-white rounded-2xl shadow-xl overflow-hidden border border-slate-200">
<!-- Header -->
<div class="bg-indigo-600 p-6 text-white text-center">
<h1 class="text-2xl font-bold">牛顿第二定律:受力与运动解析</h1>
<p class="mt-2 opacity-90">交互式物理题:$F_{合} = ma$</p>
</div>
<div class="flex flex-col lg:flex-row">
<!-- Left: Simulation and Problem -->
<div class="flex-1 p-6 border-r border-slate-100">
<div class="bg-amber-50 p-4 rounded-xl border border-amber-100 mb-6">
<h3 class="font-bold text-amber-900 flex items-center gap-2">
<span>📝</span> 典型应用题
</h3>
<p class="text-sm text-amber-800 mt-2 leading-relaxed">
一个质量为 <span class="font-bold">$m$</span> 的木块放在粗糙水平面上,动摩擦因数为 <span class="font-bold">$\mu$</span>。
现在施加一个水平向右的拉力 <span class="font-bold">$F$</span>,使其由静止开始运动。
求:木块的加速度 <span class="font-bold">$a$</span> 及运动 <span class="font-bold">$2\text{s}$</span> 后的速度。($g=10\text{m/s}^2$)
</p>
</div>
<!-- Canvas Area -->
<div class="relative bg-slate-900 rounded-xl overflow-hidden h-[350px]">
<svg id="phys-svg" class="w-full h-full" viewBox="0 0 600 350" preserveAspectRatio="xMidYMid meet">
<!-- 地面 -->
<line x1="0" y1="280" x2="600" y2="280" stroke="#475569" stroke-width="4" />
<path d="M0 280 L600 280 L600 350 L0 350 Z" fill="#1e293b" />
<!-- 木块 -->
<g id="block-group">
<rect id="block" x="100" y="200" width="80" height="80" fill="#fb923c" rx="4" />
<text x="140" y="245" text-anchor="middle" fill="white" class="font-bold">m</text>
<!-- 受力矢量 -->
<!-- 重力 -->
<line id="vec-mg" x1="140" y1="240" x2="140" y2="340" stroke="#ef4444" stroke-width="3" marker-end="url(#arrow-red)" />
<text x="145" y="330" fill="#ef4444" class="text-[10px]">G = mg</text>
<!-- 支持力 -->
<line id="vec-fn" x1="140" y1="240" x2="140" y2="140" stroke="#3b82f6" stroke-width="3" marker-end="url(#arrow-blue)" />
<text x="145" y="160" fill="#3b82f6" class="text-[10px]">N</text>
<!-- 拉力 -->
<line id="vec-f" x1="180" y1="240" x2="280" y2="240" stroke="#10b981" stroke-width="3" marker-end="url(#arrow-green)" />
<text x="260" y="230" fill="#10b981" class="text-[10px]">F</text>
<!-- 摩擦力 -->
<line id="vec-ff" x1="100" y1="280" x2="40" y2="280" stroke="#f59e0b" stroke-width="3" marker-end="url(#arrow-orange)" />
<text x="40" y="275" fill="#f59e0b" class="text-[10px]">f</text>
</g>
<!-- 定义箭头 -->
<defs>
<marker id="arrow-red" markerWidth="10" markerHeight="10" refX="9" refY="3" orientation="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#ef4444" />
</marker>
<marker id="arrow-blue" markerWidth="10" markerHeight="10" refX="9" refY="3" orientation="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#3b82f6" />
</marker>
<marker id="arrow-green" markerWidth="10" markerHeight="10" refX="9" refY="3" orientation="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#10b981" />
</marker>
<marker id="arrow-orange" markerWidth="10" markerHeight="10" refX="9" refY="3" orientation="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#f59e0b" />
</marker>
</defs>
</svg>
<!-- Control Overlay -->
<div class="absolute top-4 right-4 flex flex-col gap-2 pointer-events-none">
<button id="play-btn" onclick="window.startSim()" class="pointer-events-auto bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded-lg shadow-lg text-sm font-bold transition-all">播放模拟</button>
<button id="reset-btn" onclick="window.resetSim()" class="pointer-events-auto bg-slate-700 hover:bg-slate-800 text-white px-4 py-2 rounded-lg shadow-lg text-sm font-bold transition-all">重置</button>
</div>
</div>
<!-- Sliders -->
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-slate-50 p-3 rounded-lg border border-slate-200">
<label class="text-xs font-bold text-slate-500 block mb-1">质量 $m$ (kg): <span id="m-val">2</span></label>
<input type="range" id="m-slider" min="1" max="10" step="0.5" value="2" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none">
</div>
<div class="bg-slate-50 p-3 rounded-lg border border-slate-200">
<label class="text-xs font-bold text-slate-500 block mb-1">拉力 $F$ (N): <span id="f-val">10</span></label>
<input type="range" id="f-slider" min="0" max="30" step="1" value="10" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none">
</div>
<div class="bg-slate-50 p-3 rounded-lg border border-slate-200">
<label class="text-xs font-bold text-slate-500 block mb-1">动摩擦因数 $\mu$: <span id="mu-val">0.2</span></label>
<input type="range" id="mu-slider" min="0" max="0.8" step="0.05" value="0.2" class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none">
</div>
</div>
</div>
<!-- Right: Analysis -->
<div id="analysis-panel" class="lg:w-[400px] p-6 bg-slate-50 flex flex-col gap-4">
<h3 class="font-bold text-slate-800 flex items-center gap-2 border-b border-slate-200 pb-2">
<span>💡</span> 步骤解析
</h3>
<div id="step-1" class="bg-white p-4 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-xs text-indigo-600 uppercase tracking-wider">Step 1: 垂直方向平衡</h4>
<div id="math-1" class="text-sm text-slate-700 mt-2">
在竖直方向,物体平衡:
$$N = mg = 2 \times 10 = 20\text{N}$$
</div>
</div>
<div id="step-2" class="bg-white p-4 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-xs text-indigo-600 uppercase tracking-wider">Step 2: 计算摩擦力</h4>
<div id="math-2" class="text-sm text-slate-700 mt-2">
滑动摩擦力 $f = \mu N$:
$$f = 0.2 \times 20 = 4\text{N}$$
</div>
</div>
<div id="step-3" class="bg-white p-4 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-xs text-indigo-600 uppercase tracking-wider">Step 3: 牛顿第二定律</h4>
<div id="math-3" class="text-sm text-slate-700 mt-2">
水平方向合力 $F_{合} = F - f$:
$$ma = 10 - 4 = 6\text{N}$$
$$a = \frac{6}{2} = 3\text{m/s}^2$$
</div>
</div>
<div id="step-4" class="bg-white p-4 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-xs text-indigo-600 uppercase tracking-wider">Step 4: 运动学求解</h4>
<div id="math-4" class="text-sm text-slate-700 mt-2">
$2\text{s}$ 末的速度 $v = at$:
$$v = 3 \times 2 = 6\text{m/s}$$
</div>
</div>
</div>
</div>
</div>
<script>
// 状态变量防止渲染冲突
let isMathJaxRendering = false;
let pendingRender = false;
// 增强版渲染函数,解决 IndexSizeError 和并行冲突
window.renderMath = async () => {
if (!window.isMathJaxReady) return;
if (isMathJaxRendering) {
pendingRender = true;
return;
}
isMathJaxRendering = true;
pendingRender = false;
try {
const target = document.getElementById('analysis-panel');
// 在渲染前,确保 MathJax 的排版状态清除,防止 splitText 错误
await MathJax.typesetClear([target]);
await MathJax.typesetPromise([target]);
} catch (err) {
console.error("MathJax rendering error:", err);
} finally {
isMathJaxRendering = false;
// 如果在渲染期间有新的请求,则再运行一次
if (pendingRender) {
window.renderMath();
}
}
};
const mSlider = document.getElementById('m-slider');
const fSlider = document.getElementById('f-slider');
const muSlider = document.getElementById('mu-slider');
const blockGroup = document.getElementById('block-group');
let isAnimating = false;
let animationId;
let startTime;
function updateValues() {
const m = parseFloat(mSlider.value);
const F = parseFloat(fSlider.value);
const mu = parseFloat(muSlider.value);
const g = 10;
document.getElementById('m-val').textContent = m;
document.getElementById('f-val').textContent = F;
document.getElementById('mu-val').textContent = mu;
// 核心计算逻辑
const N = m * g;
const f_max = mu * N;
const F_net = Math.max(0, F - f_max);
const a = (F_net / m).toFixed(2);
const v2 = (a * 2).toFixed(2);
const frictionUsed = F > f_max ? f_max : F;
// 更新公式文本 (使用标准格式)
document.getElementById('math-1').innerHTML = `在竖直方向,物体平衡:$$N = mg = ${m} \\times 10 = ${N}\\text{N}$$`;
document.getElementById('math-2').innerHTML = `滑动摩擦力 $f = \\mu N$:$$f = ${mu} \\times ${N} = ${f_max.toFixed(1)}\\text{N}$$`;
if (F <= f_max) {
document.getElementById('math-3').innerHTML = `拉力 $F \\le f_{最大}$,物体静止:$$a = 0\\text{m/s}^2$$`;
document.getElementById('math-4').innerHTML = `速度:$$v = 0\\text{m/s}$$`;
} else {
document.getElementById('math-3').innerHTML = `水平方向合力 $F_{合} = F - f$:$$ma = ${F} - ${f_max.toFixed(1)} = ${F_net.toFixed(1)}\\text{N}$$ $$a = \\frac{${F_net.toFixed(1)}}{${m}} = ${a}\\text{m/s}^2$$`;
document.getElementById('math-4').innerHTML = `$2\\text{s}$ 末的速度 $v = at$:$$v = ${a} \\times 2 = ${v2}\\text{m/s}$$`;
}
// 更新 SVG 视觉效果
document.getElementById('vec-mg').setAttribute('y2', 240 + m * 5);
document.getElementById('vec-fn').setAttribute('y2', 240 - N * 5 / 2);
document.getElementById('vec-f').setAttribute('x2', 180 + F * 5);
document.getElementById('vec-ff').setAttribute('x2', 100 - frictionUsed * 5);
// 触发 MathJax 渲染
window.renderMath();
window.resetSim();
}
window.startSim = () => {
if (isAnimating) return;
isAnimating = true;
startTime = performance.now();
const m = parseFloat(mSlider.value);
const F = parseFloat(fSlider.value);
const mu = parseFloat(muSlider.value);
const f_max = mu * m * 10;
const a = F > f_max ? (F - f_max) / m : 0;
function animate(time) {
let dt = (time - startTime) / 1000;
if (dt > 3) dt = 3;
const distance = 0.5 * a * dt * dt * 50;
blockGroup.setAttribute('transform', `translate(${distance}, 0)`);
if (dt < 3) {
animationId = requestAnimationFrame(animate);
} else {
isAnimating = false;
}
}
animationId = requestAnimationFrame(animate);
};
window.resetSim = () => {
cancelAnimationFrame(animationId);
isAnimating = false;
blockGroup.setAttribute('transform', 'translate(0, 0)');
};
// 监听滑块
[mSlider, fSlider, muSlider].forEach(s => s.addEventListener('input', updateValues));
// 初始运行
updateValues();
</script>
</body>
</html>