feat(phys11 W3): Глава 2 §7-§9 + расширение phys-fx.js (LCcircuit, ACgen, Transformer)
phys-fx.js (+3 электротехнических компонента):
- PHYS.LCcircuit: колебательный контур со схемой C↔L, провода, стрелка тока,
заряды на пластинах (меняют знак), энергетические столбцы W_C и W_L,
формула T=2π√(LC) с актуальным значением
- PHYS.ACgen: генератор переменного тока — слева вращающаяся рамка в B,
справа график U(t)=U₀sin(ωt) с историей
- PHYS.Transformer: схема трансформатора с сердечником, обмотки N₁, N₂,
входное U₁, расчётное U₂, коэф. трансформации k, отметка повышающий/понижающий
physics_11_ch2.html (~63 КБ, violet-тема):
- 2-кол layout с col-side, hero violet-градиент
- psel-grid 8 карточек (§7-§13 + Финал); §7-§9 активны
- Watermarks: LC, ∿, ≡, ⚡, ⚙, λ, ☣, ★
§7 Колебательный контур. Формула Томсона:
- 3 теор. карточки (контур, формула Томсона, превращения энергии)
- Инт. 1: LCcircuit с ползунками L (1-100 мГн), C (0.1-10 мкФ)
- Инт. 2: расчёт T, ν (5 input)
- Инт. 3: аналогии и свойства (5 MC)
- Босс §7: 5 этапов, +70 XP
§8 Вынужденные ЭМ колеб. Переменный ток:
- 2 теор. карточки (генератор, действ. значения I₀/√2)
- Инт. 1: ACgen (вращ. рамка → синусоида) с ползунком ω
- Инт. 2: расчёт I/I₀, U/U₀ (5 input)
- Инт. 3: теория действующих значений (5 MC)
- Босс §8: 5 этапов, +70 XP
§9 Трансформатор:
- 2 теор. карточки (устройство, коэф. трансформации, I₁U₁=I₂U₂)
- Инт. 1: Transformer с ползунками N₁ (50-1000), N₂ (10-1000), U₁ (12-10000 В)
- Инт. 2: расчёт U₂, I₂, k (5 input)
- Инт. 3: повышающий/понижающий (5 MC)
- Босс §9: 5 этапов, +70 XP
§10-§13, Финал — stub-карточки 'в разработке (W4)'.
LocalStorage: physics11_ch2_*, общий physics11_xp
Server sync: /api/textbooks/physics-11-ch2/progress
This commit is contained in:
@@ -595,4 +595,241 @@ class LongitudinalWave {
|
||||
}
|
||||
P.LongitudinalWave = LongitudinalWave;
|
||||
|
||||
/* ============================================================ */
|
||||
/* LCcircuit — колебательный контур */
|
||||
/* q(t) = Q0 cos(ωt), i(t) = -Q0 ω sin(ωt), ω = 1/√(LC) */
|
||||
/* ============================================================ */
|
||||
|
||||
class LCcircuit {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 480;
|
||||
this.H = opts.height || 280;
|
||||
this.L = opts.L !== undefined ? opts.L : 0.01; /* Гн */
|
||||
this.C = opts.C !== undefined ? opts.C : 1e-6; /* Ф */
|
||||
this.Q0 = opts.Q0 !== undefined ? opts.Q0 : 1.0; /* нормированный заряд */
|
||||
this.color = opts.color || '#7c3aed';
|
||||
this.paused = false;
|
||||
this.t = 0;
|
||||
util.subscribe(this);
|
||||
util.observe(this);
|
||||
this.render();
|
||||
}
|
||||
setL(L){ this.L = Math.max(1e-4, L); }
|
||||
setC(C){ this.C = Math.max(1e-9, C); }
|
||||
period(){ return 2 * Math.PI * Math.sqrt(this.L * this.C); }
|
||||
freq(){ return 1 / this.period(); }
|
||||
update(dt){ this.t += dt; }
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const T = this.period();
|
||||
const omega = 2 * Math.PI / T;
|
||||
const phase = omega * this.t;
|
||||
const q = this.Q0 * Math.cos(phase);
|
||||
const i = -this.Q0 * omega * Math.sin(phase);
|
||||
/* Геометрия: C слева вверху, L справа вверху, соединены проводами */
|
||||
const cx = W / 2, cy = H / 2 - 20;
|
||||
const cap = {x: cx - 100, y: cy};
|
||||
const ind = {x: cx + 100, y: cy};
|
||||
/* Energies (для подсветки): W_C ~ q², W_L ~ i² */
|
||||
const WC = q * q;
|
||||
const WL = (i / omega) * (i / omega); /* в норм. единицах */
|
||||
const total = WC + WL;
|
||||
const cOpacity = 0.3 + 0.7 * WC / total;
|
||||
const lOpacity = 0.3 + 0.7 * WL / total;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
/* Провода */
|
||||
const wireY1 = cy - 50, wireY2 = cy + 50;
|
||||
svg += '<path d="M ' + cap.x + ' ' + (cy - 18) + ' L ' + cap.x + ' ' + wireY1 + ' L ' + ind.x + ' ' + wireY1 + ' L ' + ind.x + ' ' + (cy - 18) + '" fill="none" stroke="#0f172a" stroke-width="2"/>';
|
||||
svg += '<path d="M ' + cap.x + ' ' + (cy + 18) + ' L ' + cap.x + ' ' + wireY2 + ' L ' + ind.x + ' ' + wireY2 + ' L ' + ind.x + ' ' + (cy + 18) + '" fill="none" stroke="#0f172a" stroke-width="2"/>';
|
||||
/* Конденсатор: две параллельные пластины */
|
||||
const plateW = 36;
|
||||
svg += '<g stroke="#0f172a" stroke-width="3">';
|
||||
svg += '<line x1="' + (cap.x - plateW/2) + '" y1="' + (cap.y - 18) + '" x2="' + (cap.x + plateW/2) + '" y2="' + (cap.y - 18) + '"/>';
|
||||
svg += '<line x1="' + (cap.x - plateW/2) + '" y1="' + (cap.y + 18) + '" x2="' + (cap.x + plateW/2) + '" y2="' + (cap.y + 18) + '"/>';
|
||||
svg += '</g>';
|
||||
/* Заряды на пластинах */
|
||||
const sign = q > 0 ? 1 : -1;
|
||||
const topCh = sign > 0 ? '+' : '−';
|
||||
const botCh = sign > 0 ? '−' : '+';
|
||||
const qAbsNorm = Math.abs(q) / this.Q0;
|
||||
svg += '<text x="' + cap.x + '" y="' + (cap.y - 24) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="14" font-weight="800" fill="#dc2626" opacity="' + (qAbsNorm).toFixed(2) + '">' + topCh + '</text>';
|
||||
svg += '<text x="' + cap.x + '" y="' + (cap.y + 36) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="14" font-weight="800" fill="#2563eb" opacity="' + (qAbsNorm).toFixed(2) + '">' + botCh + '</text>';
|
||||
svg += '<text x="' + (cap.x - 30) + '" y="' + (cap.y + 5) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="13" fill="#dc2626" font-weight="700">C</text>';
|
||||
/* Индуктор: петли */
|
||||
const coils = 4, coilR = 8, coilW = 64;
|
||||
let coilPath = 'M ' + (ind.x - coilW/2) + ' ' + ind.y;
|
||||
for (let k = 0; k < coils; k++){
|
||||
const x0 = ind.x - coilW/2 + (coilW / coils) * k;
|
||||
coilPath += ' a ' + coilR + ' ' + coilR + ' 0 0 1 ' + (coilW / coils) + ' 0';
|
||||
}
|
||||
svg += '<path d="' + coilPath + '" fill="none" stroke="#0f172a" stroke-width="2.4"/>';
|
||||
svg += '<text x="' + (ind.x + 40) + '" y="' + (ind.y + 5) + '" font-family="JetBrains Mono,monospace" font-size="13" fill="#2563eb" font-weight="700">L</text>';
|
||||
/* Стрелка тока */
|
||||
const iDir = i > 0 ? 1 : -1;
|
||||
const iAbs = Math.abs(i) / (this.Q0 * omega);
|
||||
if (iAbs > 0.05){
|
||||
const aY = wireY1 - 14;
|
||||
const aX1 = cx - 30 * iDir;
|
||||
const aX2 = cx + 30 * iDir;
|
||||
svg += '<line x1="' + aX1 + '" y1="' + aY + '" x2="' + aX2 + '" y2="' + aY + '" stroke="#16a34a" stroke-width="2.4" opacity="' + iAbs.toFixed(2) + '"/>';
|
||||
svg += '<polygon points="' + aX2 + ',' + aY + ' ' + (aX2 - 8 * iDir) + ',' + (aY - 4) + ' ' + (aX2 - 8 * iDir) + ',' + (aY + 4) + '" fill="#16a34a" opacity="' + iAbs.toFixed(2) + '"/>';
|
||||
svg += '<text x="' + cx + '" y="' + (aY - 6) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#16a34a" font-weight="700">i</text>';
|
||||
}
|
||||
/* Энергетические столбцы */
|
||||
const eY = H - 36, eH = 24;
|
||||
svg += '<rect x="40" y="' + eY + '" width="120" height="' + eH + '" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>';
|
||||
svg += '<rect x="40" y="' + eY + '" width="' + (120 * WC / total).toFixed(1) + '" height="' + eH + '" fill="#dc2626" opacity="0.7"/>';
|
||||
svg += '<text x="' + (40 + 60) + '" y="' + (eY + eH + 12) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">W_C</text>';
|
||||
svg += '<rect x="' + (W - 160) + '" y="' + eY + '" width="120" height="' + eH + '" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>';
|
||||
svg += '<rect x="' + (W - 160) + '" y="' + eY + '" width="' + (120 * WL / total).toFixed(1) + '" height="' + eH + '" fill="#2563eb" opacity="0.7"/>';
|
||||
svg += '<text x="' + (W - 100) + '" y="' + (eY + eH + 12) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">W_L</text>';
|
||||
/* Подпись периода */
|
||||
const Tdisp = T < 1e-3 ? (T * 1e6).toFixed(1) + ' мкс' : (T * 1e3).toFixed(2) + ' мс';
|
||||
svg += '<text x="' + (W / 2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" fill="' + this.color + '" font-weight="800">T = 2π√(LC) = ' + Tdisp + '</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.LCcircuit = LCcircuit;
|
||||
|
||||
/* ============================================================ */
|
||||
/* ACgen — генератор переменного тока (вращающаяся рамка в B) */
|
||||
/* ============================================================ */
|
||||
|
||||
class ACgen {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 540;
|
||||
this.H = opts.height || 240;
|
||||
this.omega = opts.omega !== undefined ? opts.omega : 2 * Math.PI; /* рад/с */
|
||||
this.U0 = opts.U0 !== undefined ? opts.U0 : 1.0;
|
||||
this.color = opts.color || '#7c3aed';
|
||||
this.tWindow = opts.tWindow || 4;
|
||||
this.paused = false;
|
||||
this.t = 0;
|
||||
this.history = [];
|
||||
util.subscribe(this);
|
||||
util.observe(this);
|
||||
this.render();
|
||||
}
|
||||
setOmega(w){ this.omega = w; this.history = []; }
|
||||
update(dt){
|
||||
this.t += dt;
|
||||
this.history.push([this.t, this.U0 * Math.sin(this.omega * this.t)]);
|
||||
while (this.history.length && this.history[0][0] < this.t - this.tWindow) this.history.shift();
|
||||
}
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
/* Левая часть: рамка в магнитном поле; правая: график U(t) */
|
||||
const leftW = 200;
|
||||
const rightLeft = leftW + 10;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
/* Магнитное поле (стрелки B) */
|
||||
svg += '<g stroke="#94a3b8" stroke-width="1.2">';
|
||||
for (let i = 0; i < 5; i++){
|
||||
const x = 20 + i * 40;
|
||||
svg += '<line x1="' + x + '" y1="20" x2="' + x + '" y2="' + (H - 20) + '" marker-end="url(#acgen-arr)"/>';
|
||||
}
|
||||
svg += '</g>';
|
||||
svg += '<defs><marker id="acgen-arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#94a3b8"/></marker></defs>';
|
||||
svg += '<text x="14" y="14" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">B</text>';
|
||||
/* Вращающаяся рамка: эллипс, отображающий проекцию прямоугольника */
|
||||
const fx = 110, fy = H / 2;
|
||||
const phi = this.omega * this.t;
|
||||
const rx = 40 * Math.abs(Math.cos(phi));
|
||||
const ry = 30;
|
||||
svg += '<ellipse cx="' + fx + '" cy="' + fy + '" rx="' + rx.toFixed(1) + '" ry="' + ry + '" fill="none" stroke="#dc2626" stroke-width="2.4"/>';
|
||||
/* Ось вращения */
|
||||
svg += '<line x1="' + fx + '" y1="' + (fy - 50) + '" x2="' + fx + '" y2="' + (fy + 50) + '" stroke="#0f172a" stroke-width="2" stroke-dasharray="4 3"/>';
|
||||
/* Контакт скользящих колец (схема) */
|
||||
svg += '<line x1="' + fx + '" y1="' + (fy + 50) + '" x2="' + fx + '" y2="' + (fy + 70) + '" stroke="#0f172a" stroke-width="2"/>';
|
||||
svg += '<line x1="' + fx + '" y1="' + (fy + 70) + '" x2="' + rightLeft + '" y2="' + (fy + 70) + '" stroke="#0f172a" stroke-width="2"/>';
|
||||
/* График U(t) — справа */
|
||||
const gPad = 26;
|
||||
const tMin = Math.max(0, this.t - this.tWindow);
|
||||
const ax = util.axes(W - rightLeft, H, gPad, this.tWindow, [-this.U0 * 1.2, this.U0 * 1.2]);
|
||||
svg += '<g transform="translate(' + rightLeft + ' 0)">' + ax.svg + '</g>';
|
||||
if (this.history.length > 1){
|
||||
const pts = this.history.map(([t, y]) => (rightLeft + ax.left + (t - tMin) * (ax.right - ax.left) / this.tWindow).toFixed(1) + ',' + ax.toY(y).toFixed(1));
|
||||
svg += '<polyline points="' + pts.join(' ') + '" fill="none" stroke="' + this.color + '" stroke-width="2.4"/>';
|
||||
}
|
||||
/* Подпись */
|
||||
svg += '<text x="' + (rightLeft + 20) + '" y="20" font-family="JetBrains Mono,monospace" font-size="12" fill="' + this.color + '" font-weight="700">U(t) = U₀ sin(ωt)</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.ACgen = ACgen;
|
||||
|
||||
/* ============================================================ */
|
||||
/* Transformer — схема трансформатора с расчётом */
|
||||
/* ============================================================ */
|
||||
|
||||
class Transformer {
|
||||
constructor(container, opts){
|
||||
opts = opts || {};
|
||||
this.el = (typeof container === 'string') ? document.querySelector(container) : container;
|
||||
this.W = opts.width || 520;
|
||||
this.H = opts.height || 240;
|
||||
this.N1 = opts.N1 || 200;
|
||||
this.N2 = opts.N2 || 50;
|
||||
this.U1 = opts.U1 || 220;
|
||||
this.color = opts.color || '#7c3aed';
|
||||
this.render();
|
||||
}
|
||||
setN1(n){ this.N1 = Math.max(1, n|0); this.render(); }
|
||||
setN2(n){ this.N2 = Math.max(1, n|0); this.render(); }
|
||||
setU1(u){ this.U1 = u; this.render(); }
|
||||
update(){ /* статика */ }
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const k = this.N1 / this.N2;
|
||||
const U2 = this.U1 / k;
|
||||
const cy = H / 2;
|
||||
let svg = util.svgFrame(W, H, {bg:'#f8fafc'});
|
||||
/* Сердечник трансформатора (прямоугольник с вырезом) */
|
||||
const coreL = 110, coreR = W - 110;
|
||||
svg += '<rect x="' + coreL + '" y="' + (cy - 70) + '" width="' + (coreR - coreL) + '" height="140" fill="none" stroke="#64748b" stroke-width="6"/>';
|
||||
svg += '<rect x="' + (coreL + 16) + '" y="' + (cy - 54) + '" width="' + (coreR - coreL - 32) + '" height="108" fill="#f8fafc" stroke="none"/>';
|
||||
/* Первичная обмотка слева */
|
||||
const coil1X = coreL + 24;
|
||||
for (let i = 0; i < 6; i++){
|
||||
const y = cy - 50 + i * 16;
|
||||
svg += '<path d="M ' + (coil1X - 12) + ' ' + y + ' a 8 8 0 0 1 8 -8 a 8 8 0 0 1 8 8 a 8 8 0 0 1 -8 8 a 8 8 0 0 1 -8 -8 z" fill="none" stroke="#dc2626" stroke-width="2.4"/>';
|
||||
}
|
||||
/* Вторичная обмотка справа */
|
||||
const coil2X = coreR - 24;
|
||||
/* Адаптируем число витков визуально (max 8 для удобства) */
|
||||
const visTurns2 = Math.max(2, Math.min(10, Math.round(6 * this.N2 / this.N1)));
|
||||
for (let i = 0; i < visTurns2; i++){
|
||||
const y = cy - 50 + i * (100 / visTurns2);
|
||||
svg += '<path d="M ' + (coil2X - 12) + ' ' + y + ' a 6 6 0 0 1 8 -6 a 6 6 0 0 1 8 6 a 6 6 0 0 1 -8 6 a 6 6 0 0 1 -8 -6 z" fill="none" stroke="#2563eb" stroke-width="2.4"/>';
|
||||
}
|
||||
/* Провода-выходы первичной */
|
||||
svg += '<line x1="' + (coil1X - 12) + '" y1="' + (cy - 50) + '" x2="20" y2="' + (cy - 50) + '" stroke="#dc2626" stroke-width="2"/>';
|
||||
svg += '<line x1="' + (coil1X - 12) + '" y1="' + (cy + 50) + '" x2="20" y2="' + (cy + 50) + '" stroke="#dc2626" stroke-width="2"/>';
|
||||
/* Провода вторичной */
|
||||
svg += '<line x1="' + (coil2X + 4) + '" y1="' + (cy - 50) + '" x2="' + (W - 20) + '" y2="' + (cy - 50) + '" stroke="#2563eb" stroke-width="2"/>';
|
||||
svg += '<line x1="' + (coil2X + 4) + '" y1="' + (cy + 50) + '" x2="' + (W - 20) + '" y2="' + (cy + 50) + '" stroke="#2563eb" stroke-width="2"/>';
|
||||
/* Подписи N1, N2, U1, U2 */
|
||||
svg += '<text x="' + (coil1X - 16) + '" y="' + (cy - 60) + '" font-family="JetBrains Mono,monospace" font-size="13" fill="#dc2626" font-weight="800">N₁ = ' + this.N1 + '</text>';
|
||||
svg += '<text x="' + (coil2X + 16) + '" y="' + (cy - 60) + '" font-family="JetBrains Mono,monospace" font-size="13" fill="#2563eb" font-weight="800" text-anchor="end">N₂ = ' + this.N2 + '</text>';
|
||||
svg += '<text x="' + (coil2X + 6) + '" y="' + (cy - 60) + '" font-family="JetBrains Mono,monospace" font-size="13" fill="#2563eb" font-weight="800">N₂ = ' + this.N2 + '</text>';
|
||||
svg += '<text x="20" y="' + (cy - 56) + '" font-family="JetBrains Mono,monospace" font-size="12" fill="#dc2626">U₁ = ' + this.U1.toFixed(0) + ' В</text>';
|
||||
svg += '<text x="' + (W - 20) + '" y="' + (cy - 56) + '" font-family="JetBrains Mono,monospace" font-size="12" fill="#2563eb" text-anchor="end">U₂ = ' + U2.toFixed(1) + ' В</text>';
|
||||
/* Подпись коэф. */
|
||||
svg += '<text x="' + (W/2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" fill="' + this.color + '" font-weight="800">k = N₁/N₂ = ' + k.toFixed(2) + '</text>';
|
||||
svg += '<text x="' + (W/2) + '" y="' + (H - 12) + '" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">' + (k > 1 ? 'понижающий' : 'повышающий') + '</text>';
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
}
|
||||
P.Transformer = Transformer;
|
||||
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user