feat(phys11 ch6): Wave 10 — Глава 6 «Физика атома» (§30-§34 + Финал)

- phys-fx.js: PHYS.BohrAtom (атом водорода с орбитами + переходом + фотон), PHYS.EnergyLevels (диаграмма E_n + переход + λ)
- ch6 — зелёная тема (--pri:#16a34a), watermark ⊕/n/ν/ω/L/★
- §30: открытие электрона (Томсон), опыт Резерфорда (1911), состав ядра, изотопы
- §31: 2 постулата Бора (1913), E_n=-13,6/n², Боровский радиус
- §32: спектры (сплошные/линейчатые/полосатые), формула Ридберга, серии Лаймана/Бальмера/Пашена
- §33: спонтанное и индуцированное излучение (Эйнштейн 1916), инверсная населённость
- §34: лазеры (Мейман 1960), резонатор, 4 свойства лазерного излучения, применения
- 10 квизов + 5 боссов (b1-b5) + buildSingleQuizPara helper для компактности
- Финал: 3 интегральных босса (fb1-fb3), +150 XP бонус, ачивка ch6_master
This commit is contained in:
Maxim Dolgolyov
2026-05-29 19:18:04 +03:00
parent b5c224d7f5
commit a6d86f4374
2 changed files with 1069 additions and 130 deletions
+159
View File
@@ -1915,4 +1915,163 @@ class PlanckLinear {
}
P.PlanckLinear = PlanckLinear;
/* ============================================================ */
/* BohrAtom — атом водорода с орбитами и переходом */
/* ============================================================ */
class BohrAtom {
constructor(container, opts){
opts = opts || {};
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
this.W = opts.width || 540;
this.H = opts.height || 380;
this.n_from = opts.n_from !== undefined ? opts.n_from : 3;
this.n_to = opts.n_to !== undefined ? opts.n_to : 2;
this.phase = 0;
this.transitioning = false;
this.t_trans = 0;
this.paused = false;
util.subscribe(this);
util.observe(this);
this._render();
}
setFrom(n){ this.n_from = n; this.startTransition(); }
setTo(n){ this.n_to = n; this.startTransition(); }
startTransition(){ this.transitioning = true; this.t_trans = 0; }
update(dt){
this.phase += dt * 2;
if (this.transitioning){
this.t_trans += dt;
if (this.t_trans > 1.5){ this.transitioning = false; this.t_trans = 0; }
}
}
render(){
if (!this.el) return;
const W = this.W, H = this.H;
const cx = W / 2, cy = H / 2;
let svg = util.svgFrame(W, H, {bg:'#0f172a'});
/* Ядро */
svg += '<circle cx="' + cx + '" cy="' + cy + '" r="14" fill="#dc2626" stroke="#fff" stroke-width="2"/>';
svg += '<text x="' + cx + '" y="' + (cy + 5) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" fill="#fff" font-weight="700">+</text>';
/* Орбиты n = 1..5 */
const radii = [30, 55, 85, 120, 160];
for (let n = 0; n < 5; n++){
const r = radii[n];
const active = (n + 1 === this.n_from || n + 1 === this.n_to);
svg += '<circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="none" stroke="' + (active ? '#fcd34d' : '#475569') + '" stroke-width="' + (active ? 1.5 : 0.8) + '" opacity="' + (active ? 0.9 : 0.5) + '"/>';
svg += '<text x="' + (cx + r + 4) + '" y="' + (cy + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#94a3b8">n=' + (n + 1) + '</text>';
}
/* Электрон на текущей орбите */
let curN;
if (this.transitioning){
const t = Math.min(1, this.t_trans / 0.8);
const r1 = radii[this.n_from - 1] || 30;
const r2 = radii[this.n_to - 1] || 30;
const r = r1 + (r2 - r1) * t;
const a = this.phase;
const ex = cx + r * Math.cos(a), ey = cy + r * Math.sin(a);
svg += '<circle cx="' + ex.toFixed(1) + '" cy="' + ey.toFixed(1) + '" r="6" fill="#06b6d4" stroke="#fff" stroke-width="1.4"/>';
curN = this.n_to;
/* Фотон при переходе на нижний уровень */
if (this.n_from > this.n_to && t > 0.4){
const pt = (t - 0.4) / 0.6;
const fx = ex + 80 * pt;
const fy = ey - 40 * pt;
svg += '<g><path d="M ' + ex.toFixed(1) + ' ' + ey.toFixed(1) + ' Q ' + ((ex + fx) / 2).toFixed(1) + ' ' + ((ey + fy) / 2 - 10).toFixed(1) + ' ' + fx.toFixed(1) + ' ' + fy.toFixed(1) + '" stroke="#facc15" stroke-width="2" fill="none" opacity="' + (1 - pt) + '"/>';
svg += '<text x="' + (fx + 12).toFixed(1) + '" y="' + fy.toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#facc15" font-weight="700" opacity="' + (1 - pt) + '">hν</text></g>';
}
} else {
const r = radii[this.n_to - 1] || 30;
const a = this.phase;
const ex = cx + r * Math.cos(a), ey = cy + r * Math.sin(a);
svg += '<circle cx="' + ex.toFixed(1) + '" cy="' + ey.toFixed(1) + '" r="6" fill="#06b6d4" stroke="#fff" stroke-width="1.4"/>';
curN = this.n_to;
}
/* Подписи */
const En = -13.6 / (curN * curN);
svg += '<text x="14" y="22" font-family="JetBrains Mono,monospace" font-size="11" fill="#cbd5e1" font-weight="700">Боровская модель атома H</text>';
svg += '<text x="14" y="38" font-family="JetBrains Mono,monospace" font-size="10" fill="#94a3b8">n = ' + curN + ' · E = -13,6/n² = ' + En.toFixed(2) + ' эВ</text>';
if (this.n_from !== this.n_to){
const E_f = -13.6 / (this.n_from * this.n_from);
const E_t = -13.6 / (this.n_to * this.n_to);
const dE = Math.abs(E_t - E_f);
svg += '<text x="14" y="' + (H - 26) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#fcd34d" font-weight="700">переход ' + this.n_from + ' → ' + this.n_to + '</text>';
svg += '<text x="14" y="' + (H - 12) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#cbd5e1">hν = |E' + this.n_from + ' E' + this.n_to + '| = ' + dE.toFixed(2) + ' эВ</text>';
}
svg += '</svg>';
this.el.innerHTML = svg;
}
_render(){ this.render(); }
}
P.BohrAtom = BohrAtom;
/* ============================================================ */
/* EnergyLevels — диаграмма E_n + переход */
/* ============================================================ */
class EnergyLevels {
constructor(container, opts){
opts = opts || {};
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
this.W = opts.width || 540;
this.H = opts.height || 360;
this.n_from = opts.n_from !== undefined ? opts.n_from : 4;
this.n_to = opts.n_to !== undefined ? opts.n_to : 2;
this.paused = true;
this.render();
}
setFrom(n){ this.n_from = n; this.render(); }
setTo(n){ this.n_to = n; this.render(); }
update(){}
render(){
if (!this.el) return;
const W = this.W, H = this.H;
const pad = 40, leftLine = 200, rightLine = W - 100;
let svg = util.svgFrame(W, H, {bg:'#fef3c7'});
/* Уровни n=1..6 + ионизация */
const nMax = 6;
/* y(n) — нелинейное расположение, E пропорционально -1/n² */
function E(n){ return -13.6 / (n * n); }
function y(En){
const minE = -13.6, maxE = 0;
return pad + (maxE - En) / (maxE - minE) * (H - 2 * pad);
}
/* Линия ионизации (E=0) */
const y0 = y(0);
svg += '<line x1="' + leftLine + '" y1="' + y0 + '" x2="' + rightLine + '" y2="' + y0 + '" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/>';
svg += '<text x="' + (rightLine + 6) + '" y="' + (y0 + 4) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#dc2626" font-weight="700">E = 0 (ионизация)</text>';
/* Уровни */
for (let n = 1; n <= nMax; n++){
const En = E(n);
const yL = y(En);
const active = (n === this.n_from || n === this.n_to);
svg += '<line x1="' + leftLine + '" y1="' + yL.toFixed(1) + '" x2="' + rightLine + '" y2="' + yL.toFixed(1) + '" stroke="' + (active ? '#dc2626' : '#475569') + '" stroke-width="' + (active ? 2 : 1.2) + '"/>';
svg += '<text x="' + (leftLine - 8) + '" y="' + (yL + 4).toFixed(1) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="' + (active ? '#dc2626' : '#0f172a') + '" font-weight="' + (active ? '700' : '600') + '">n=' + n + '</text>';
svg += '<text x="' + (rightLine + 6) + '" y="' + (yL + 4).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">' + En.toFixed(2) + ' эВ</text>';
}
/* Переход — стрелка */
if (this.n_from !== this.n_to){
const E1 = E(this.n_from), E2 = E(this.n_to);
const y1 = y(E1), y2 = y(E2);
const xT = (leftLine + rightLine) / 2;
const color = E1 > E2 ? '#dc2626' : '#16a34a';
svg += '<line x1="' + xT + '" y1="' + y1.toFixed(1) + '" x2="' + xT + '" y2="' + y2.toFixed(1) + '" stroke="' + color + '" stroke-width="2.6"/>';
const dir = y2 < y1 ? -1 : 1;
svg += '<polygon points="' + xT + ',' + y2.toFixed(1) + ' ' + (xT - 6) + ',' + (y2 + 10 * dir).toFixed(1) + ' ' + (xT + 6) + ',' + (y2 + 10 * dir).toFixed(1) + '" fill="' + color + '"/>';
const dE = Math.abs(E1 - E2);
svg += '<text x="' + (xT + 12) + '" y="' + ((y1 + y2) / 2).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="12" fill="' + color + '" font-weight="700">hν = ' + dE.toFixed(2) + ' эВ</text>';
const lam = 1240 / dE;
svg += '<text x="' + (xT + 12) + '" y="' + ((y1 + y2) / 2 + 16).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="' + color + '">λ ≈ ' + lam.toFixed(0) + ' нм</text>';
}
/* Серии */
svg += '<text x="14" y="' + (y(E(1)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Лайман →</text>';
svg += '<text x="14" y="' + (y(E(2)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Бальмер →</text>';
svg += '<text x="14" y="' + (y(E(3)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Пашен →</text>';
svg += '<text x="' + (W/2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">E_n = -13,6/n² (атом водорода)</text>';
svg += '</svg>';
this.el.innerHTML = svg;
}
}
P.EnergyLevels = EnergyLevels;
})();
File diff suppressed because it is too large Load Diff