Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

<title>带电粒子在匀强电场与匀强磁场复合场中的运动</title> <style> :root { --bg: #0f172a; --panel: #111827; --card: #1f2937; --text: #e5e7eb; --muted: #94a3b8; --accent: #38bdf8; --accent2: #a78bfa; --good: #22c55e; --warn: #f59e0b; --danger: #ef4444; --grid: rgba(255,255,255,0.08); }
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
  background: linear-gradient(180deg, #020617 0%, #0f172a 100%);
  color: var(--text);
}

.wrap {
  max-width: 1400px;
  margin: 0 auto;
  padding: 18px;
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: 18px;
}

.panel, .main {
  background: rgba(17, 24, 39, 0.92);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 18px;
  box-shadow: 0 12px 36px rgba(0,0,0,0.35);
  overflow: hidden;
}

.panel {
  padding: 18px;
  position: sticky;
  top: 18px;
  height: fit-content;
}

h1 {
  margin: 0 0 10px;
  font-size: 24px;
  line-height: 1.3;
}

h2 {
  margin: 18px 0 10px;
  font-size: 16px;
  color: #cbd5e1;
}

.desc {
  color: var(--muted);
  line-height: 1.65;
  font-size: 14px;
}

.group {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  padding: 12px;
  margin-top: 12px;
}

.control {
  margin: 10px 0 14px;
}

.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

label {
  font-size: 14px;
  color: #e2e8f0;
  display: block;
  margin-bottom: 6px;
}

.value {
  min-width: 110px;
  text-align: right;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
}

input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}

.btns {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 12px;
}

button {
  border: none;
  border-radius: 12px;
  padding: 11px 12px;
  color: white;
  background: linear-gradient(135deg, #0ea5e9, #6366f1);
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  transition: transform 0.12s ease, opacity 0.12s ease;
}

button:hover { transform: translateY(-1px); }
button.secondary { background: linear-gradient(135deg, #334155, #475569); }
button.good { background: linear-gradient(135deg, #16a34a, #22c55e); }
button.warn { background: linear-gradient(135deg, #d97706, #f59e0b); }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-radius: 999px;
  padding: 8px 12px;
  margin-top: 12px;
  font-size: 13px;
  background: rgba(56,189,248,0.12);
  color: #bae6fd;
  border: 1px solid rgba(56,189,248,0.28);
}

.formula {
  background: rgba(255,255,255,0.04);
  border-left: 4px solid var(--accent2);
  padding: 10px 12px;
  margin-top: 10px;
  border-radius: 10px;
  font-family: "Times New Roman", serif;
  line-height: 1.8;
  font-size: 16px;
  color: #ede9fe;
}

.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 14px;
}

.stat {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
  padding: 10px;
}

.stat .k {
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 6px;
}

.stat .v {
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  color: #f8fafc;
}

.main {
  padding: 14px;
}

.canvasBox {
  position: relative;
  background: #020617;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.08);
}

canvas {
  display: block;
  width: 100%;
  height: 720px;
  background:
    linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.025) 1px, transparent 1px),
    radial-gradient(circle at top, rgba(59,130,246,0.08), transparent 30%),
    #020617;
  background-size: 40px 40px, 40px 40px, auto, auto;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  color: var(--muted);
  font-size: 13px;
  padding: 10px 4px 0;
}

.dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  display: inline-block;
  margin-right: 6px;
}

.footerNote {
  margin-top: 12px;
  font-size: 13px;
  color: #94a3b8;
  line-height: 1.7;
}

.emph { color: #fef08a; }

@media (max-width: 1100px) {
  .wrap { grid-template-columns: 1fr; }
  .panel { position: static; }
  canvas { height: 560px; }
}
</style>

复合场中带电粒子运动演示

本演示采用二维数值积分,模拟带电粒子在 匀强电场匀强磁场(磁场垂直屏幕)组成的复合场中的运动。 电场方向固定为 竖直向上,磁场方向固定为 垂直屏幕向外。粒子从左侧水平射入。
  <div class="badge">配速法核心:当 <b>v = E / B</b> 时,电场力与洛伦兹力大小相等、方向相反,粒子做匀速直线运动。</div>

  <div class="formula">
    洛伦兹力:F = q(E + v × B)<br/>
    本模型中:F<sub>电</sub> = qE,F<sub>磁</sub> = qvB<br/>
    配速条件:qE = qvB ⇒ <span class="emph">v = E / B</span>(B ≠ 0)
  </div>

  <h2>参数调节</h2>
  <div class="group">
    <div class="control">
      <div class="row">
        <label for="q">电荷量 q(C)</label>
        <div class="value" id="qVal"></div>
      </div>
      <input id="q" type="range" min="-5" max="5" step="0.1" value="1.0" />
    </div>

    <div class="control">
      <div class="row">
        <label for="m">粒子质量 m(kg)</label>
        <div class="value" id="mVal"></div>
      </div>
      <input id="m" type="range" min="0.5" max="10" step="0.1" value="2.0" />
    </div>

    <div class="control">
      <div class="row">
        <label for="E">电场强度 E(N/C)</label>
        <div class="value" id="EVal"></div>
      </div>
      <input id="E" type="range" min="0" max="20" step="0.1" value="8.0" />
    </div>

    <div class="control">
      <div class="row">
        <label for="B">磁感应强度 B(T)</label>
        <div class="value" id="BVal"></div>
      </div>
      <input id="B" type="range" min="0" max="5" step="0.05" value="1.6" />
    </div>

    <div class="control">
      <div class="row">
        <label for="v0">入射速度 v₀(m/s)</label>
        <div class="value" id="v0Val"></div>
      </div>
      <input id="v0" type="range" min="0" max="20" step="0.1" value="5.0" />
    </div>

    <div class="control">
      <div class="row">
        <label for="speed">动画倍率</label>
        <div class="value" id="speedVal"></div>
      </div>
      <input id="speed" type="range" min="0.2" max="3" step="0.1" value="1.0" />
    </div>

    <div class="btns">
      <button id="toggleBtn">暂停</button>
      <button class="secondary" id="resetBtn">重置轨迹</button>
      <button class="good" id="matchBtn">一键配速</button>
      <button class="warn" id="clearBtn">仅清空轨迹</button>
    </div>
  </div>

  <h2>实时信息</h2>
  <div class="stats">
    <div class="stat">
      <div class="k">理论配速 v = E/B</div>
      <div class="v" id="vSelect">—</div>
    </div>
    <div class="stat">
      <div class="k">当前速度大小</div>
      <div class="v" id="vNow">—</div>
    </div>
    <div class="stat">
      <div class="k">电场力大小 |q|E</div>
      <div class="v" id="feNow">—</div>
    </div>
    <div class="stat">
      <div class="k">磁场力大小 |q|vB</div>
      <div class="v" id="fbNow">—</div>
    </div>
    <div class="stat">
      <div class="k">合力竖直分量</div>
      <div class="v" id="fyNow">—</div>
    </div>
    <div class="stat">
      <div class="k">运动状态判断</div>
      <div class="v" id="judge">—</div>
    </div>
  </div>

  <div class="footerNote">
    说明:<br/>
    1. 本演示使用经典力学,忽略相对论效应。<br/>
    2. 当 <b>B = 0</b> 时,仅受电场力作用;当 <b>E = 0</b> 时,仅受磁场力作用。<br/>
    3. 在当前设定下,<b>正电荷</b>若速度偏小,电场力占优,轨迹上偏;速度偏大,磁场力占优,轨迹下偏。负电荷方向整体反转,但 <b>配速条件仍然是 v = E/B</b>。
  </div>
</aside>

<main class="main">
  <div class="canvasBox">
    <canvas id="cv" width="1000" height="720"></canvas>
  </div>
  <div class="legend">
    <span><span class="dot" style="background:#f43f5e"></span>带电粒子</span>
    <span><span class="dot" style="background:#38bdf8"></span>轨迹</span>
    <span><span class="dot" style="background:#22c55e"></span>速度矢量</span>
    <span><span class="dot" style="background:#f59e0b"></span>合力矢量</span>
    <span>紫色箭头:电场方向(向上)</span>
    <span>圆点“·”:磁场方向(向外)</span>
  </div>
</main>
<script> const canvas = document.getElementById('cv'); const ctx = canvas.getContext('2d'); const ui = { q: document.getElementById('q'), m: document.getElementById('m'), E: document.getElementById('E'), B: document.getElementById('B'), v0: document.getElementById('v0'), speed: document.getElementById('speed'), qVal: document.getElementById('qVal'), mVal: document.getElementById('mVal'), EVal: document.getElementById('EVal'), BVal: document.getElementById('BVal'), v0Val: document.getElementById('v0Val'), speedVal: document.getElementById('speedVal'), vSelect: document.getElementById('vSelect'), vNow: document.getElementById('vNow'), feNow: document.getElementById('feNow'), fbNow: document.getElementById('fbNow'), fyNow: document.getElementById('fyNow'), judge: document.getElementById('judge'), toggleBtn: document.getElementById('toggleBtn'), resetBtn: document.getElementById('resetBtn'), clearBtn: document.getElementById('clearBtn'), matchBtn: document.getElementById('matchBtn') }; const scale = 30; // 1 m -> 30 px const world = { xmin: 0, xmax: canvas.width / scale, ymin: -canvas.height / (2 * scale), ymax: canvas.height / (2 * scale) }; let running = true; let particle; let trace = []; function num(v, digits = 2) { return Number(v).toFixed(digits); } function getParams() { return { q: parseFloat(ui.q.value), m: parseFloat(ui.m.value), E: parseFloat(ui.E.value), B: parseFloat(ui.B.value), v0: parseFloat(ui.v0.value), speed: parseFloat(ui.speed.value) }; } function updateLabels() { const p = getParams(); ui.qVal.textContent = `${num(p.q, 2)} C`; ui.mVal.textContent = `${num(p.m, 2)} kg`; ui.EVal.textContent = `${num(p.E, 2)} N/C`; ui.BVal.textContent = `${num(p.B, 2)} T`; ui.v0Val.textContent = `${num(p.v0, 2)} m/s`; ui.speedVal.textContent = `${num(p.speed, 1)} ×`; ui.vSelect.textContent = p.B === 0 ? 'B = 0,不适用' : `${num(p.E / p.B, 3)} m/s`; } function resetParticle() { const p = getParams(); particle = { x: 2.2, y: 0, vx: p.v0, vy: 0, ax: 0, ay: 0, t: 0 }; trace = [{ x: particle.x, y: particle.y }]; updateInfo(); } function clearTrace() { trace = [{ x: particle.x, y: particle.y }]; } function worldToCanvas(x, y) { return { x: x * scale, y: canvas.height / 2 - y * scale }; } // 设定:E 方向向上,即 E = (0, +E, 0) // 磁场向外,即 B = (0, 0, +B) // 速度在平面内 v = (vx, vy, 0) // 则 v × B = (vy B, -vx B, 0) // 所以: // Fx = q * vy * B // Fy = q * (E - vx * B) function acceleration(state) { const p = getParams(); const Fx = p.q * state.vy * p.B; const Fy = p.q * (p.E - state.vx * p.B); return { ax: Fx / p.m, ay: Fy / p.m, Fx, Fy }; } // 四阶 Runge-Kutta,尽量保证轨迹稳定、物理演示更平滑 function rk4Step(dt) { const s = { x: particle.x, y: particle.y, vx: particle.vx, vy: particle.vy }; function deriv(st) { const a = acceleration(st); return { dx: st.vx, dy: st.vy, dvx: a.ax, dvy: a.ay }; } const k1 = deriv(s); const k2 = deriv({ x: s.x + k1.dx * dt / 2, y: s.y + k1.dy * dt / 2, vx: s.vx + k1.dvx * dt / 2, vy: s.vy + k1.dvy * dt / 2 }); const k3 = deriv({ x: s.x + k2.dx * dt / 2, y: s.y + k2.dy * dt / 2, vx: s.vx + k2.dvx * dt / 2, vy: s.vy + k2.dvy * dt / 2 }); const k4 = deriv({ x: s.x + k3.dx * dt, y: s.y + k3.dy * dt, vx: s.vx + k3.dvx * dt, vy: s.vy + k3.dvy * dt }); particle.x += (dt / 6) * (k1.dx + 2 * k2.dx + 2 * k3.dx + k4.dx); particle.y += (dt / 6) * (k1.dy + 2 * k2.dy + 2 * k3.dy + k4.dy); particle.vx += (dt / 6) * (k1.dvx + 2 * k2.dvx + 2 * k3.dvx + k4.dvx); particle.vy += (dt / 6) * (k1.dvy + 2 * k2.dvy + 2 * k3.dvy + k4.dvy); particle.t += dt; const aNow = acceleration(particle); particle.ax = aNow.ax; particle.ay = aNow.ay; particle.Fx = aNow.Fx; particle.Fy = aNow.Fy; } function updateInfo() { const p = getParams(); const v = Math.hypot(particle.vx, particle.vy); const Fe = Math.abs(p.q * p.E); const Fb = Math.abs(p.q * v * p.B); const Fy = p.q * (p.E - particle.vx * p.B); ui.vNow.textContent = `${num(v, 3)} m/s`; ui.feNow.textContent = `${num(Fe, 3)} N`; ui.fbNow.textContent = `${num(Fb, 3)} N`; ui.fyNow.textContent = `${num(Fy, 3)} N`; let text = '一般曲线运动'; if (p.B === 0 && p.E === 0) { text = '匀速直线运动'; } else if (p.B === 0) { text = '仅受电场力,类平抛/匀加速偏转'; } else if (p.E === 0) { text = '仅受磁场力,近似圆周偏转'; } else { const vSel = p.E / p.B; if (Math.abs(particle.vx - vSel) < 0.06 && Math.abs(particle.vy) < 0.05) { text = '接近配速:近似匀速直线'; } else if ((p.q > 0 && Fy > 0) || (p.q < 0 && Fy > 0)) { text = '竖直合力向上'; } else if ((p.q > 0 && Fy < 0) || (p.q < 0 && Fy < 0)) { text = '竖直合力向下'; } else { text = '瞬时竖直合力约为 0'; } } ui.judge.textContent = text; } function drawArrow(x1, y1, x2, y2, color, width = 2) { const head = 10; const ang = Math.atan2(y2 - y1, x2 - x1); ctx.save(); ctx.strokeStyle = color; ctx.fillStyle = color; ctx.lineWidth = width; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x2, y2); ctx.lineTo(x2 - head * Math.cos(ang - Math.PI / 6), y2 - head * Math.sin(ang - Math.PI / 6)); ctx.lineTo(x2 - head * Math.cos(ang + Math.PI / 6), y2 - head * Math.sin(ang + Math.PI / 6)); ctx.closePath(); ctx.fill(); ctx.restore(); } function drawFields() { const p = getParams(); ctx.save(); // 电场箭头(向上) const exCols = 8; const eyRows = 5; for (let i = 1; i <= exCols; i++) { for (let j = 1; j <= eyRows; j++) { const x = (canvas.width / (exCols + 1)) * i; const y = (canvas.height / (eyRows + 1)) * j + 90; drawArrow(x, y + 18, x, y - 18, 'rgba(167,139,250,0.95)', 1.8); } } ctx.fillStyle = 'rgba(196,181,253,0.95)'; ctx.font = '16px sans-serif'; ctx.fillText(`电场 E = ${num(p.E, 2)} N/C(方向:向上)`, 20, 28); // 磁场圆点(向外) for (let x = 60; x < canvas.width; x += 90) { for (let y = 70; y < canvas.height; y += 90) { ctx.beginPath(); ctx.strokeStyle = 'rgba(56,189,248,0.55)'; ctx.lineWidth = 1.2; ctx.arc(x, y, 10, 0, Math.PI * 2); ctx.stroke(); ctx.beginPath(); ctx.fillStyle = 'rgba(56,189,248,0.95)'; ctx.arc(x, y, 2.7, 0, Math.PI * 2); ctx.fill(); } } ctx.fillStyle = 'rgba(125,211,252,0.95)'; ctx.fillText(`磁场 B = ${num(p.B, 2)} T(方向:垂直屏幕向外)`, 20, 52); // 中线 ctx.strokeStyle = 'rgba(255,255,255,0.14)'; ctx.beginPath(); ctx.moveTo(0, canvas.height / 2); ctx.lineTo(canvas.width, canvas.height / 2); ctx.stroke(); ctx.restore(); } function drawTrace() { if (trace.length < 2) return; ctx.save(); ctx.strokeStyle = 'rgba(56,189,248,0.95)'; ctx.lineWidth = 2.6; ctx.beginPath(); const p0 = worldToCanvas(trace[0].x, trace[0].y); ctx.moveTo(p0.x, p0.y); for (let i = 1; i < trace.length; i++) { const p = worldToCanvas(trace[i].x, trace[i].y); ctx.lineTo(p.x, p.y); } ctx.stroke(); ctx.restore(); } function drawParticle() { const pos = worldToCanvas(particle.x, particle.y); const vScale = 12; const fScale = 8; ctx.save(); ctx.beginPath(); ctx.fillStyle = getParams().q >= 0 ? '#f43f5e' : '#fb7185'; ctx.arc(pos.x, pos.y, 8, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'white'; ctx.font = '12px sans-serif'; ctx.fillText(getParams().q >= 0 ? '+' : '−', pos.x - 4, pos.y + 4); ctx.restore(); // 速度矢量 drawArrow( pos.x, pos.y, pos.x + particle.vx * vScale, pos.y - particle.vy * vScale, 'rgba(34,197,94,0.95)', 2.2 ); // 合力矢量 drawArrow( pos.x, pos.y, pos.x + particle.Fx * fScale, pos.y - particle.Fy * fScale, 'rgba(245,158,11,0.95)', 2.2 ); ctx.save(); ctx.fillStyle = '#cbd5e1'; ctx.font = '13px sans-serif'; ctx.fillText(`t = ${num(particle.t, 2)} s`, 20, canvas.height - 20); ctx.restore(); } function drawGuide() { const p = getParams(); ctx.save(); ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.font = '14px sans-serif'; ctx.fillText('粒子从左向右射入。观察轨迹上偏、下偏或直线,以理解配速法。', 20, 80); if (p.B !== 0) { const ratio = p.v0 - p.E / p.B; let msg = ''; if (Math.abs(ratio) < 1e-6) msg = '当前已处于理论配速。'; else if (ratio < 0) msg = '当前 v₀ < E/B:对正电荷而言,电场力占优,轨迹上偏。'; else msg = '当前 v₀ > E/B:对正电荷而言,磁场力占优,轨迹下偏。'; ctx.fillText(msg, 20, 102); } ctx.restore(); } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawFields(); drawGuide(); drawTrace(); drawParticle(); } function step() { if (running) { const p = getParams(); const dtBase = 0.008; const dt = dtBase * p.speed; for (let i = 0; i < 2; i++) { rk4Step(dt); trace.push({ x: particle.x, y: particle.y }); if (trace.length > 5000) trace.shift(); } // 出界后自动重置,便于连续观察 if ( particle.x < world.xmin - 2 || particle.x > world.xmax + 2 || particle.y < world.ymin - 2 || particle.y > world.ymax + 2 ) { resetParticle(); } updateInfo(); } draw(); requestAnimationFrame(step); } // 事件绑定 [ui.q, ui.m, ui.E, ui.B, ui.v0, ui.speed].forEach(el => { el.addEventListener('input', () => { updateLabels(); if (el !== ui.speed) { resetParticle(); } }); }); ui.toggleBtn.addEventListener('click', () => { running = !running; ui.toggleBtn.textContent = running ? '暂停' : '继续'; }); ui.resetBtn.addEventListener('click', () => { resetParticle(); }); ui.clearBtn.addEventListener('click', () => { clearTrace(); }); ui.matchBtn.addEventListener('click', () => { const p = getParams(); if (p.B === 0) { alert('当 B = 0 时,不存在配速条件 v = E/B。请先把磁感应强度调到非零。'); return; } ui.v0.value = (p.E / p.B).toFixed(2); updateLabels(); resetParticle(); }); updateLabels(); resetParticle(); step(); </script>

About

物理场景的动画演示

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors