feat(phys11 ch5): Wave 9 — Глава 5 «Фотоны. Действия света» (§27-§29 + Финал)
- phys-fx.js: PHYS.PhotoeffectLab (катод+свет+анод+амперметр), PHYS.PlanckLinear (график Eк,max от ν) - ch5 — розовая тема (--pri:#ec4899), watermark hν/A/λ/★ - §27: кризис АЧТ, гипотеза Планка E=hν (1900), открытие ф/э (Герц, Столетов), 3 закона - §28: фотон, уравнение Эйнштейна hν=A+Eк, работа выхода, красная граница - §29: импульс фотона p=hν/c, давление света, опыт Лебедева (1900), КВ-дуализм, де Бройль λ=h/p - 6 квизов + 3 босса (b1-b3) - Финал: 3 интегральных босса (fb1-fb3), +150 XP бонус, ачивка ch5_master
This commit is contained in:
@@ -1732,4 +1732,187 @@ class LengthContraction {
|
||||
}
|
||||
P.LengthContraction = LengthContraction;
|
||||
|
||||
/* ============================================================ */
|
||||
/* PhotoeffectLab — катод, свет, цепь, амперметр */
|
||||
/* ============================================================ */
|
||||
|
||||
class PhotoeffectLab {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 580;
|
||||
this.H = opts.height || 280;
|
||||
this.nu = opts.nu !== undefined ? opts.nu : 8e14; /* Гц */
|
||||
this.nu0 = opts.nu0 !== undefined ? opts.nu0 : 5.5e14; /* красная граница */
|
||||
this.U = opts.U !== undefined ? opts.U : 0; /* В, тормозящее < 0 */
|
||||
this.phase = 0;
|
||||
this.paused = false;
|
||||
util.subscribe(this);
|
||||
util.observe(this);
|
||||
this._render();
|
||||
}
|
||||
setNu(v){ this.nu = v; }
|
||||
setNu0(v){ this.nu0 = v; }
|
||||
setU(v){ this.U = v; }
|
||||
/* Кинетическая энергия фотоэлектронов: hν - A (А = hν₀) */
|
||||
ekMax(){
|
||||
const h = 6.63e-34;
|
||||
return Math.max(0, h * (this.nu - this.nu0));
|
||||
}
|
||||
/* Электроны летят, если их Eк больше тормозящего eU */
|
||||
flying(){
|
||||
const e = 1.6e-19;
|
||||
return this.nu > this.nu0 && this.ekMax() > e * Math.max(0, -this.U);
|
||||
}
|
||||
update(dt){ this.phase += dt * 4; }
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
let svg = util.svgFrame(W, H, {bg:'#fef3c7'});
|
||||
/* Лампа-источник */
|
||||
const lx = 50, ly = 60;
|
||||
svg += '<circle cx="' + lx + '" cy="' + ly + '" r="22" fill="#fde68a" stroke="#f59e0b" stroke-width="2"/>';
|
||||
svg += '<text x="' + lx + '" y="' + (ly + 5) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#92400e" font-weight="700">hν</text>';
|
||||
/* Луч света — цвет зависит от ν: 4e14 (красн) до 1e15 (фиолет) */
|
||||
const nuRel = Math.max(0, Math.min(1, (this.nu - 4e14) / 6e14));
|
||||
const lightColor = `rgb(${Math.round(255*(1-nuRel))},${Math.round(80+120*(1-Math.abs(nuRel-0.5)*2))},${Math.round(255*nuRel)})`;
|
||||
for (let i = 0; i < 6; i++){
|
||||
const yo = (this.phase * 30 + i * 22) % 120;
|
||||
svg += '<line x1="' + (lx + 22) + '" y1="' + (ly - 30 + yo) + '" x2="' + (lx + 100) + '" y2="' + (ly + 50 + yo) + '" stroke="' + lightColor + '" stroke-width="2" opacity="0.7"/>';
|
||||
}
|
||||
/* Катод */
|
||||
const cx = 180, cyy = 140;
|
||||
svg += '<rect x="' + cx + '" y="' + (cyy - 40) + '" width="14" height="80" fill="#475569" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
svg += '<text x="' + (cx + 7) + '" y="' + (cyy + 60) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">катод (-)</text>';
|
||||
/* Анод */
|
||||
const ax = 380;
|
||||
svg += '<rect x="' + ax + '" y="' + (cyy - 40) + '" width="14" height="80" fill="#94a3b8" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
svg += '<text x="' + (ax + 7) + '" y="' + (cyy + 60) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">анод (' + (this.U >= 0 ? '+' : '-') + ')</text>';
|
||||
/* Стеклянный баллон */
|
||||
svg += '<ellipse cx="' + ((cx + ax + 14) / 2) + '" cy="' + cyy + '" rx="160" ry="70" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 3"/>';
|
||||
/* Электроны летят */
|
||||
if (this.flying()){
|
||||
for (let i = 0; i < 5; i++){
|
||||
const t = ((this.phase * 0.7) + i * 0.2) % 1;
|
||||
const ex = cx + 14 + t * (ax - cx - 14);
|
||||
const ey = cyy + Math.sin(t * 6 + i) * 8;
|
||||
svg += '<circle cx="' + ex.toFixed(1) + '" cy="' + ey.toFixed(1) + '" r="4" fill="#16a34a" stroke="#065f46" stroke-width="1"/>';
|
||||
svg += '<text x="' + ex.toFixed(1) + '" y="' + (ey + 3).toFixed(1) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="8" fill="#fff">e</text>';
|
||||
}
|
||||
}
|
||||
/* Цепь — снизу */
|
||||
svg += '<line x1="' + (cx + 7) + '" y1="' + (cyy + 40) + '" x2="' + (cx + 7) + '" y2="' + (H - 30) + '" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
svg += '<line x1="' + (cx + 7) + '" y1="' + (H - 30) + '" x2="' + (ax + 7) + '" y2="' + (H - 30) + '" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
svg += '<line x1="' + (ax + 7) + '" y1="' + (cyy + 40) + '" x2="' + (ax + 7) + '" y2="' + (H - 30) + '" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
/* Амперметр */
|
||||
const amx = (cx + ax) / 2 + 7, amy = H - 30;
|
||||
svg += '<circle cx="' + amx + '" cy="' + amy + '" r="14" fill="#fff" stroke="#0f172a" stroke-width="1.6"/>';
|
||||
svg += '<text x="' + amx + '" y="' + (amy + 4) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">A</text>';
|
||||
/* Стрелка тока */
|
||||
if (this.flying()){
|
||||
svg += '<text x="' + (amx - 28) + '" y="' + (amy - 18) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#16a34a" font-weight="700">I > 0</text>';
|
||||
} else {
|
||||
svg += '<text x="' + (amx - 28) + '" y="' + (amy - 18) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#dc2626" font-weight="700">I = 0</text>';
|
||||
}
|
||||
/* Подписи */
|
||||
const h = 6.63e-34, e_ch = 1.6e-19;
|
||||
const Em = this.ekMax();
|
||||
const Aevh = h * this.nu0 / e_ch;
|
||||
svg += '<text x="' + (W - 12) + '" y="22" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="#0f172a" font-weight="700">hν = A + Eк</text>';
|
||||
svg += '<text x="' + (W - 12) + '" y="40" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">ν = ' + (this.nu / 1e14).toFixed(2) + '·10¹⁴ Гц</text>';
|
||||
svg += '<text x="' + (W - 12) + '" y="56" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">ν₀ = ' + (this.nu0 / 1e14).toFixed(2) + '·10¹⁴ Гц</text>';
|
||||
svg += '<text x="' + (W - 12) + '" y="72" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">A = ' + Aevh.toFixed(2) + ' эВ</text>';
|
||||
svg += '<text x="' + (W - 12) + '" y="88" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">Eк,max = ' + (Em/e_ch).toFixed(2) + ' эВ</text>';
|
||||
svg += '<text x="' + (W - 12) + '" y="104" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">U = ' + this.U.toFixed(2) + ' В</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
_render(){ this.render(); }
|
||||
}
|
||||
P.PhotoeffectLab = PhotoeffectLab;
|
||||
|
||||
/* ============================================================ */
|
||||
/* PlanckLinear — график Eк,max от ν */
|
||||
/* ============================================================ */
|
||||
|
||||
class PlanckLinear {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 560;
|
||||
this.H = opts.height || 280;
|
||||
this.nu0 = opts.nu0 !== undefined ? opts.nu0 : 5e14;
|
||||
this.nu = opts.nu !== undefined ? opts.nu : 8e14;
|
||||
this.paused = true;
|
||||
this.render();
|
||||
}
|
||||
setNu0(v){ this.nu0 = v; this.render(); }
|
||||
setNu(v){ this.nu = v; this.render(); }
|
||||
update(){}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const pad = 50;
|
||||
const left = pad, right = W - pad - 80, top = 30, bot = H - 50;
|
||||
const numax = 12e14;
|
||||
const h = 6.63e-34, e_ch = 1.6e-19;
|
||||
const eMaxEV = h * (numax - this.nu0) / e_ch;
|
||||
let svg = util.svgFrame(W, H, {bg:'#fdf2f8'});
|
||||
/* Сетка */
|
||||
svg += '<g stroke="#fbcfe8" stroke-width="0.8">';
|
||||
for (let i = 0; i <= 12; i++){
|
||||
const x = left + i * (right - left) / 12;
|
||||
svg += '<line x1="' + x + '" y1="' + top + '" x2="' + x + '" y2="' + bot + '"/>';
|
||||
}
|
||||
for (let i = 0; i <= 6; i++){
|
||||
const y = top + i * (bot - top) / 6;
|
||||
svg += '<line x1="' + left + '" y1="' + y + '" x2="' + right + '" y2="' + y + '"/>';
|
||||
}
|
||||
svg += '</g>';
|
||||
/* Оси */
|
||||
svg += '<line x1="' + left + '" y1="' + bot + '" x2="' + right + '" y2="' + bot + '" stroke="#0f172a" stroke-width="1.4"/>';
|
||||
svg += '<line x1="' + left + '" y1="' + top + '" x2="' + left + '" y2="' + bot + '" stroke="#0f172a" stroke-width="1.4"/>';
|
||||
svg += '<text x="' + ((left+right)/2) + '" y="' + (bot + 30) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569">ν (·10¹⁴ Гц)</text>';
|
||||
svg += '<text x="' + (left - 30) + '" y="' + ((top+bot)/2) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" transform="rotate(-90 ' + (left - 30) + ' ' + ((top+bot)/2) + ')">Eк,max (эВ)</text>';
|
||||
/* Прямая Eк = h(ν - ν₀) */
|
||||
const nu0x = left + (this.nu0 / numax) * (right - left);
|
||||
let pts = '';
|
||||
for (let i = 0; i <= 50; i++){
|
||||
const v = this.nu0 + i * (numax - this.nu0) / 50;
|
||||
const eV = h * (v - this.nu0) / e_ch;
|
||||
const x = left + (v / numax) * (right - left);
|
||||
const y = bot - (eV / Math.max(0.1, eMaxEV)) * (bot - top);
|
||||
pts += x.toFixed(1) + ',' + y.toFixed(1) + ' ';
|
||||
}
|
||||
svg += '<polyline points="' + pts + '" fill="none" stroke="#ec4899" stroke-width="2.4"/>';
|
||||
/* До ν₀ — горизонтальная линия Eк = 0 */
|
||||
svg += '<line x1="' + left + '" y1="' + bot + '" x2="' + nu0x + '" y2="' + bot + '" stroke="#ec4899" stroke-width="2.4" stroke-dasharray="4 3"/>';
|
||||
/* Отметка ν₀ */
|
||||
svg += '<line x1="' + nu0x + '" y1="' + bot + '" x2="' + nu0x + '" y2="' + (bot + 6) + '" stroke="#dc2626" stroke-width="1.4"/>';
|
||||
svg += '<text x="' + nu0x + '" y="' + (bot + 18) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#dc2626" font-weight="700">ν₀</text>';
|
||||
/* Текущая точка ν */
|
||||
const cx2 = left + (this.nu / numax) * (right - left);
|
||||
const eVcur = Math.max(0, h * (this.nu - this.nu0) / e_ch);
|
||||
const cy2 = bot - (eVcur / Math.max(0.1, eMaxEV)) * (bot - top);
|
||||
svg += '<line x1="' + cx2.toFixed(1) + '" y1="' + bot + '" x2="' + cx2.toFixed(1) + '" y2="' + cy2.toFixed(1) + '" stroke="#0f172a" stroke-width="1" stroke-dasharray="3 3"/>';
|
||||
svg += '<circle cx="' + cx2.toFixed(1) + '" cy="' + cy2.toFixed(1) + '" r="5" fill="#0f172a"/>';
|
||||
/* Метки на осях X */
|
||||
for (let v = 0; v <= 12; v += 2){
|
||||
const x = left + (v / 12) * (right - left);
|
||||
svg += '<text x="' + x + '" y="' + (bot + 14) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="9" fill="#64748b">' + v + '</text>';
|
||||
}
|
||||
/* Подпись */
|
||||
svg += '<text x="' + (W/2) + '" y="20" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">Eк,max = h(ν − ν₀) — угловой коэф. = h</text>';
|
||||
/* Панель */
|
||||
const px = right + 12;
|
||||
svg += '<text x="' + px + '" y="' + (top + 14) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#ec4899" font-weight="700">ν = ' + (this.nu/1e14).toFixed(1) + '·10¹⁴</text>';
|
||||
svg += '<text x="' + px + '" y="' + (top + 32) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">ν₀ = ' + (this.nu0/1e14).toFixed(1) + '·10¹⁴</text>';
|
||||
svg += '<text x="' + px + '" y="' + (top + 50) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#0f172a">Eк = ' + eVcur.toFixed(2) + ' эВ</text>';
|
||||
svg += '<text x="' + px + '" y="' + (top + 68) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">A = ' + (h * this.nu0 / e_ch).toFixed(2) + ' эВ</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.PlanckLinear = PlanckLinear;
|
||||
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user