feat(phys8 ch2): Phase 3 Wave 1 — §19 источники тока + §20 I=q/t

§19 Электрический ток. Источники:
- 3 теории: условие тока, типы источников, гальв. элемент Вольта
- IV-1: круговая анимация работы батарейки — 14 электронов
  бегут по контуру против тока, лампа загорается
- IV-2: 5 источников «химия/механика/свет/тепло»
- IV-3: DnD «есть ли ток?» 8 ситуаций
- IV-4: 6 MCQ

§20 Сила тока I = q/t:
- 3 теории: формула, направление тока vs электронов, таблица
- IV-1: симуляция потока электронов через провод, slider I 0-3 А,
  лампа меняет яркость, счётчик «Кл и электронов в секунду»
- IV-2: 5 числовых задач q/t/I
- IV-3: DnD 5 устройств по возрастанию I (наушники → стартер)
- IV-4: 5 расчётных задач с допусками

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 23:34:55 +03:00
parent 17a4a1b751
commit 9a123be71c
+500 -6
View File
@@ -358,8 +358,20 @@ const SIDEBARS = {
["Розетка","220 В"],
["1 В","работа 1 Дж на 1 Кл"]
]},
p19:{title:"Шпаргалка § 19",rows:[["В разработке","Phase 3 Wave 1"]]},
p20:{title:"Шпаргалка § 20",rows:[["В разработке","Phase 3 Wave 1"]]},
p19:{title:"Шпаргалка § 19",rows:[
["Ток","упорядоченное движение зарядов"],
["Условие 1","свободные носители"],
["Условие 2","эл. поле от источника"],
["Источники","батарейки, аккумуляторы, генераторы"],
["Внутри источника","сторонние силы $\\to$ ЭДС"]
]},
p20:{title:"Шпаргалка § 20",rows:[
["Формула","$I = q/t$"],
["Единица","[I] = А = Кл/с"],
["1 А","заряд 1 Кл за 1 с"],
["Направление тока","от + к &minus; (исторически)"],
["В металлах","электроны против тока"]
]},
p21:{title:"Шпаргалка § 21",rows:[["В разработке","Phase 3 Wave 2"]]},
p22:{title:"Шпаргалка § 22",rows:[["В разработке","Phase 3 Wave 2"]]},
p23:{title:"Шпаргалка § 23",rows:[["В разработке","Phase 3 Wave 3"]]},
@@ -382,8 +394,8 @@ const TIPS=[
{sec:'p16',html:"Атом — это плотное положительное ядро (протоны+нейтроны) и облако отрицательных электронов вокруг. В обычном атоме число электронов равно числу протонов — он нейтрален. Если потерять электрон → ион +, если получить → ион &minus;."},
{sec:'p17',html:"Заряд не «торкает» другие заряды напрямую — он создаёт вокруг себя <b>электрическое поле</b>, а уже оно действует силой на другие заряды. Линии поля идут от + к &minus; и показывают направление силы на пробный +заряд."},
{sec:'p18',html:"Напряжение $U$ — это работа поля по перемещению единичного заряда: $A = qU$. Единица — 1 Вольт. Розетка 220 В, батарейка 1,5 В. Чем больше $U$, тем «сильнее» поле толкает заряд."},
{sec:'p19',html:"Параграф § 19 будет реализован в Phase 3 Wave 1. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{sec:'p20',html:"Параграф § 20 будет реализован в Phase 3 Wave 1. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
{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:'p23',html:"Параграф § 23 будет реализован в Phase 3 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
@@ -406,8 +418,8 @@ const BUILDERS = {
p16: ()=>{ build_p16(); },
p17: ()=>{ build_p17(); },
p18: ()=>{ build_p18(); },
p19: ()=>{ const box=document.getElementById('p19-body'); box.innerHTML = buildStub('p19', 'Электрический ток. Источники тока', 'Phase 3 Wave 1') + secNavFor('p19') + readButton('p19'); renderMath(box); wireReadBtn('p19'); },
p20: ()=>{ const box=document.getElementById('p20-body'); box.innerHTML = buildStub('p20', 'Сила и направление электрического тока', 'Phase 3 Wave 1') + secNavFor('p20') + readButton('p20'); renderMath(box); wireReadBtn('p20'); },
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'); },
p23: ()=>{ const box=document.getElementById('p23-body'); box.innerHTML = buildStub('p23', 'Единица сопротивления. Расчёт сопротивления', 'Phase 3 Wave 3') + secNavFor('p23') + readButton('p23'); renderMath(box); wireReadBtn('p23'); },
@@ -2266,6 +2278,488 @@ function _initP18_tasks(){
render();
}
/* ======================================================================
PHASE 3 · WAVE 1 — §19, §20
====================================================================== */
/* ======== §19 — Электрический ток. Источники тока ======== */
function build_p19(){
const box = document.getElementById('p19-body');
let h = '';
h += makeCard('theory', 'Что такое электрический ток', '§ 19.1',
'<p><b>Электрический ток</b> — упорядоченное (направленное) движение свободных заряженных частиц.</p>'
+'<p>В проводе у каждой точки много электронов, движущихся хаотически. Если приложить эл. поле — все они «сдвигаются» в одну сторону. Это и есть ток.</p>'
+'<p>Для существования тока нужно:</p>'
+'<ol style="padding-left:20px;margin:6px 0">'
+'<li><b>свободные носители заряда</b> (электроны в металле, ионы в растворе);</li>'
+'<li><b>электрическое поле</b>, заставляющее их двигаться упорядоченно.</li>'
+'</ol>'
);
h += makeCard('rule', 'Источники тока', '§ 19.2',
'<p><b>Источник тока</b> создаёт и поддерживает эл. поле в цепи. Внутри него «<b>сторонние силы</b>» (химические, механические, световые) переносят заряды против поля — от одного электрода к другому.</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>'
+'<li><b>Термоэлемент</b> — тепловая → электрическая.</li>'
+'</ul>'
);
h += makeCard('example', 'Простейшая батарейка', '§ 19.3',
'<p>Два разных металла (например, медь и цинк) опущенные в раствор кислоты, дают электрический ток. Цинк отдаёт электроны в раствор и становится «&minus;». Медь принимает электроны и становится «+».</p>'
+'<p>Так устроен <b>гальванический элемент</b> — изобретение Алессандро Вольта (1799 г.). Это была первая в истории батарейка!</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="p19-sim" viewBox="0 0 460 220" 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="p19-on">Замкнуть цепь</button><button class="btn" id="p19-reset">Сброс</button></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="p19-quiz"></div>'
+'<div class="actions"><button class="btn" id="p19-quiz-next">Следующий</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p19-quiz-r">1</b> / 5</span><span>Правильно: <b id="p19-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="p19-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="y"></div></div>'
+'<div class="drop-box"><h5>Нет тока</h5><div class="drop-items" data-cat="n"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p19-dnd-check">Проверить</button><button class="btn" id="p19-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p19-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="p19-mcq"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p19-mcq-i">1</b> / 6</span><span>Правильно: <b id="p19-mcq-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p19') + readButton('p19');
renderMath(box);
wireReadBtn('p19');
_initP19_sim();
_initP19_quiz();
_initP19_dnd();
_initP19_mcq();
}
function _initP19_sim(){
_killSim('p19sim');
const svg = document.getElementById('p19-sim'); if(!svg) return;
let on = false;
const N = 14;
const electrons = [];
/* Путь: батарея (40,110) → провод вверх → горизонталь → вниз → лампа → возврат */
/* Замкнутый контур по часовой стрелке (электроны двигаются ПРОТИВ тока — против часовой) */
/* Точки контура */
const path = [
{x:80, y:170}, /* − батареи (низ) */
{x:80, y:60}, /* левый верхний угол */
{x:200, y:60}, /* перед лампой */
{x:230, y:90}, /* в лампу */
{x:260, y:60}, /* выход лампы */
{x:380, y:60}, /* правый верхний угол */
{x:380, y:170}, /* + батареи (низ) */
{x:80, y:170} /* возврат через батарею */
];
/* Длина пути */
function pathLen(){
let L = 0;
for(let i=0;i<path.length-1;i++){ L += Math.hypot(path[i+1].x-path[i].x, path[i+1].y-path[i].y); }
return L;
}
const totalLen = pathLen();
for(let i = 0; i < N; i++) electrons.push({ t: i * (totalLen/N) });
function posAt(t){
let rem = ((t % totalLen) + totalLen) % totalLen;
for(let i=0;i<path.length-1;i++){
const seg = Math.hypot(path[i+1].x-path[i].x, path[i+1].y-path[i].y);
if(rem <= seg){
const k = rem/seg;
return { x: path[i].x + (path[i+1].x-path[i].x)*k, y: path[i].y + (path[i+1].y-path[i].y)*k };
}
rem -= seg;
}
return path[0];
}
function tick(){
if(!_isVisible('p19')){ _SIMS.p19sim.raf = requestAnimationFrame(tick); return; }
if(on) for(const e of electrons) e.t += 1.4;
let s = '';
/* провода */
for(let i=0;i<path.length-1;i++){
s += '<line x1="'+path[i].x+'" y1="'+path[i].y+'" x2="'+path[i+1].x+'" y2="'+path[i+1].y+'" stroke="#374151" stroke-width="3"/>';
}
/* батарея слева */
s += '<rect x="40" y="115" width="80" height="60" fill="#e0f2fe" stroke="#0f172a" stroke-width="2" rx="4"/>';
s += '<text x="80" y="135" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#0f172a">источник</text>';
s += '<text x="55" y="170" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">+</text>';
s += '<text x="100" y="170" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#2563eb"></text>';
/* стрелка тока I (внутри цепи) */
if(on){
s += '<text x="230" y="40" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I →</text>';
}
/* лампа в центре */
s += window.PHYS.lightbulbSymbol(230, 90, 18);
if(on){
/* свечение */
s += '<circle cx="230" cy="90" r="28" fill="#fde047" opacity="0.45"/>';
s += '<circle cx="230" cy="90" r="40" fill="#fbbf24" opacity="0.18"/>';
}
/* электроны двигаются против тока — то есть против хода path */
for(const e of electrons){
const p = posAt(-e.t);
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="4.5" fill="#2563eb" stroke="#0f172a" stroke-width="0.6"/>';
s += '<text x="'+p.x.toFixed(1)+'" y="'+(p.y+3).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="9" font-weight="800" fill="#fff">&minus;</text>';
}
svg.innerHTML = s;
_SIMS.p19sim.raf = requestAnimationFrame(tick);
}
_SIMS.p19sim = { raf: 0 };
_SIMS.p19sim.raf = requestAnimationFrame(tick);
document.getElementById('p19-on').addEventListener('click', ()=>{
on = !on;
document.getElementById('p19-on').textContent = on ? 'Разомкнуть' : 'Замкнуть цепь';
});
document.getElementById('p19-reset').addEventListener('click', ()=>{
on = false; document.getElementById('p19-on').textContent = 'Замкнуть цепь';
for(let i=0;i<N;i++) electrons[i].t = i * (totalLen/N);
});
}
function _initP19_quiz(){
const QS = [
{sit:'Гальванический элемент', ans:'C', why:'Химическая (реакция в электролите) → электрическая.'},
{sit:'Гидроэлектростанция (плотина)', ans:'M', why:'Механическая (вращение турбины) → электрическая.'},
{sit:'Солнечная батарея', ans:'L', why:'Световая (фотоны) → электрическая.'},
{sit:'Термопара', ans:'T', why:'Тепловая → электрическая.'},
{sit:'Аккумулятор автомобиля', ans:'C', why:'Химическая, как и батарейка.'}
];
let i = 0, ok = 0;
function render(){
const q = QS[i]; const wrap = document.getElementById('p19-quiz'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin:8px 0;line-height:1.5">'+q.sit+'</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">'
+'<button class="btn" data-pick="C" style="padding:10px"><b>Химическая</b></button>'
+'<button class="btn" data-pick="M" style="padding:10px"><b>Механическая</b></button>'
+'<button class="btn" data-pick="L" style="padding:10px"><b>Световая</b></button>'
+'<button class="btn" data-pick="T" style="padding:10px"><b>Тепловая</b></button>'
+'</div>'
+'<div class="feedback" id="p19-quiz-fb"></div>';
document.getElementById('p19-quiz-r').textContent = (i+1);
document.getElementById('p19-quiz-ok').textContent = ok;
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
btn.addEventListener('click', ()=>{
if(btn.disabled) return; wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
const fb = document.getElementById('p19-quiz-fb');
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(3,'p19-quiz'); bumpProgress('p19', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p19-quiz-ok').textContent = ok;
});
});
}
document.getElementById('p19-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP19_dnd(){
const items = [
{id:'a', cat:'y', html:'медный провод + батарейка + лампа'},
{id:'b', cat:'y', html:'раствор соли + 2 угольных электрода + батарейка'},
{id:'c', cat:'y', html:'аккумулятор автомобиля при стартере'},
{id:'d', cat:'y', html:'солнечная панель в светлый день'},
{id:'e', cat:'n', html:'стеклянный стакан + батарейка'},
{id:'f', cat:'n', html:'разомкнутый ключ + батарейка'},
{id:'g', cat:'n', html:'сухое дерево + батарейка'},
{id:'h', cat:'n', html:'медный провод без источника'}
];
const dnd = setupSorter({ poolId:'p19-dnd-pool', scopeSelector:'#sec-p19', cats:['y','n'], items, columnLayout:false });
document.getElementById('p19-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p19-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. Нужны: проводник + источник + замкнутая цепь.'; addXp(15,'p19-dnd'); bumpProgress('p19', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'. Без источника или с разомкнутой цепью тока нет.'; }
});
document.getElementById('p19-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p19-dnd-fb'); fb.style.display='none'; });
}
function _initP19_mcq(){
const QS = [
{q:'Что такое электрический ток?', opts:['хаос. движение','упорядоченное движение зарядов','нагрев тела','сила поля'], ans:1, why:'Ток = направленное движение свободных зарядов.'},
{q:'Что нужно для тока?', opts:['только поле','только проводник','свободные носители + поле','магнит'], ans:2, why:'Носители + поле.'},
{q:'В батарейке энергия…', opts:['световая → эл.','химическая → эл.','механическая → эл.','тепловая → эл.'], ans:1, why:'Химическая реакция переносит заряды.'},
{q:'Зачем нужен источник тока в цепи?', opts:['греть провод','создавать и поддерживать поле','охлаждать','тратить заряд'], ans:1, why:'Источник поддерживает разность потенциалов.'},
{q:'Что переносит заряды внутри источника против поля?', opts:['внешние силы','сторонние силы','гравитация','магнетизм'], ans:1, why:'Это и есть «сторонние силы».'},
{q:'Куда «бегут» свободные электроны в цепи (от куда к куда)?', opts:['от + к &minus;','от &minus; к +','туда-сюда','стоят на месте'], ans:1, why:'Электроны двигаются от &minus; к + по внешней цепи (против тока).'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const q = QS[i]; const wrap = document.getElementById('p19-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="p19-mcq-fb"></div><div class="actions"><button class="btn" id="p19-mcq-next">Следующий</button></div>';
wrap.innerHTML = h;
document.getElementById('p19-mcq-i').textContent = (i+1);
document.getElementById('p19-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('p19-mcq-fb');
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно. '+q.why; addXp(2,'p19-mcq'); bumpProgress('p19', 3); }
else { done++; fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. '+q.why; }
document.getElementById('p19-mcq-ok').textContent = ok;
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p19-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — тренажёр пройден.'; addXp(15,'p19-mcq-bonus'); bumpProgress('p19', 15); }, 600); }
});
});
const nb = document.getElementById('p19-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
}
render();
}
/* ======== §20 — Сила и направление тока ======== */
function build_p20(){
const box = document.getElementById('p20-body');
let h = '';
h += makeCard('theory', 'Сила тока', '§ 20.1',
'<p><b>Сила тока</b> $I$ показывает, сколько заряда проходит через поперечное сечение проводника за единицу времени:</p>'
+'<p style="text-align:center;margin:8px 0">$$I = \\dfrac{q}{t}$$</p>'
+'<p>Единица измерения — <b>Ампер (А)</b>. <b>1 А</b> — это $1$ Кл, проходящий за $1$ с (это очень много электронов: $\\approx 6\\cdot 10^{18}$ за секунду!).</p>'
+'<p>Названа в честь французского физика Андре-Мари Ампера.</p>'
);
h += makeCard('rule', 'Направление тока', '§ 20.2',
'<p>Исторически за направление тока приняли направление движения <b>положительных зарядов</b> — от «+» к «&minus;» во внешней цепи.</p>'
+'<p>Однако в металлах реально двигаются <b>электроны</b> — от «&minus;» к «+», то есть <b>против</b> направления тока.</p>'
+'<p>Это «договорённость» — она сформировалась до открытия электрона. Менять её уже поздно.</p>'
);
h += makeCard('example', 'Типичные токи', '§ 20.3',
'<table style="width:100%;border-collapse:collapse;font-size:.92rem"><tbody>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">наушники</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~1 мА</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">светодиод</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~10 мА</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">лампочка карманного фонаря</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~0,3 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">лампа 60 Вт в розетке</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~0,27 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">чайник электрический</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~10 А</code></td></tr>'
+'<tr><td style="padding:5px;border-bottom:1px dashed var(--border)">стартер автомобиля</td><td style="padding:5px;text-align:right;border-bottom:1px dashed var(--border)"><code>~200 А</code></td></tr>'
+'<tr><td style="padding:5px">молния</td><td style="padding:5px;text-align:right"><code>$\\sim 10^4 - 10^5$ А</code></td></tr>'
+'</tbody></table>'
);
/* 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">Меняй $I$ — увидь, как меняется скорость потока электронов в проводе. Лампа загорается сильнее при большем токе.</div>'
+'<div class="sliders" style="margin-bottom:10px">'
+'<label>$I$, А: <b id="p20-iv">0.3</b><input type="range" id="p20-i" min="0" max="3" step="0.1" value="0.3"></label>'
+'</div>'
+'<svg id="p20-sim" viewBox="0 0 460 180" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
+'<div class="score-display" style="margin-top:10px"><span>За 1 с пройдёт <b id="p20-q1">0.3</b> Кл</span><span>Это <b id="p20-ne">1.9&times;10<sup>18</sup></b> электронов</span></div>'
+'</div>';
/* IV2 — викторина «по формуле» */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Найди по формуле $I = q/t$</div></div>'
+'<div class="wg-help">Числовые вопросы.</div>'
+'<div id="p20-quiz"></div>'
+'<div class="actions"><button class="btn" id="p20-quiz-next">Следующая</button></div>'
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p20-quiz-r">1</b> / 5</span><span>Правильно: <b id="p20-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="p20-dnd-pool"></div>'
+'<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-top:10px">'
+'<div class="drop-box"><h5>1. Меньше</h5><div class="drop-items" data-cat="r1"></div></div>'
+'<div class="drop-box"><h5>2</h5><div class="drop-items" data-cat="r2"></div></div>'
+'<div class="drop-box"><h5>3</h5><div class="drop-items" data-cat="r3"></div></div>'
+'<div class="drop-box"><h5>4</h5><div class="drop-items" data-cat="r4"></div></div>'
+'<div class="drop-box"><h5>5. Больше</h5><div class="drop-items" data-cat="r5"></div></div>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="p20-dnd-check">Проверить</button><button class="btn" id="p20-dnd-reset">Сброс</button></div>'
+'<div class="feedback" id="p20-dnd-fb"></div>'
+'</div>';
/* IV4 — расчётные задачи */
h += '<div class="wg">'
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 5 задач</div></div>'
+'<div class="wg-help">4+ — +15 XP.</div>'
+'<div id="p20-task"></div>'
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p20-task-i">1</b> / 5</span><span>Правильно: <b id="p20-task-ok">0</b></span></div>'
+'</div>';
box.innerHTML = h + secNavFor('p20') + readButton('p20');
renderMath(box);
wireReadBtn('p20');
_initP20_sim();
_initP20_quiz();
_initP20_dnd();
_initP20_tasks();
}
function _initP20_sim(){
_killSim('p20sim');
const svg = document.getElementById('p20-sim'); if(!svg) return;
const N = 22;
const electrons = [];
for(let i=0;i<N;i++) electrons.push({ x: 60 + i*16, y: 90 });
let I = 0.3;
function tick(){
if(!_isVisible('p20')){ _SIMS.p20sim.raf = requestAnimationFrame(tick); return; }
/* скорость пропорциональна I */
const v = I * 1.5;
for(const e of electrons){
e.x -= v; /* электроны двигаются справа налево (против тока, которое идёт слева направо) */
if(e.x < 60) e.x += (400 - 60);
}
let s = '';
/* провод */
s += '<rect x="60" y="80" width="340" height="20" fill="#cbd5e1" stroke="#0f172a" stroke-width="2" rx="3"/>';
/* стрелки тока I (направление + → −, слева направо для удобства) */
if(I > 0){
s += '<text x="230" y="60" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#d97706">I →</text>';
s += window.PHYS.drawArrow(190, 70, 270, 70, '#d97706', 2.2, 9);
}
/* электроны */
for(const e of electrons){
s += '<circle cx="'+e.x.toFixed(1)+'" cy="'+e.y+'" r="4" fill="#2563eb" stroke="#0f172a" stroke-width="0.5"/>';
}
/* подпись о направлении электронов */
if(I > 0){
s += '<text x="230" y="135" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#2563eb">электроны ← (против тока)</text>';
}
/* лампа справа */
s += window.PHYS.lightbulbSymbol(420, 90, 16);
/* свечение зависит от I */
if(I > 0){
const glow = Math.min(1, I/2);
s += '<circle cx="420" cy="90" r="'+(20+I*10).toFixed(0)+'" fill="#fde047" opacity="'+(glow*0.4).toFixed(2)+'"/>';
s += '<circle cx="420" cy="90" r="'+(34+I*15).toFixed(0)+'" fill="#fbbf24" opacity="'+(glow*0.15).toFixed(2)+'"/>';
}
svg.innerHTML = s;
_SIMS.p20sim.raf = requestAnimationFrame(tick);
}
_SIMS.p20sim = { raf: 0 };
_SIMS.p20sim.raf = requestAnimationFrame(tick);
function update(){
I = +document.getElementById('p20-i').value;
document.getElementById('p20-iv').textContent = I.toFixed(1);
document.getElementById('p20-q1').textContent = I.toFixed(1);
const Ne = I / E_CHARGE;
document.getElementById('p20-ne').innerHTML = (Ne/1e18).toFixed(2)+'&times;10<sup>18</sup>';
}
document.getElementById('p20-i').addEventListener('input', update);
update();
}
function _initP20_quiz(){
const QS = [
{q:'$q = 6$ Кл прошло за $t = 2$ с. Найди $I$ (А).', ans:3, tol:0.05, why:'$I = 6/2 = 3$ А.'},
{q:'$I = 0{,}5$ А, $t = 10$ с. Найди $q$ (Кл).', ans:5, tol:0.05, why:'$q = It = 0{,}5 \\cdot 10 = 5$ Кл.'},
{q:'$q = 60$ Кл, $I = 2$ А. Найди $t$ (с).', ans:30, tol:0.5, why:'$t = q/I = 60/2 = 30$ с.'},
{q:'За 1 минуту прошёл заряд $q = 30$ Кл. Найди $I$ (А).', ans:0.5, tol:0.05, why:'$I = 30/60 = 0{,}5$ А.'},
{q:'$I = 0{,}1$ А течёт 5 мин. Какой заряд (Кл)?', ans:30, tol:0.5, why:'$t = 300$ с, $q = 0{,}1 \\cdot 300 = 30$ Кл.'}
];
let i = 0, ok = 0;
function render(){
const t = QS[i]; const wrap = document.getElementById('p20-quiz'); if(!wrap) return;
wrap.innerHTML =
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin:8px 0;line-height:1.5">'+t.q+'</div>'
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p20-quiz-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p20-quiz-go">Ответ</button></div>'
+'<div class="feedback" id="p20-quiz-fb"></div>';
document.getElementById('p20-quiz-r').textContent = (i+1);
document.getElementById('p20-quiz-ok').textContent = ok;
document.getElementById('p20-quiz-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p20-quiz-inp').value || '').replace(',','.'));
const fb = document.getElementById('p20-quiz-fb');
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='&#10003; Верно! '+t.why; addXp(3,'p20-quiz'); bumpProgress('p20', 4); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p20-quiz-ok').textContent = ok;
renderMath(wrap);
});
renderMath(wrap);
}
document.getElementById('p20-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
render();
}
function _initP20_dnd(){
const items = [
{id:'h', cat:'r1', html:'наушники ($1$ мА)'},
{id:'l', cat:'r2', html:'светодиод ($10$ мА)'},
{id:'f', cat:'r3', html:'лампа в розетке ($0{,}3$ А)'},
{id:'k', cat:'r4', html:'чайник ($10$ А)'},
{id:'s', cat:'r5', html:'стартер ($200$ А)'}
];
const dnd = setupSorter({ poolId:'p20-dnd-pool', scopeSelector:'#sec-p20', cats:['r1','r2','r3','r4','r5'], items, columnLayout:false });
document.getElementById('p20-dnd-check').addEventListener('click', ()=>{
const fb = document.getElementById('p20-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. Бытовые токи — от миллиампер до 100+ А.'; addXp(15,'p20-dnd'); bumpProgress('p20', 20); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Ошибок: '+wrong+'.'; }
});
document.getElementById('p20-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p20-dnd-fb'); fb.style.display='none'; });
}
function _initP20_tasks(){
const TASKS = [
{q:'Через лампу за 0,5 с прошёл заряд 0,15 Кл. Найди ток (А).', ans:0.3, tol:0.02, why:'$I = q/t = 0{,}15/0{,}5 = 0{,}3$ А.'},
{q:'$I = 2$ А течёт 10 минут. Сколько Кл пройдёт?', ans:1200, tol:10, why:'$t = 600$ с, $q = 2 \\cdot 600 = 1200$ Кл.'},
{q:'Сколько электронов проходит через сечение провода за 1 с при $I = 1$ А? Ответ в формате $a \\cdot 10^{18}$, введи $a$.', ans:6.25, tol:0.05, why:'$N = I \\cdot 1/e = 1/(1{,}6\\cdot10^{-19}) = 6{,}25 \\cdot 10^{18}$.'},
{q:'Через спираль чайника за 30 секунд прошло 300 Кл. Найди ток (А).', ans:10, tol:0.1, why:'$I = 300/30 = 10$ А.'},
{q:'Светодиод работает на токе $I = 20$ мА = $0{,}02$ А. Сколько Кл пройдёт за час?', ans:72, tol:1, why:'$t = 3600$ с, $q = 0{,}02 \\cdot 3600 = 72$ Кл.'}
];
let i = 0, ok = 0, done = 0, awarded = false;
function render(){
const t = TASKS[i]; const wrap = document.getElementById('p20-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="p20-task-inp" placeholder="число" style="width:140px">'
+'<button class="btn primary" id="p20-task-go">Ответ</button>'
+'<button class="btn" id="p20-task-hint">Подсказка</button>'
+'<button class="btn" id="p20-task-next">Следующая</button></div>'
+'<div class="boss-hint-txt" id="p20-task-hint-txt">'+t.why+'</div>'
+'<div class="feedback" id="p20-task-fb"></div>';
document.getElementById('p20-task-i').textContent = (i+1);
document.getElementById('p20-task-ok').textContent = ok;
document.getElementById('p20-task-go').addEventListener('click', ()=>{
const v = parseFloat((document.getElementById('p20-task-inp').value || '').replace(',','.'));
const fb = document.getElementById('p20-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,'p20-task'); bumpProgress('p20', 6); }
else { fb.className='feedback fail'; fb.innerHTML='&#10007; Не то. Ответ: '+t.ans+'. '+t.why; }
document.getElementById('p20-task-ok').textContent = ok;
renderMath(wrap);
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p20-task-fb'); wf.className='feedback ok'; wf.innerHTML='&#10003; +15 XP — расчёты сданы.'; addXp(15,'p20-task-bonus'); bumpProgress('p20', 15); }, 600); }
});
document.getElementById('p20-task-hint').addEventListener('click', ()=>{ document.getElementById('p20-task-hint-txt').classList.toggle('show'); });
document.getElementById('p20-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);