feat(phys8 ch2): Phase 3 Wave 2 — §21 эл. цепь + §22 закон Ома I=U/R

§21 Электрическая цепь:
- 3 теории: элементы цепи, амперметр/вольтметр, схема
- IV-1: интерактивная цепь с батареей, лампой, амперметром
  (последовательно), вольтметром (параллельно лампе) и ключом;
  при замыкании ключа лампа загорается и приборы показывают значения
- IV-2: 6 вопросов «как включать прибор?»
- IV-3: DnD 8 утверждений «правильно/неправильно»
- IV-4: 6 MCQ

§22 Закон Ома:
- 3 теории: I=U/R, выводы (U=IR, R=U/I), ВАХ
- IV-1: ВАХ-плоттер — slider R 2-50 Ом, динамическая прямая I(U)
  с подписью наклона
- IV-2: калькулятор U+R → I с «бытовой аналогией»
- IV-3: 5 концептуальных вопросов
- IV-4: 6 числовых задач

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 23:38:18 +03:00
parent 9a123be71c
commit 073cc3c06d
+446 -6
View File
@@ -372,8 +372,20 @@ const SIDEBARS = {
["Направление тока","от + к &minus; (исторически)"],
["В металлах","электроны против тока"]
]},
p21:{title:"Шпаргалка § 21",rows:[["В разработке","Phase 3 Wave 2"]]},
p22:{title:"Шпаргалка § 22",rows:[["В разработке","Phase 3 Wave 2"]]},
p21:{title:"Шпаргалка § 21",rows:[
["Цепь","источник, проводник, потребитель, ключ"],
["Амперметр","ПОСЛЕДОВАТЕЛЬНО, малое $R$"],
["Вольтметр","ПАРАЛЛЕЛЬНО, большое $R$"],
["Шкала А","А, мА, мкА"],
["Шкала В","В, мВ, кВ"]
]},
p22:{title:"Шпаргалка § 22",rows:[
["Закон Ома","$I = U / R$"],
["$R$","сопротивление, Ом"],
["ВАХ металла","прямая через 0"],
["Больше $U$","больше $I$ (пропорц.)"],
["Больше $R$","меньше $I$"]
]},
p23:{title:"Шпаргалка § 23",rows:[["В разработке","Phase 3 Wave 3"]]},
p24:{title:"Шпаргалка § 24",rows:[["В разработке","Phase 3 Wave 3"]]},
p25:{title:"Шпаргалка § 25",rows:[["В разработке","Phase 3 Wave 3"]]},
@@ -396,8 +408,8 @@ const TIPS=[
{sec:'p18',html:"Напряжение $U$ — это работа поля по перемещению единичного заряда: $A = qU$. Единица — 1 Вольт. Розетка 220 В, батарейка 1,5 В. Чем больше $U$, тем «сильнее» поле толкает заряд."},
{sec:'p19',html:"Ток — это упорядоченное движение свободных зарядов. Чтобы оно поддерживалось, нужен <b>источник тока</b> — батарейка, аккумулятор, генератор. Внутри источника <i>сторонние силы</i> переносят заряды против поля — это и есть «насос электричества»."},
{sec:'p20',html:"$I = q/t$ — сколько Кулонов прошло через сечение провода за 1 секунду. Единица — <b>Ампер</b> ($1$ А = $1$ Кл/с). За направление тока приняли движение «+» зарядов — хотя в металлах реально двигаются электроны (против тока)."},
{sec:'p21',html:араграф § 21 будет реализован в Phase 3 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p22',html:"Параграф § 22 будет реализован в Phase 3 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p21',html:ростейшая цепь: батарея, лампа, ключ, провода. Чтобы измерить ток через лампу — амперметр включают <b>последовательно</b> с лампой. Чтобы измерить напряжение на лампе — вольтметр включают <b>параллельно</b>."},
{sec:'p22',html:"Закон Ома для участка цепи: $I = U/R$. Увеличил напряжение в 2 раза — ток вырос в 2 раза. Поставил резистор побольше — ток упал. Прямая зависимость для металлов."},
{sec:'p23',html:"Параграф § 23 будет реализован в Phase 3 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p24',html:"Параграф § 24 будет реализован в Phase 3 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p25',html:"Параграф § 25 будет реализован в Phase 3 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
@@ -420,8 +432,8 @@ const BUILDERS = {
p18: ()=>{ build_p18(); },
p19: ()=>{ build_p19(); },
p20: ()=>{ build_p20(); },
p21: ()=>{ const box=document.getElementById('p21-body'); box.innerHTML = buildStub('p21', 'Электрическая цепь. Измерение силы тока и напряжения', 'Phase 3 Wave 2') + secNavFor('p21') + readButton('p21'); renderMath(box); wireReadBtn('p21'); },
p22: ()=>{ const box=document.getElementById('p22-body'); box.innerHTML = buildStub('p22', 'Связь силы тока и напряжения. Закон Ома для участка электрической цепи', 'Phase 3 Wave 2') + secNavFor('p22') + readButton('p22'); renderMath(box); wireReadBtn('p22'); },
p21: ()=>{ build_p21(); },
p22: ()=>{ build_p22(); },
p23: ()=>{ const box=document.getElementById('p23-body'); box.innerHTML = buildStub('p23', 'Единица сопротивления. Расчёт сопротивления', 'Phase 3 Wave 3') + secNavFor('p23') + readButton('p23'); renderMath(box); wireReadBtn('p23'); },
p24: ()=>{ const box=document.getElementById('p24-body'); box.innerHTML = buildStub('p24', 'Последовательное соединение проводников. Реостат', 'Phase 3 Wave 3') + secNavFor('p24') + readButton('p24'); renderMath(box); wireReadBtn('p24'); },
p25: ()=>{ const box=document.getElementById('p25-body'); box.innerHTML = buildStub('p25', 'Параллельное соединение проводников', 'Phase 3 Wave 3') + secNavFor('p25') + readButton('p25'); renderMath(box); wireReadBtn('p25'); },
@@ -2760,6 +2772,434 @@ function _initP20_tasks(){
render();
}
/* ======================================================================
PHASE 3 · WAVE 2 — §21, §22
====================================================================== */
/* ======== §21 — Эл. цепь. Амперметр и вольтметр ======== */
function build_p21(){
const box = document.getElementById('p21-body');
let h = '';
h += makeCard('theory', 'Элементы цепи', '§ 21.1',
'<p>Простейшая электрическая цепь состоит из:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li><b>источника тока</b> (батарея, генератор);</li>'
+'<li><b>потребителя</b> (лампа, нагреватель, мотор);</li>'
+'<li><b>соединительных проводов</b>;</li>'
+'<li><b>ключа</b> (выключателя), который замыкает/размыкает цепь.</li>'
+'</ul>'
+'<p>На схемах элементы рисуют условными значками — это <b>принципиальная схема</b>.</p>'
);
h += makeCard('rule', 'Амперметр и вольтметр', '§ 21.2',
'<p><b>Амперметр</b> измеряет силу тока. Подключается <b>последовательно</b> с измеряемым участком — чтобы весь ток шёл через него.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Имеет очень маленькое сопротивление, чтобы почти не «съедать» напряжение.</li>'
+'<li>Нельзя включать параллельно — сгорит!</li>'
+'</ul>'
+'<p><b>Вольтметр</b> измеряет напряжение. Подключается <b>параллельно</b> участку — чтобы измерить разность потенциалов на его концах.</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>Имеет очень большое сопротивление, чтобы почти не отбирать ток.</li>'
+'</ul>'
);
h += makeCard('example', 'Простейшая принципиальная схема', '§ 21.3',
'<p>Батарея $\\varepsilon$ — лампа $L$ — амперметр $A$ — ключ $K$ — обратно к батарее. Это всё в одной «петле» — <b>последовательная цепь</b>.</p>'
+'<p>Параллельно лампе подключён вольтметр $V$ — он измеряет напряжение именно на ней.</p>'
);
/* IV1 — конструктор цепи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Простейшая электрическая цепь</div></div>'
+'<div class="wg-help">Включи ключ — увидь, как через лампу пошёл ток. Амперметр и вольтметр показывают свои значения.</div>'
+'<svg id="p21-sim" viewBox="0 0 460 260" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="actions" style="margin-top:10px"><button class="btn primary" id="p21-key">Замкнуть ключ</button><button class="btn" id="p21-reset">Разомкнуть</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Амперметр: <b id="p21-amp">0 А</b></span><span>Вольтметр: <b id="p21-volt">0 В</b></span></div>'
+'</div>';
/* IV2 — викторина «куда включать прибор?» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Как включать прибор?</div></div>'
+'<div class="wg-help">Определи правильное подключение.</div>'
+'<div id="p21-quiz"></div>'
+'<div class="actions"><button class="btn" id="p21-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p21-quiz-r">1</b> / 6</span><span>Правильно: <b id="p21-quiz-ok">0</b></span></div>'
+'</div>';
/* IV3 — DnD «правильно/неправильно» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Что правильно?</div></div>'
+'<div class="wg-help">Распредели утверждения.</div>'
+'<div id="p21-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
+'<div class="drop-box"><h5>Правильно</h5><div class="drop-items" data-cat="t"></div></div>'
+'<div class="drop-box"><h5>Неправильно</h5><div class="drop-items" data-cat="f"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p21-dnd-check">Проверить</button><button class="btn" id="p21-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p21-dnd-fb"></div>'
+'</div>';
/* IV4 — MCQ */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p21-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p21-mcq-i">1</b> / 6</span><span>Правильно: <b id="p21-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p21') + readButton('p21');
renderMath(box);
wireReadBtn('p21');
_initP21_sim();
_initP21_quiz();
_initP21_dnd();
_initP21_mcq();
}
function _initP21_sim(){
const svg = document.getElementById('p21-sim'); if(!svg) return;
let closed = false;
function draw(){
let s = '';
/* схема: батарея слева внизу, по проводам идём: справа от батареи вверх → амперметр → вправо → лампа → вниз → обратно к батарее. Вольтметр параллельно лампе. */
/* батарея */
s += window.PHYS.batteryEMF(80, 200, '4,5 В', 'h');
/* провода */
s += window.PHYS.wire(80, 180, 80, 80); /* левый верт. */
s += window.PHYS.wire(80, 80, 180, 80); /* верх к ам-ру */
s += window.PHYS.wire(220, 80, 280, 80); /* ам-р к лампе */
s += window.PHYS.wire(310, 80, 380, 80); /* после лампы */
s += window.PHYS.wire(380, 80, 380, 200); /* правый верт. */
s += window.PHYS.wire(380, 200, 80, 200);/* низ (через батарею) */
/* амперметр (последовательно) */
s += window.PHYS.ammeterSymbol(200, 80, 18);
/* лампа */
s += window.PHYS.lightbulbSymbol(295, 80, 18);
if(closed){
s += '<circle cx="295" cy="80" r="28" fill="#fde047" opacity="0.45"/>';
s += '<circle cx="295" cy="80" r="40" fill="#fbbf24" opacity="0.18"/>';
}
/* вольтметр (параллельно лампе) */
s += window.PHYS.wire(295, 100, 295, 140);
s += window.PHYS.wire(295, 140, 250, 140);
s += window.PHYS.wire(250, 140, 250, 170);
s += window.PHYS.wire(340, 140, 340, 170);
s += window.PHYS.wire(295, 140, 340, 140);
s += window.PHYS.voltmeterSymbol(295, 155, 18);
/* ключ */
s += '<line x1="160" y1="200" x2="200" y2="200" stroke="#0f172a" stroke-width="2.5"/>';
s += '<circle cx="160" cy="200" r="3" fill="#0f172a"/>';
s += '<circle cx="200" cy="200" r="3" fill="#0f172a"/>';
if(closed){
s += '<line x1="160" y1="200" x2="200" y2="200" stroke="#dc2626" stroke-width="3"/>';
} else {
s += '<line x1="160" y1="200" x2="195" y2="185" stroke="#0f172a" stroke-width="2.5"/>';
}
s += '<text x="180" y="225" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#475569">ключ</text>';
svg.innerHTML = s;
document.getElementById('p21-amp').textContent = closed ? '0,5 А' : '0 А';
document.getElementById('p21-volt').textContent = closed ? '4,5 В' : '0 В';
}
document.getElementById('p21-key').addEventListener('click', ()=>{ closed = !closed; document.getElementById('p21-key').textContent = closed ? 'Разомкнуть' : 'Замкнуть ключ'; draw(); });
document.getElementById('p21-reset').addEventListener('click', ()=>{ closed = false; document.getElementById('p21-key').textContent = 'Замкнуть ключ'; draw(); });
draw();
}
function _initP21_quiz(){
const QS = [
{q:'Как включить <b>амперметр</b> для измерения тока через лампу?', opts:['параллельно лампе','последовательно с лампой','между + и − батареи напрямую','произвольно'], ans:1, why:'Амперметр включается в разрыв цепи (последовательно).'},
{q:'Как включить <b>вольтметр</b> для измерения напряжения на лампе?', opts:['параллельно лампе','последовательно с лампой','до батареи','после ключа'], ans:0, why:'Вольтметр меряет разность потенциалов — параллельно.'},
{q:'У амперметра сопротивление…', opts:['очень малое','очень большое','среднее','равно лампе'], ans:0, why:'Чтобы почти не влиять на ток.'},
{q:'У вольтметра сопротивление…', opts:['очень малое','очень большое','среднее','нулевое'], ans:1, why:'Чтобы не отбирать ток у цепи.'},
{q:'Если подключить амперметр <b>параллельно</b> лампе…', opts:['прибор покажет ток','прибор сгорит — короткое замыкание','напряжение увеличится','ничего не изменится'], ans:1, why:'Через малое R амперметра потечёт огромный ток.'},
{q:'Если подключить вольтметр <b>последовательно</b> с лампой…', opts:['ток будет нормальным','ток будет почти нулевой, лампа не загорится','прибор сгорит','лампа станет ярче'], ans:1, why:'Большое R вольтметра ограничит ток до неощутимого.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p21-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p21-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p21-quiz-r').textContent = (i+1);
document.getElementById('p21-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p21-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p21-quiz'); bumpProgress('p21', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p21-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p21-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP21_dnd(){
const items = [
{id:'a', cat:'t', html:'амперметр последовательно с лампой'},
{id:'b', cat:'t', html:'вольтметр параллельно лампе'},
{id:'c', cat:'t', html:'амперметр имеет малое $R$'},
{id:'d', cat:'t', html:'вольтметр имеет большое $R$'},
{id:'e', cat:'f', html:'амперметр параллельно батарее'},
{id:'f', cat:'f', html:'вольтметр в разрыв цепи'},
{id:'g', cat:'f', html:'амперметр имеет большое $R$'},
{id:'h', cat:'f', html:'вольтметр меряет силу тока'}
];
const dnd = setupSorter({ poolId:'p21-dnd-pool', scopeSelector:'#sec-p21', cats:['t','f'], items, columnLayout:false });
document.getElementById('p21-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p21-dnd-fb');
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='&#10003; Идеально! +15 XP. A последовательно (мал. R), V параллельно (бол. R).'; addXp(15,'p21-dnd'); bumpProgress('p21', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p21-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p21-dnd-fb'); fb.style.display='none'; });
}
function _initP21_mcq(){
const QS = [
{q:'Что такое замкнутая цепь?', opts:['разрыв провода','круговое соединение элементов','один провод','лампа без батареи'], ans:1, why:'Цепь — петля, по которой может пройти ток.'},
{q:'Что произойдёт, если разомкнуть ключ?', opts:['ток усилится','ток исчезнет','напряжение упадёт','ток пойдёт по новому пути'], ans:1, why:'Цепь разорвана — тока нет.'},
{q:'Какой элемент схемы рисуется как круг с буквой A?', opts:['батарея','амперметр','вольтметр','резистор'], ans:1, why:'A — амперметр.'},
{q:'А с буквой V?', opts:['батарея','вольтметр','лампа','ключ'], ans:1, why:'V — вольтметр.'},
{q:'Все потребители в одной «петле» — это…', opts:['последовательное соединение','параллельное','смешанное','короткое замыкание'], ans:0, why:'В одной петле — последовательно.'},
{q:'Почему амперметр НЕ имеет большого $R$?', opts:['чтобы не сгореть','чтобы не уменьшать ток в цепи','чтобы было дёшево','чтобы измерять напряжение'], ans:1, why:'Большое R снизило бы ток, измерение было бы неточным.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p21-mcq'); if(!wrap) return;
let h = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ h += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
h += '</div><div class="feedback" id="p21-mcq-fb"></div><div class="actions"><button class="btn" id="p21-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p21-mcq-i').textContent = (i+1);
document.getElementById('p21-mcq-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p21-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p21-mcq'); bumpProgress('p21', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p21-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p21-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p21-mcq-bonus'); bumpProgress('p21', 15); }, 600); }
});
});
const nb = document.getElementById('p21-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §22 — Закон Ома I = U/R ======== */
function build_p22(){
const box = document.getElementById('p22-body');
let h = '';
h += makeCard('theory', 'Связь $I$ и $U$', '§ 22.1',
'<p>Опыт: подключаем резистор к источнику и меняем напряжение. Что будет с током?</p>'
+'<p>Ток <b>пропорционален</b> напряжению: увеличили $U$ в 2 раза — $I$ тоже в 2 раза. Это и есть <b>закон Ома</b>:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{U}{R}$$</p>'
+'<p>$R$ — <b>сопротивление</b> участка, измеряется в <b>Омах</b> (Ом). Закон открыт немецким физиком Георгом Симоном Омом в 1826 г.</p>'
);
h += makeCard('rule', 'Из закона Ома', '§ 22.2',
'<p>Закон можно «перевернуть» тремя способами:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{U}{R}, \\quad U = IR, \\quad R = \\dfrac{U}{I}$$</p>'
+'<p>Эта связка — <b>основа всей электротехники</b>. Зная любые две величины — найдём третью.</p>'
+'<p><b>1 Ом</b> — это сопротивление участка, на котором при напряжении 1 В идёт ток 1 А.</p>'
);
h += makeCard('example', 'ВАХ — вольт-амперная характеристика', '§ 22.3',
'<p>Если на графике отложить $U$ по горизонтали, а $I$ по вертикали, получится <b>ВАХ</b>:</p>'
+'<ul style="padding-left:20px;margin:6px 0">'
+'<li>для металла — прямая через 0;</li>'
+'<li>наклон прямой определяет $R$: круче — меньше $R$;</li>'
+'<li>для лампы с раскалённой нитью ВАХ слегка изогнута (нить нагревается, $R$ растёт).</li>'
+'</ul>'
);
/* IV1 — ВАХ-плоттер */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">ВАХ-плоттер</div></div>'
+'<div class="wg-help">Меняй $R$ — увидь, как меняется наклон прямой $I(U)$. Чем меньше $R$, тем круче растёт ток.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$R$, Ом: <b id="p22-rv">10</b><input type="range" id="p22-r" min="2" max="50" step="1" value="10"></label>'
+'</div>'
+'<svg id="p22-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'</div>';
/* IV2 — калькулятор */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Калькулятор закона Ома</div></div>'
+'<div class="wg-help">Введи две величины — узнай третью.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$U$, В: <b id="p22-uv">12</b><input type="range" id="p22-u" min="1" max="220" step="1" value="12"></label>'
+'<label>$R$, Ом: <b id="p22-rv2">10</b><input type="range" id="p22-r2" min="1" max="100" step="1" value="10"></label>'
+'</div>'
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$I = U/R$ = <b id="p22-icur">1,2</b> А</span>'
+'<span style="font-size:.84rem;color:var(--muted)">Для $U = 220$ В и $R = 100$ Ом ток будет 2,2 А — это <b id="p22-bulb">средняя лампа накаливания</b>.</span>'
+'</div>'
+'</div>';
/* IV3 — викторина по графику */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Какой $R$ больше?</div></div>'
+'<div class="wg-help">По наклону ВАХ оцени сопротивление.</div>'
+'<div id="p22-quiz"></div>'
+'<div class="actions"><button class="btn" id="p22-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p22-quiz-r">1</b> / 5</span><span>Правильно: <b id="p22-quiz-ok">0</b></span></div>'
+'</div>';
/* IV4 — задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p22-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p22-task-i">1</b> / 6</span><span>Правильно: <b id="p22-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p22') + readButton('p22');
renderMath(box);
wireReadBtn('p22');
_initP22_vah();
_initP22_calc();
_initP22_quiz();
_initP22_tasks();
}
function _initP22_vah(){
const svg = document.getElementById('p22-sim'); if(!svg) return;
function draw(){
const R = +document.getElementById('p22-r').value;
document.getElementById('p22-rv').textContent = R;
const W = 460, H = 240, pad = 38;
const Umax = 12, Imax = 2;
const toX = u => pad + (W-2*pad) * u / Umax;
const toY = i => H - pad - (H-2*pad) * i / Imax;
let s = '';
/* оси */
s += '<line x1="'+pad+'" y1="'+(H-pad)+'" x2="'+(W-pad)+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
s += '<line x1="'+pad+'" y1="'+pad+'" x2="'+pad+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
/* сетка */
for(let u = 2; u <= 10; u+=2){
s += '<line x1="'+toX(u)+'" y1="'+pad+'" x2="'+toX(u)+'" y2="'+(H-pad)+'" stroke="#e5e7eb" stroke-width="1"/>';
s += '<text x="'+toX(u)+'" y="'+(H-pad+14)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">'+u+'</text>';
}
for(let i = 0.5; i <= 1.5; i+=0.5){
s += '<line x1="'+pad+'" y1="'+toY(i)+'" x2="'+(W-pad)+'" y2="'+toY(i)+'" stroke="#e5e7eb" stroke-width="1"/>';
s += '<text x="'+(pad-6)+'" y="'+(toY(i)+3)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">'+i+'</text>';
}
/* подписи осей */
s += '<text x="'+(W-pad+4)+'" y="'+(H-pad+4)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">U, В</text>';
s += '<text x="'+(pad-4)+'" y="'+(pad-6)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="#0f172a">I, А</text>';
/* прямая I = U/R */
const x0 = toX(0), y0 = toY(0);
const uMaxVisible = Math.min(Umax, Imax * R);
const x1 = toX(uMaxVisible);
const y1 = toY(uMaxVisible / R);
s += '<line x1="'+x0+'" y1="'+y0+'" x2="'+x1+'" y2="'+y1+'" stroke="#d97706" stroke-width="3"/>';
/* подпись наклона */
s += '<text x="'+(W/2)+'" y="'+(pad+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I = U / '+R+' Ом</text>';
svg.innerHTML = s;
}
document.getElementById('p22-r').addEventListener('input', draw);
draw();
}
function _initP22_calc(){
function update(){
const U = +document.getElementById('p22-u').value;
const R = +document.getElementById('p22-r2').value;
document.getElementById('p22-uv').textContent = U;
document.getElementById('p22-rv2').textContent = R;
const I = U/R;
document.getElementById('p22-icur').textContent = I.toFixed(2);
let analogy = 'мало';
if(I > 5) analogy = 'мощный нагреватель';
else if(I > 1) analogy = 'средняя лампа накаливания';
else if(I > 0.2) analogy = 'светодиодная лампа';
else analogy = 'светодиод';
document.getElementById('p22-bulb').textContent = analogy;
}
document.getElementById('p22-u').addEventListener('input', update);
document.getElementById('p22-r2').addEventListener('input', update);
update();
}
function _initP22_quiz(){
const QS = [
{q:'У какого резистора $R$ больше: ВАХ круче или ВАХ положе?', opts:['круче','положе','одинаково','зависит от U'], ans:1, why:'$R$ = $U/I$, при том же $U$ положая прямая даёт меньший $I$ → больше $R$.'},
{q:'Если $U$ постоянно, а $R$ удвоить, что станет с $I$?', opts:['удвоится','уменьшится в 2 раза','не изменится','исчезнет'], ans:1, why:'$I = U/R$, $R$ ↑ → $I$ ↓.'},
{q:'Если $R$ постоянно, а $U$ утроить, что с $I$?', opts:['удвоится','утроится','уменьшится в 3 раза','не изменится'], ans:1, why:'Прямая пропорция.'},
{q:'При $U = 6$ В через резистор течёт $I = 0{,}5$ А. Чему равно $R$?', opts:['3 Ом','12 Ом','6 Ом','0,5 Ом'], ans:1, why:'$R = U/I = 6/0{,}5 = 12$ Ом.'},
{q:'Закон Ома справедлив для…', opts:['только газов','любых веществ','металлических проводников','только сверхпроводников'], ans:2, why:'Для металлов точно, для других — с оговорками.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p22-quiz'); if(!wrap) return;
let html = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">';
q.opts.forEach((opt,k)=>{ html += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
html += '</div><div class="feedback" id="p22-quiz-fb"></div>';
wrap.innerHTML = html;
document.getElementById('p22-quiz-r').textContent = (i+1);
document.getElementById('p22-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-k]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
const k = +btn.dataset.k; const fb = document.getElementById('p22-quiz-fb');
if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p22-quiz'); bumpProgress('p22', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p22-quiz-ok').textContent = ok;
renderMath(wrap);
});
});
renderMath(wrap);
}
document.getElementById('p22-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP22_tasks(){
const TASKS = [
{q:'$U = 12$ В, $R = 4$ Ом. Найди $I$ (А).', ans:3, tol:0.05, why:'$I = 12/4 = 3$ А.'},
{q:'$I = 0{,}5$ А, $R = 220$ Ом. Найди $U$ (В).', ans:110, tol:1, why:'$U = IR = 0{,}5 \\cdot 220 = 110$ В.'},
{q:'$U = 220$ В, $I = 2$ А. Найди $R$ (Ом).', ans:110, tol:1, why:'$R = 220/2 = 110$ Ом.'},
{q:'Лампа $R = 240$ Ом подключена к 220 В. Какой ток (А, до сотых)?', ans:0.92, tol:0.02, why:'$I = 220/240 \\approx 0{,}917$ А.'},
{q:'$U$ возросло с 5 до 15 В на том же $R$. Во сколько раз возрос ток?', ans:3, tol:0.05, why:'$I$ пропорционально $U$.'},
{q:'При $U = 1{,}5$ В через светодиод течёт 20 мА. Найди $R$ (Ом).', ans:75, tol:1, why:'$R = 1{,}5/0{,}02 = 75$ Ом.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p22-task'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Задача '+(i+1)+'.</b> '+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p22-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p22-task-go">Ответ</button>'
+'<button class="btn" id="p22-task-hint">Подсказка</button>'
+'<button class="btn" id="p22-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p22-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p22-task-fb"></div>';
document.getElementById('p22-task-i').textContent = (i+1);
document.getElementById('p22-task-ok').textContent = ok;
document.getElementById('p22-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p22-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p22-task-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
done++;
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(4,'p22-task'); bumpProgress('p22', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p22-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p22-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p22-task-bonus'); bumpProgress('p22', 15); }, 600); }
});
document.getElementById('p22-task-hint').addEventListener('click', ()=>{ document.getElementById('p22-task-hint-txt').classList.toggle('show'); });
document.getElementById('p22-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
renderMath(wrap);
}
render();
}
function init(){
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);