feat(phys8 ch1): Phase 1 Wave 2 — §3 теплопроводность + §4 конвекция + §5 излучение
§3 Теплопроводность: - Главный визуал: симуляция стержня через PHYS.createHeatBar — slider'ы T_горячий, T_холодный, α (от шерсти до серебра), 1D-уравнение тепла - Викторина «лучший проводник»: 6 пар материалов - DnD: 8 материалов на 2 категории (хорошие/плохие) - MCQ 6 вопросов §4 Конвекция: - Симуляция тороидального потока: 30 частиц в сосуде, нагреватель снизу, тёплые поднимаются по центру, холодные опускаются по краям, цвет по tempColor - Викторина «возможна ли конвекция?» с 6 ситуациями - DnD: 8 ситуаций (возможна/невозможна) - MCQ 6 вопросов §5 Излучение: - Симуляция «Солнце греет чёрную и белую пластины»: лучи к чёрной поглощаются, от белой отражаются; температуры растут с разной скоростью (чёрная до 75°C, белая до 35°C) - True/False квикфайр (7 утверждений) - DnD: 9 примеров на 3 вида теплопередачи (главный синтез главы) - MCQ 6 вопросов Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -283,9 +283,24 @@ const SIDEBARS = {
|
||||
["Теплопередача","проводность, конвекция, излучение"],
|
||||
["Знак","нагрев $\\Rightarrow$ $\\Delta U > 0$; охлаждение $\\Rightarrow$ $\\Delta U < 0$"]
|
||||
]},
|
||||
p3:{title:"Шпаргалка § 3",rows:[["В разработке","Phase 1 Wave 2"]]},
|
||||
p4:{title:"Шпаргалка § 4",rows:[["В разработке","Phase 1 Wave 2"]]},
|
||||
p5:{title:"Шпаргалка § 5",rows:[["В разработке","Phase 1 Wave 2"]]},
|
||||
p3:{title:"Шпаргалка § 3",rows:[
|
||||
["Теплопроводность","передача $U$ без переноса вещества"],
|
||||
["Хорошие","металлы (медь, серебро, алюминий)"],
|
||||
["Плохие","дерево, пластик, воздух, шерсть, вакуум"],
|
||||
["В газах и жидкостях","низкая (исключение — ртуть)"]
|
||||
]},
|
||||
p4:{title:"Шпаргалка § 4",rows:[
|
||||
["Конвекция","перенос $U$ потоками жидкости / газа"],
|
||||
["Механизм","тёплое $\\uparrow$, холодное $\\downarrow$"],
|
||||
["В твёрдых телах","невозможна"],
|
||||
["Примеры","батарея, ветер, кипение"]
|
||||
]},
|
||||
p5:{title:"Шпаргалка § 5",rows:[
|
||||
["Излучение","перенос $U$ электромагнитными волнами"],
|
||||
["Среда","не нужна — идёт через вакуум"],
|
||||
["Излучают всё","чем горячее, тем сильнее"],
|
||||
["Тёмные тела","поглощают и излучают сильнее светлых"]
|
||||
]},
|
||||
p6:{title:"Шпаргалка § 6",rows:[["В разработке","Phase 1 Wave 3"]]},
|
||||
p7:{title:"Шпаргалка § 7",rows:[["В разработке","Phase 1 Wave 3"]]},
|
||||
p8:{title:"Шпаргалка § 8",rows:[["В разработке","Phase 1 Wave 4"]]},
|
||||
@@ -298,9 +313,9 @@ const SIDEBARS = {
|
||||
const TIPS=[
|
||||
{sec:'p1',html:"Тело состоит из молекул. Они движутся (есть $E_k$) и взаимодействуют (есть $E_p$). Их сумма — внутренняя энергия $U$. Главное: $U$ не зависит от того, движется ли тело и на какой высоте оно лежит."},
|
||||
{sec:'p2',html:"Изменить $U$ можно двумя способами: совершить работу (трение, сжатие, удар) или передать тепло без работы (контакт, поток, излучение). Чай в стакане остывает — это теплопередача. Спички в коробке нагреваются от тряски — это работа."},
|
||||
{sec:'p3',html:"Параграф § 3 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p4',html:"Параграф § 4 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p5',html:"Параграф § 5 будет реализован в Phase 1 Wave 2. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p3',html:"Один конец металлического стержня в огне — другой нагревается, хотя ничто не движется. Это <b>теплопроводность</b>: молекулы передают энергию соседям. У металлов это работает быстро, у дерева — медленно."},
|
||||
{sec:'p4',html:"Тёплый воздух легче холодного и поднимается вверх. Так батарея греет всю комнату — это <b>конвекция</b>. В твёрдых телах конвекции нет, потому что молекулы не могут свободно двигаться."},
|
||||
{sec:'p5',html:"Солнце греет Землю через космический вакуум — теплопроводность и конвекция тут невозможны. Это <b>излучение</b> электромагнитными волнами. Чёрная футболка в жаркий день нагревается сильнее белой."},
|
||||
{sec:'p6',html:"Параграф § 6 будет реализован в Phase 1 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p7',html:"Параграф § 7 будет реализован в Phase 1 Wave 3. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p8',html:"Параграф § 8 будет реализован в Phase 1 Wave 4. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
@@ -313,9 +328,9 @@ const TIPS=[
|
||||
const BUILDERS = {
|
||||
p1: ()=>{ build_p1(); },
|
||||
p2: ()=>{ build_p2(); },
|
||||
p3: ()=>{ const box=document.getElementById('p3-body'); box.innerHTML = buildStub('p3', 'Теплопроводность', 'Phase 1 Wave 2') + secNavFor('p3') + readButton('p3'); renderMath(box); wireReadBtn('p3'); },
|
||||
p4: ()=>{ const box=document.getElementById('p4-body'); box.innerHTML = buildStub('p4', 'Конвекция', 'Phase 1 Wave 2') + secNavFor('p4') + readButton('p4'); renderMath(box); wireReadBtn('p4'); },
|
||||
p5: ()=>{ const box=document.getElementById('p5-body'); box.innerHTML = buildStub('p5', 'Излучение', 'Phase 1 Wave 2') + secNavFor('p5') + readButton('p5'); renderMath(box); wireReadBtn('p5'); },
|
||||
p3: ()=>{ build_p3(); },
|
||||
p4: ()=>{ build_p4(); },
|
||||
p5: ()=>{ build_p5(); },
|
||||
p6: ()=>{ const box=document.getElementById('p6-body'); box.innerHTML = buildStub('p6', 'Расчёт количества теплоты при нагревании и охлаждении. Удельная теплоёмкость', 'Phase 1 Wave 3') + secNavFor('p6') + readButton('p6'); renderMath(box); wireReadBtn('p6'); },
|
||||
p7: ()=>{ const box=document.getElementById('p7-body'); box.innerHTML = buildStub('p7', 'Горение. Удельная теплота сгорания топлива', 'Phase 1 Wave 3') + secNavFor('p7') + readButton('p7'); renderMath(box); wireReadBtn('p7'); },
|
||||
p8: ()=>{ const box=document.getElementById('p8-body'); box.innerHTML = buildStub('p8', 'Плавление и кристаллизация', 'Phase 1 Wave 4') + secNavFor('p8') + readButton('p8'); renderMath(box); wireReadBtn('p8'); },
|
||||
@@ -1297,6 +1312,724 @@ function _initP2_mcq(){
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
PHASE 1 · WAVE 2 — §3, §4, §5
|
||||
====================================================================== */
|
||||
|
||||
/* ======== §3 — Теплопроводность ======== */
|
||||
function build_p3(){
|
||||
const box = document.getElementById('p3-body');
|
||||
let h = '';
|
||||
|
||||
h += makeCard('theory', 'Что такое теплопроводность', '§ 3.1',
|
||||
'<p><b>Теплопроводность</b> — передача внутренней энергии от более горячих частей тела к более холодным <b>без переноса вещества</b>.</p>'
|
||||
+'<p>Механизм: быстрые молекулы (в горячей части) сталкиваются с медленными (в холодной) и передают им часть своей $E_k$. Так энергия путешествует через тело молекула за молекулой.</p>'
|
||||
+'<p>Само вещество при этом остаётся на месте — двигается только энергия.</p>'
|
||||
);
|
||||
h += makeCard('rule', 'Хорошие и плохие проводники', '§ 3.2',
|
||||
'<p><b>Хорошие проводники</b> тепла:</p>'
|
||||
+'<ul style="padding-left:20px;margin:4px 0"><li>металлы: серебро, медь, алюминий, железо;</li><li>в них свободные электроны быстро переносят энергию.</li></ul>'
|
||||
+'<p style="margin-top:8px"><b>Плохие проводники</b> (тепловые изоляторы):</p>'
|
||||
+'<ul style="padding-left:20px;margin:4px 0"><li>дерево, стекло, кирпич, пластик;</li><li>шерсть, мех, пух, вата (они «держат» воздух между волокнами);</li><li>сами газы и жидкости (кроме ртути);</li><li><b>вакуум</b> — идеальный изолятор: нет частиц $\\Rightarrow$ нечем передавать.</li></ul>'
|
||||
);
|
||||
h += makeCard('example', 'Зачем это нам', '§ 3.3',
|
||||
'<ul style="padding-left:20px;margin:4px 0">'
|
||||
+'<li>Ручка сковороды — из дерева или пластика, чтобы не обжечь руку.</li>'
|
||||
+'<li>Шуба греет не сама — между волосками много воздуха.</li>'
|
||||
+'<li>Стеклопакет: два стекла + воздух между ними плохо проводят тепло.</li>'
|
||||
+'<li>В термосе между двумя стенками — вакуум (минимум теплопроводности).</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">Один конец стержня — горячий (<b>$T_{гор}$</b>), другой — холодный (<b>$T_{хол}$</b>). Тепло перетекает с горячего конца на холодный. Цвет показывает температуру вдоль стержня. Меняй $\\alpha$ — коэффициент теплопроводности.</div>'
|
||||
+'<div class="sliders" style="margin-bottom:10px">'
|
||||
+'<label>$T_{гор}$, °C: <b id="p3-th">200</b><input type="range" id="p3-thr" min="50" max="500" step="10" value="200"></label>'
|
||||
+'<label>$T_{хол}$, °C: <b id="p3-tc">0</b><input type="range" id="p3-tcr" min="-50" max="100" step="5" value="0"></label>'
|
||||
+'<label>$\\alpha$ (проводимость): <b id="p3-av">медь</b><input type="range" id="p3-ar" min="1" max="100" step="1" value="50"></label>'
|
||||
+'</div>'
|
||||
+'<svg id="p3-sim" viewBox="0 0 460 80" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
|
||||
+'<div class="actions" style="margin-top:8px"><button class="btn" id="p3-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="p3-quiz"></div>'
|
||||
+'<div class="actions"><button class="btn" id="p3-quiz-next">Следующий раунд</button></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p3-quiz-r">1</b> / 6</span><span>Правильно: <b id="p3-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="p3-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="good"></div></div>'
|
||||
+'<div class="drop-box"><h5>Плохие проводники</h5><div class="drop-items" data-cat="bad"></div></div>'
|
||||
+'</div>'
|
||||
+'<div class="actions"><button class="btn primary" id="p3-dnd-check">Проверить</button><button class="btn" id="p3-dnd-reset">Сброс</button></div>'
|
||||
+'<div class="feedback" id="p3-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="p3-mcq"></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p3-mcq-i">1</b> / 6</span><span>Правильно: <b id="p3-mcq-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('p3') + readButton('p3');
|
||||
renderMath(box);
|
||||
wireReadBtn('p3');
|
||||
|
||||
_initP3_sim();
|
||||
_initP3_quiz();
|
||||
_initP3_dnd();
|
||||
_initP3_mcq();
|
||||
}
|
||||
|
||||
function _initP3_sim(){
|
||||
_killSim('p3sim');
|
||||
const svg = document.getElementById('p3-sim'); if(!svg) return;
|
||||
const W=460, H=80, padX=24, barY=20, barH=44;
|
||||
let bar = window.PHYS.createHeatBar({ N: 40, tHot: 200, tCold: 0, alpha: 0.5 });
|
||||
const MAT_LABELS = ['вакуум','шерсть','дерево','стекло','вода','свинец','железо','алюминий','медь','серебро'];
|
||||
function alphaFromSlider(v){
|
||||
/* v in 1..100, exponential mapping to 0.02..0.95 */
|
||||
return 0.02 + (v/100) * 0.93;
|
||||
}
|
||||
function matLabel(v){
|
||||
const idx = Math.min(MAT_LABELS.length-1, Math.floor(v/10));
|
||||
return MAT_LABELS[idx];
|
||||
}
|
||||
let lastT = performance.now();
|
||||
function tick(now){
|
||||
if(!_isVisible('p3')){ _SIMS.p3sim.raf = requestAnimationFrame(tick); return; }
|
||||
const dt = Math.min(50, now - lastT); lastT = now;
|
||||
bar.step(dt * 0.06);
|
||||
/* render */
|
||||
let s = bar.render(padX, barY, W - 2*padX, barH);
|
||||
/* подписи концов */
|
||||
s += '<text x="'+(padX-4)+'" y="'+(barY-4)+'" text-anchor="start" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#dc2626">горячий</text>';
|
||||
s += '<text x="'+(W-padX+4)+'" y="'+(barY-4)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#2563eb">холодный</text>';
|
||||
svg.innerHTML = s;
|
||||
_SIMS.p3sim.raf = requestAnimationFrame(tick);
|
||||
}
|
||||
_SIMS.p3sim = { raf: 0 };
|
||||
_SIMS.p3sim.raf = requestAnimationFrame(tick);
|
||||
|
||||
function applyAll(){
|
||||
const th = +document.getElementById('p3-thr').value;
|
||||
const tc = +document.getElementById('p3-tcr').value;
|
||||
const av = +document.getElementById('p3-ar').value;
|
||||
document.getElementById('p3-th').textContent = th;
|
||||
document.getElementById('p3-tc').textContent = tc;
|
||||
document.getElementById('p3-av').textContent = matLabel(av);
|
||||
bar.setTHot(th); bar.setTCold(tc); bar.alpha = alphaFromSlider(av);
|
||||
}
|
||||
['p3-thr','p3-tcr','p3-ar'].forEach(id => document.getElementById(id).addEventListener('input', applyAll));
|
||||
document.getElementById('p3-reset').addEventListener('click', ()=>{
|
||||
bar = window.PHYS.createHeatBar({ N: 40, tHot: +document.getElementById('p3-thr').value, tCold: +document.getElementById('p3-tcr').value, alpha: alphaFromSlider(+document.getElementById('p3-ar').value) });
|
||||
});
|
||||
applyAll();
|
||||
}
|
||||
|
||||
function _initP3_quiz(){
|
||||
const QS = [
|
||||
{A:'медная ложка', B:'деревянная ложка', ans:'A', why:'Медь — отличный проводник, дерево — изолятор.'},
|
||||
{A:'железная дверная ручка', B:'пластиковая дверная ручка', ans:'A', why:'Железо проводит тепло гораздо лучше пластика, поэтому железная ручка зимой кажется холоднее.'},
|
||||
{A:'воздух', B:'вода', ans:'B', why:'Жидкости проводят тепло лучше газов, хотя обе плохо.'},
|
||||
{A:'стекло', B:'серебро', ans:'B', why:'Серебро — лучший проводник тепла среди обычных металлов.'},
|
||||
{A:'шерстяной свитер', B:'хлопковая футболка', ans:'B', why:'Хлопок проводит тепло немного лучше шерсти; шерсть лучше задерживает воздух и потому теплее (но проводит хуже).'},
|
||||
{A:'вакуум', B:'воздух', ans:'B', why:'В вакууме нет частиц, поэтому теплопроводность нулевая. Воздух хоть и плохой, но проводник.'}
|
||||
];
|
||||
let i = 0, ok = 0;
|
||||
function render(){
|
||||
const q = QS[i];
|
||||
const wrap = document.getElementById('p3-quiz');
|
||||
if(!wrap) return;
|
||||
wrap.innerHTML =
|
||||
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">'
|
||||
+'<button class="btn" data-pick="A" style="padding:14px;text-align:left"><b>A.</b> '+q.A+'</button>'
|
||||
+'<button class="btn" data-pick="B" style="padding:14px;text-align:left"><b>B.</b> '+q.B+'</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="p3-quiz-fb"></div>';
|
||||
document.getElementById('p3-quiz-r').textContent = (i+1);
|
||||
document.getElementById('p3-quiz-ok').textContent = ok;
|
||||
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
|
||||
btn.addEventListener('click', ()=>{
|
||||
if(btn.disabled) return;
|
||||
const pick = btn.dataset.pick;
|
||||
wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
|
||||
const fb = document.getElementById('p3-quiz-fb');
|
||||
if(pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(3,'p3-quiz'); bumpProgress('p3', 4); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p3-quiz-ok').textContent = ok;
|
||||
});
|
||||
});
|
||||
}
|
||||
document.getElementById('p3-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
render();
|
||||
}
|
||||
|
||||
function _initP3_dnd(){
|
||||
const items = [
|
||||
{id:'cu', cat:'good', html:'медь'},
|
||||
{id:'ag', cat:'good', html:'серебро'},
|
||||
{id:'al', cat:'good', html:'алюминий'},
|
||||
{id:'fe', cat:'good', html:'железо'},
|
||||
{id:'wo', cat:'bad', html:'дерево'},
|
||||
{id:'pl', cat:'bad', html:'пластик'},
|
||||
{id:'sh', cat:'bad', html:'шерсть'},
|
||||
{id:'va', cat:'bad', html:'вакуум'}
|
||||
];
|
||||
const dnd = setupSorter({ poolId:'p3-dnd-pool', scopeSelector:'#sec-p3', cats:['good','bad'], items, columnLayout:false });
|
||||
document.getElementById('p3-dnd-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p3-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='✓ Идеально! +15 XP. Металлы — отличные проводники, остальное — плохие.'; addXp(15,'p3-dnd'); bumpProgress('p3', 20); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'. Подсказка: металлы — это хорошие проводники.'; }
|
||||
});
|
||||
document.getElementById('p3-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p3-dnd-fb'); fb.style.display='none'; });
|
||||
}
|
||||
|
||||
function _initP3_mcq(){
|
||||
const QS = [
|
||||
{q:'Что происходит при теплопроводности?', opts:['Переносится вещество и энергия','Переносится только энергия','Переносится только вещество','Тело меняет форму'], ans:1, why:'Молекулы передают $E_k$ соседям, оставаясь на месте.'},
|
||||
{q:'В каком веществе теплопроводность нулевая?', opts:['в воде','в воздухе','в вакууме','в стекле'], ans:2, why:'В вакууме нет частиц — нечем передавать.'},
|
||||
{q:'Почему шуба греет?', opts:['Сама вырабатывает тепло','Между ворсинками воздух — плохой проводник','Отражает тепло','Уменьшает излучение'], ans:1, why:'Шерсть удерживает прослойки воздуха, которые плохо проводят тепло.'},
|
||||
{q:'У какого вещества теплопроводность выше?', opts:['у воды','у льда','у пара','одинакова'], ans:1, why:'У льда (твёрдое — упорядоченные молекулы передают энергию быстрее).'},
|
||||
{q:'Зачем стеклопакет с воздухом между стёклами?', opts:['Для прочности','Для красоты','Воздух — плохой проводник, теряется меньше тепла','Чтобы стекло не треснуло'], ans:2, why:'Прослойка воздуха работает как теплоизолятор.'},
|
||||
{q:'Какие частицы переносят тепло в металлах быстрее всего?', opts:['Атомы','Ионы','Молекулы','Свободные электроны'], ans:3, why:'Свободные электроны движутся быстро и переносят $E_k$ через весь металл.'}
|
||||
];
|
||||
let i = 0, ok = 0, done = 0, awarded = false;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p3-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>';
|
||||
h += '<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="p3-mcq-fb"></div><div class="actions"><button class="btn" id="p3-mcq-next">Следующий вопрос</button></div>';
|
||||
wrap.innerHTML = h;
|
||||
document.getElementById('p3-mcq-i').textContent = (i+1);
|
||||
document.getElementById('p3-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('p3-mcq-fb');
|
||||
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(2,'p3-mcq'); bumpProgress('p3', 3); }
|
||||
else { done++; fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p3-mcq-ok').textContent = ok;
|
||||
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p3-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='✓ +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p3-mcq-bonus'); bumpProgress('p3', 15); }, 600); }
|
||||
});
|
||||
});
|
||||
const nb = document.getElementById('p3-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======== §4 — Конвекция ======== */
|
||||
function build_p4(){
|
||||
const box = document.getElementById('p4-body');
|
||||
let h = '';
|
||||
|
||||
h += makeCard('theory', 'Что такое конвекция', '§ 4.1',
|
||||
'<p><b>Конвекция</b> — передача внутренней энергии <b>потоками</b> жидкости или газа.</p>'
|
||||
+'<p>Нагретая часть жидкости (газа) <b>расширяется</b>, становится менее плотной и <b>всплывает</b> вверх. Холодная — опускается вниз. Так образуется круговорот, который и переносит тепло.</p>'
|
||||
+'<p>В <b>твёрдых телах</b> конвекции нет — молекулы зафиксированы, потоков образоваться не может.</p>'
|
||||
);
|
||||
h += makeCard('rule', 'Условия для конвекции', '§ 4.2',
|
||||
'<ul style="padding-left:20px;margin:4px 0">'
|
||||
+'<li>есть текучая среда (газ или жидкость);</li>'
|
||||
+'<li>есть <b>разность температур</b> сверху и снизу: нагрев должен быть снизу или охлаждение сверху;</li>'
|
||||
+'<li>есть гравитация (в невесомости конвекции почти нет).</li>'
|
||||
+'</ul>'
|
||||
+'<p style="margin-top:6px">Если греть сверху — конвекции не будет: горячий слой и так наверху.</p>'
|
||||
);
|
||||
h += makeCard('example', 'Где встречается', '§ 4.3',
|
||||
'<ul style="padding-left:20px;margin:4px 0">'
|
||||
+'<li>Батарея греет всю комнату — горячий воздух поднимается к потолку, холодный спускается к полу.</li>'
|
||||
+'<li>Кипение чайника: пузырьки и потоки воды видны глазом.</li>'
|
||||
+'<li>Ветры в атмосфере: тёплый экватор и холодные полюса.</li>'
|
||||
+'<li>Течения в океане: тёплый Гольфстрим греет Европу.</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">Включи / выключи нагреватель снизу. Тёплая жидкость <b>поднимается</b> по центру, холодная <b>опускается</b> по краям.</div>'
|
||||
+'<svg id="p4-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
|
||||
+'<div class="actions" style="margin-top:8px"><button class="btn primary" id="p4-on">Включить нагрев</button><button class="btn" id="p4-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="p4-quiz"></div>'
|
||||
+'<div class="actions"><button class="btn" id="p4-quiz-next">Следующий раунд</button></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p4-quiz-r">1</b> / 6</span><span>Правильно: <b id="p4-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="p4-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="yes"></div></div>'
|
||||
+'<div class="drop-box"><h5>Конвекция невозможна</h5><div class="drop-items" data-cat="no"></div></div>'
|
||||
+'</div>'
|
||||
+'<div class="actions"><button class="btn primary" id="p4-dnd-check">Проверить</button><button class="btn" id="p4-dnd-reset">Сброс</button></div>'
|
||||
+'<div class="feedback" id="p4-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="p4-mcq"></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p4-mcq-i">1</b> / 6</span><span>Правильно: <b id="p4-mcq-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('p4') + readButton('p4');
|
||||
renderMath(box);
|
||||
wireReadBtn('p4');
|
||||
|
||||
_initP4_sim();
|
||||
_initP4_quiz();
|
||||
_initP4_dnd();
|
||||
_initP4_mcq();
|
||||
}
|
||||
|
||||
function _initP4_sim(){
|
||||
_killSim('p4sim');
|
||||
const svg = document.getElementById('p4-sim'); if(!svg) return;
|
||||
const W=460, H=240, vesselX=120, vesselY=20, vesselW=220, vesselH=180;
|
||||
/* 30 частиц жидкости */
|
||||
const N = 30;
|
||||
const ps = [];
|
||||
for(let i=0;i<N;i++){
|
||||
ps.push({
|
||||
x: vesselX + 10 + Math.random()*(vesselW-20),
|
||||
y: vesselY + 10 + Math.random()*(vesselH-20),
|
||||
vx: 0, vy: 0,
|
||||
T: 20
|
||||
});
|
||||
}
|
||||
let heaterOn = false;
|
||||
function tick(){
|
||||
if(!_isVisible('p4')){ _SIMS.p4sim.raf = requestAnimationFrame(tick); return; }
|
||||
/* Движение по тороидальному полю при включённом нагреве */
|
||||
for(const p of ps){
|
||||
if(heaterOn){
|
||||
/* нагрев в нижней зоне */
|
||||
const distFromBottom = (vesselY + vesselH) - p.y;
|
||||
if(distFromBottom < 30) p.T = Math.min(90, p.T + 0.3);
|
||||
else p.T = Math.max(20, p.T - 0.04);
|
||||
|
||||
/* поле скорости: тёплая частица в нижней половине → вверх,
|
||||
холодная в верхней → вниз. По краям движение в обратные стороны. */
|
||||
const cx = vesselX + vesselW/2;
|
||||
const dx = p.x - cx;
|
||||
const yNorm = (p.y - vesselY) / vesselH; /* 0 верх .. 1 низ */
|
||||
/* основной вертикальный поток: вверх у центра, вниз у стенок */
|
||||
if(Math.abs(dx) < vesselW*0.25) p.vy = -0.9 * (p.T - 20)/70 - 0.2;
|
||||
else p.vy = 0.6 * (1 - yNorm)*0.8 + 0.2;
|
||||
/* горизонтальное замыкание петли */
|
||||
if(yNorm < 0.18) p.vx = (dx > 0 ? 1 : -1) * 0.8;
|
||||
else if(yNorm > 0.82) p.vx = (dx < 0 ? 1 : -1) * 0.8;
|
||||
else p.vx *= 0.85;
|
||||
} else {
|
||||
p.T = Math.max(20, p.T - 0.1);
|
||||
p.vx *= 0.94; p.vy *= 0.94;
|
||||
}
|
||||
p.x += p.vx; p.y += p.vy;
|
||||
/* стенки */
|
||||
if(p.x < vesselX+6){ p.x = vesselX+6; p.vx = Math.abs(p.vx); }
|
||||
if(p.x > vesselX+vesselW-6){ p.x = vesselX+vesselW-6; p.vx = -Math.abs(p.vx); }
|
||||
if(p.y < vesselY+6){ p.y = vesselY+6; p.vy = Math.abs(p.vy); }
|
||||
if(p.y > vesselY+vesselH-6){ p.y = vesselY+vesselH-6; p.vy = -Math.abs(p.vy); }
|
||||
}
|
||||
let s = '';
|
||||
/* сосуд */
|
||||
s += '<rect x="'+vesselX+'" y="'+vesselY+'" width="'+vesselW+'" height="'+vesselH+'" fill="#e0f2fe" stroke="#0f172a" stroke-width="2" rx="6"/>';
|
||||
/* нагреватель снизу */
|
||||
s += '<rect x="'+(vesselX-4)+'" y="'+(vesselY+vesselH+4)+'" width="'+(vesselW+8)+'" height="18" fill="'+(heaterOn?'#dc2626':'#94a3b8')+'" stroke="#0f172a" stroke-width="1.5" rx="4"/>';
|
||||
s += '<text x="'+(vesselX+vesselW/2)+'" y="'+(vesselY+vesselH+17)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">'+(heaterOn?'НАГРЕВАТЕЛЬ ВКЛ':'выкл')+'</text>';
|
||||
/* частицы */
|
||||
for(const p of ps){
|
||||
const c = window.PHYS.tempColor(p.T, 20, 90);
|
||||
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="5" fill="'+c+'" stroke="#0f172a" stroke-width="0.5"/>';
|
||||
}
|
||||
/* стрелки потока (декоративные) */
|
||||
if(heaterOn){
|
||||
const cx = vesselX + vesselW/2;
|
||||
s += window.PHYS.drawArrow(cx, vesselY+vesselH-20, cx, vesselY+30, '#dc2626', 2, 9);
|
||||
s += window.PHYS.drawArrow(vesselX+18, vesselY+30, vesselX+18, vesselY+vesselH-20, '#2563eb', 2, 9);
|
||||
s += window.PHYS.drawArrow(vesselX+vesselW-18, vesselY+30, vesselX+vesselW-18, vesselY+vesselH-20, '#2563eb', 2, 9);
|
||||
}
|
||||
svg.innerHTML = s;
|
||||
_SIMS.p4sim.raf = requestAnimationFrame(tick);
|
||||
}
|
||||
_SIMS.p4sim = { raf: 0 };
|
||||
_SIMS.p4sim.raf = requestAnimationFrame(tick);
|
||||
document.getElementById('p4-on').addEventListener('click', ()=>{
|
||||
heaterOn = !heaterOn;
|
||||
document.getElementById('p4-on').textContent = heaterOn ? 'Выключить нагрев' : 'Включить нагрев';
|
||||
});
|
||||
document.getElementById('p4-reset').addEventListener('click', ()=>{
|
||||
heaterOn = false;
|
||||
document.getElementById('p4-on').textContent = 'Включить нагрев';
|
||||
for(let i=0;i<N;i++){
|
||||
ps[i].x = vesselX + 10 + Math.random()*(vesselW-20);
|
||||
ps[i].y = vesselY + 10 + Math.random()*(vesselH-20);
|
||||
ps[i].vx = 0; ps[i].vy = 0; ps[i].T = 20;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _initP4_quiz(){
|
||||
const QS = [
|
||||
{sit:'Воздух в комнате с радиатором у стены', ans:'Y', why:'Тёплый воздух поднимается, холодный спускается — классическая конвекция.'},
|
||||
{sit:'Вода в кастрюле, нагреваемой снизу', ans:'Y', why:'Нагрев снизу + жидкость = конвекционный поток.'},
|
||||
{sit:'Камень во время летнего полудня', ans:'N', why:'Камень — твёрдое тело, частицы не движутся, конвекции нет.'},
|
||||
{sit:'Вода в сосуде, нагреваемом сверху', ans:'N', why:'Горячий слой уже наверху — он там и останется, потоков нет.'},
|
||||
{sit:'Атмосфера планеты', ans:'Y', why:'Газ + разность температур (день/ночь, экватор/полюса) = ветры.'},
|
||||
{sit:'Металлический стержень, нагреваемый с конца', ans:'N', why:'Это твёрдое тело — теплопроводность, а не конвекция.'}
|
||||
];
|
||||
let i = 0, ok = 0;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p4-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:10px">'
|
||||
+'<button class="btn" data-pick="Y" style="padding:14px"><b>Да, конвекция</b></button>'
|
||||
+'<button class="btn" data-pick="N" style="padding:14px"><b>Нет, не конвекция</b></button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="p4-quiz-fb"></div>';
|
||||
document.getElementById('p4-quiz-r').textContent = (i+1);
|
||||
document.getElementById('p4-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('p4-quiz-fb');
|
||||
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(3,'p4-quiz'); bumpProgress('p4', 4); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p4-quiz-ok').textContent = ok;
|
||||
});
|
||||
});
|
||||
}
|
||||
document.getElementById('p4-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
render();
|
||||
}
|
||||
|
||||
function _initP4_dnd(){
|
||||
const items = [
|
||||
{id:'rad', cat:'yes', html:'воздух у радиатора'},
|
||||
{id:'ket', cat:'yes', html:'вода в кипящем чайнике'},
|
||||
{id:'oce', cat:'yes', html:'течения в океане'},
|
||||
{id:'wnd', cat:'yes', html:'ветер в атмосфере'},
|
||||
{id:'sto', cat:'no', html:'нагрев стального стержня'},
|
||||
{id:'top', cat:'no', html:'вода, нагреваемая сверху'},
|
||||
{id:'vac', cat:'no', html:'жидкость в невесомости'},
|
||||
{id:'wal', cat:'no', html:'кирпичная стена'}
|
||||
];
|
||||
const dnd = setupSorter({ poolId:'p4-dnd-pool', scopeSelector:'#sec-p4', cats:['yes','no'], items, columnLayout:false });
|
||||
document.getElementById('p4-dnd-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p4-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='✓ Идеально! +15 XP. Конвекция требует текучей среды + разности температур + гравитации.'; addXp(15,'p4-dnd'); bumpProgress('p4', 20); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'. В твёрдых телах и при нагреве сверху конвекции нет.'; }
|
||||
});
|
||||
document.getElementById('p4-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p4-dnd-fb'); fb.style.display='none'; });
|
||||
}
|
||||
|
||||
function _initP4_mcq(){
|
||||
const QS = [
|
||||
{q:'Что переносится при конвекции?', opts:['Только энергия','Только вещество','Вещество вместе с энергией','Электромагнитные волны'], ans:2, why:'Жидкость или газ движется вместе с теплотой.'},
|
||||
{q:'Почему в твёрдых телах нет конвекции?', opts:['Они холодные','Молекулы не могут свободно перемещаться','Они не проводят тепло','Они отражают энергию'], ans:1, why:'В твёрдом теле молекулы прочно связаны и не образуют потоков.'},
|
||||
{q:'Как направлен поток в нагреваемой снизу жидкости?', opts:['Горячий вниз, холодный вверх','Горячий вверх, холодный вниз','Хаотически','Только по краям'], ans:1, why:'Нагретая часть менее плотная — всплывает.'},
|
||||
{q:'Зачем радиатор ставят под окном?', opts:['Чтобы не мешал','Чтобы тёплый воздух поднимался, не пропуская холодного с улицы','Так красивее','Для экономии труб'], ans:1, why:'Восходящий тёплый поток сразу отсекает падающий холодный.'},
|
||||
{q:'Возможна ли конвекция в открытом космосе?', opts:['Да, как на Земле','Только в атмосфере планет','Нет, нужна гравитация и среда','Только у звёзд'], ans:2, why:'В вакууме нет среды; в невесомости нет всплывания.'},
|
||||
{q:'Куда направить пламя свечи, чтобы быстрее закипела вода в стакане?', opts:['Сверху','Снизу','Сбоку','Не имеет значения'], ans:1, why:'Нагрев снизу запускает конвекцию — вода прогреется быстрее.'}
|
||||
];
|
||||
let i = 0, ok = 0, done = 0, awarded = false;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p4-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>';
|
||||
h += '<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="p4-mcq-fb"></div><div class="actions"><button class="btn" id="p4-mcq-next">Следующий вопрос</button></div>';
|
||||
wrap.innerHTML = h;
|
||||
document.getElementById('p4-mcq-i').textContent = (i+1);
|
||||
document.getElementById('p4-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('p4-mcq-fb');
|
||||
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(2,'p4-mcq'); bumpProgress('p4', 3); }
|
||||
else { done++; fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p4-mcq-ok').textContent = ok;
|
||||
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p4-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='✓ +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p4-mcq-bonus'); bumpProgress('p4', 15); }, 600); }
|
||||
});
|
||||
});
|
||||
const nb = document.getElementById('p4-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======== §5 — Излучение ======== */
|
||||
function build_p5(){
|
||||
const box = document.getElementById('p5-body');
|
||||
let h = '';
|
||||
|
||||
h += makeCard('theory', 'Что такое тепловое излучение', '§ 5.1',
|
||||
'<p><b>Излучение</b> — передача энергии электромагнитными волнами. Не требует среды: идёт через вакуум.</p>'
|
||||
+'<p>Любое тело с температурой выше абсолютного нуля излучает. Чем выше $T$, тем интенсивнее излучение и тем более коротковолновое (горячая печь — инфракрасный диапазон; Солнце — видимый свет).</p>'
|
||||
+'<p>Так Солнце греет Землю, прошив 150 млн км вакуума за 8 минут.</p>'
|
||||
);
|
||||
h += makeCard('rule', 'Поглощение и отражение', '§ 5.2',
|
||||
'<ul style="padding-left:20px;margin:4px 0">'
|
||||
+'<li><b>Тёмные</b> и шероховатые поверхности — <b>хорошо поглощают</b> излучение (нагреваются быстрее).</li>'
|
||||
+'<li><b>Светлые</b> и зеркальные — <b>отражают</b> излучение (нагреваются медленнее).</li>'
|
||||
+'<li>Те же тёмные поверхности и <b>излучают</b> сильнее: чёрный чайник остывает быстрее.</li>'
|
||||
+'</ul>'
|
||||
+'<p style="margin-top:6px"><b>Правило:</b> что хорошо поглощает, то хорошо и излучает.</p>'
|
||||
);
|
||||
h += makeCard('example', 'Применение', '§ 5.3',
|
||||
'<ul style="padding-left:20px;margin:4px 0">'
|
||||
+'<li>Чёрные машины в жару раскаляются сильнее белых.</li>'
|
||||
+'<li>Зеркальные стенки термоса уменьшают потерю излучения.</li>'
|
||||
+'<li>Космические корабли покрывают фольгой, чтобы отражать солнечное излучение.</li>'
|
||||
+'<li>Тёмная футболка летом — жарко; белая — прохладно.</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">Включи Солнце. Чёрная пластина <b>поглощает</b> излучение и быстро нагревается, белая <b>отражает</b> и нагревается медленно.</div>'
|
||||
+'<svg id="p5-sim" viewBox="0 0 460 240" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
|
||||
+'<div class="actions" style="margin-top:8px"><button class="btn primary" id="p5-on">Включить Солнце</button><button class="btn" id="p5-reset">Сброс</button></div>'
|
||||
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:4px">'
|
||||
+'<span>Чёрная пластина: $T = $ <b id="p5-tb">20</b> °C</span>'
|
||||
+'<span>Белая пластина: $T = $ <b id="p5-tw">20</b> °C</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="p5-quiz"></div>'
|
||||
+'<div class="actions"><button class="btn" id="p5-quiz-next">Следующее</button></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p5-quiz-r">1</b> / 7</span><span>Правильно: <b id="p5-quiz-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV3 — DnD 3 вида теплопередачи */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">3 вида теплопередачи</div></div>'
|
||||
+'<div class="wg-help">Сортируй примеры на три группы: проводность, конвекция, излучение.</div>'
|
||||
+'<div id="p5-dnd-pool"></div>'
|
||||
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:10px">'
|
||||
+'<div class="drop-box"><h5>Проводность</h5><div class="drop-items" data-cat="cond"></div></div>'
|
||||
+'<div class="drop-box"><h5>Конвекция</h5><div class="drop-items" data-cat="conv"></div></div>'
|
||||
+'<div class="drop-box"><h5>Излучение</h5><div class="drop-items" data-cat="rad"></div></div>'
|
||||
+'</div>'
|
||||
+'<div class="actions"><button class="btn primary" id="p5-dnd-check">Проверить</button><button class="btn" id="p5-dnd-reset">Сброс</button></div>'
|
||||
+'<div class="feedback" id="p5-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="p5-mcq"></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p5-mcq-i">1</b> / 6</span><span>Правильно: <b id="p5-mcq-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('p5') + readButton('p5');
|
||||
renderMath(box);
|
||||
wireReadBtn('p5');
|
||||
|
||||
_initP5_sim();
|
||||
_initP5_quiz();
|
||||
_initP5_dnd();
|
||||
_initP5_mcq();
|
||||
}
|
||||
|
||||
function _initP5_sim(){
|
||||
_killSim('p5sim');
|
||||
const svg = document.getElementById('p5-sim'); if(!svg) return;
|
||||
const W=460, H=240;
|
||||
let sunOn = false;
|
||||
let tB = 20, tW = 20; /* black, white plate temps */
|
||||
function reset(){ tB=20; tW=20; sunOn=false; document.getElementById('p5-on').textContent='Включить Солнце'; document.getElementById('p5-tb').textContent='20'; document.getElementById('p5-tw').textContent='20'; }
|
||||
function tick(){
|
||||
if(!_isVisible('p5')){ _SIMS.p5sim.raf = requestAnimationFrame(tick); return; }
|
||||
if(sunOn){
|
||||
/* чёрная: поглощает 0.92, белая: 0.15 */
|
||||
if(tB < 75) tB += 0.32;
|
||||
if(tW < 35) tW += 0.05;
|
||||
} else {
|
||||
if(tB > 20) tB -= 0.10;
|
||||
if(tW > 20) tW -= 0.10;
|
||||
}
|
||||
document.getElementById('p5-tb').textContent = tB.toFixed(0);
|
||||
document.getElementById('p5-tw').textContent = tW.toFixed(0);
|
||||
/* draw */
|
||||
let s = '';
|
||||
/* Солнце */
|
||||
const cx = 230, cy = 36;
|
||||
s += '<circle cx="'+cx+'" cy="'+cy+'" r="22" fill="'+(sunOn?'#fbbf24':'#cbd5e1')+'" stroke="#0f172a" stroke-width="1.5"/>';
|
||||
if(sunOn){
|
||||
for(let i=0;i<12;i++){
|
||||
const a = i*Math.PI/6;
|
||||
const x1 = cx + 26*Math.cos(a), y1 = cy + 26*Math.sin(a);
|
||||
const x2 = cx + 36*Math.cos(a), y2 = cy + 36*Math.sin(a);
|
||||
s += '<line x1="'+x1.toFixed(1)+'" y1="'+y1.toFixed(1)+'" x2="'+x2.toFixed(1)+'" y2="'+y2.toFixed(1)+'" stroke="#fbbf24" stroke-width="2.5" stroke-linecap="round"/>';
|
||||
}
|
||||
}
|
||||
/* Лучи к пластинам */
|
||||
if(sunOn){
|
||||
const ray = (x1,y1,x2,y2,col)=>'<line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'" stroke="'+col+'" stroke-width="1.6" stroke-dasharray="6 4" opacity="0.8"/>';
|
||||
/* к чёрной */
|
||||
s += ray(cx-22, cy+18, 120, 175, '#fbbf24');
|
||||
s += ray(cx-10, cy+22, 145, 175, '#fbbf24');
|
||||
s += ray(cx, cy+22, 170, 175, '#fbbf24');
|
||||
/* к белой */
|
||||
s += ray(cx+22, cy+18, 340, 175, '#fbbf24');
|
||||
s += ray(cx+10, cy+22, 315, 175, '#fbbf24');
|
||||
s += ray(cx, cy+22, 290, 175, '#fbbf24');
|
||||
/* отражённые от белой (вверх) */
|
||||
s += '<line x1="290" y1="172" x2="270" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
|
||||
s += '<line x1="315" y1="172" x2="310" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
|
||||
s += '<line x1="340" y1="172" x2="350" y2="100" stroke="#cbd5e1" stroke-width="1.4" stroke-dasharray="4 4" opacity="0.7"/>';
|
||||
}
|
||||
/* чёрная пластина */
|
||||
const cB = sunOn ? window.PHYS.tempColor(tB, 20, 80) : '#1f2937';
|
||||
s += '<rect x="100" y="175" width="100" height="40" fill="'+cB+'" stroke="#0f172a" stroke-width="2" rx="3"/>';
|
||||
s += '<text x="150" y="200" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="'+(tB>50?'#fff':'#0f172a')+'">'+tB.toFixed(0)+' °C</text>';
|
||||
s += '<text x="150" y="230" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">ЧЁРНАЯ</text>';
|
||||
/* белая пластина */
|
||||
s += '<rect x="270" y="175" width="100" height="40" fill="#f1f5f9" stroke="#0f172a" stroke-width="2" rx="3"/>';
|
||||
s += '<text x="320" y="200" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#0f172a">'+tW.toFixed(0)+' °C</text>';
|
||||
s += '<text x="320" y="230" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">БЕЛАЯ</text>';
|
||||
svg.innerHTML = s;
|
||||
_SIMS.p5sim.raf = requestAnimationFrame(tick);
|
||||
}
|
||||
_SIMS.p5sim = { raf: 0 };
|
||||
_SIMS.p5sim.raf = requestAnimationFrame(tick);
|
||||
document.getElementById('p5-on').addEventListener('click', ()=>{
|
||||
sunOn = !sunOn;
|
||||
document.getElementById('p5-on').textContent = sunOn ? 'Выключить Солнце' : 'Включить Солнце';
|
||||
});
|
||||
document.getElementById('p5-reset').addEventListener('click', reset);
|
||||
}
|
||||
|
||||
function _initP5_quiz(){
|
||||
const QS = [
|
||||
{st:'Излучение требует среды (например, воздуха) для распространения.', ans:'F', why:'Излучение проходит через вакуум, что и доказывает солнечный свет.'},
|
||||
{st:'Тёмные тела сильнее нагреваются под Солнцем, чем светлые.', ans:'T', why:'Тёмная поверхность поглощает большую долю излучения.'},
|
||||
{st:'Любое тело с $T > 0$ К излучает.', ans:'T', why:'Тепловое излучение есть всегда, чем горячее — тем сильнее.'},
|
||||
{st:'Чем горячее тело, тем меньше излучения оно отдаёт.', ans:'F', why:'Наоборот: интенсивность излучения резко растёт с температурой.'},
|
||||
{st:'Хороший поглотитель — хороший излучатель.', ans:'T', why:'Это правило Кирхгофа: $a = \\varepsilon$.'},
|
||||
{st:'Зеркальная поверхность плохо излучает.', ans:'T', why:'Зеркало почти всё отражает — а значит и излучает мало.'},
|
||||
{st:'Излучение нельзя обнаружить, если тело не светится видимым светом.', ans:'F', why:'Тело при комнатной температуре излучает инфракрасные волны — их видит тепловизор.'}
|
||||
];
|
||||
let i = 0, ok = 0;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p5-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.st+'"</div>'
|
||||
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
|
||||
+'<button class="btn" data-pick="T" style="padding:14px"><b>Правда</b></button>'
|
||||
+'<button class="btn" data-pick="F" style="padding:14px"><b>Ложь</b></button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="p5-quiz-fb"></div>';
|
||||
document.getElementById('p5-quiz-r').textContent = (i+1);
|
||||
document.getElementById('p5-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('p5-quiz-fb');
|
||||
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(3,'p5-quiz'); bumpProgress('p5', 4); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p5-quiz-ok').textContent = ok;
|
||||
renderMath(wrap);
|
||||
});
|
||||
});
|
||||
renderMath(wrap);
|
||||
}
|
||||
document.getElementById('p5-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
render();
|
||||
}
|
||||
|
||||
function _initP5_dnd(){
|
||||
const items = [
|
||||
{id:'spoon', cat:'cond', html:'нагрев ложки в супе'},
|
||||
{id:'bar', cat:'cond', html:'нагрев конца железного прута'},
|
||||
{id:'iron', cat:'cond', html:'утюг гладит бельё'},
|
||||
{id:'rad', cat:'conv', html:'батарея греет комнату'},
|
||||
{id:'ket', cat:'conv', html:'вода в кипящем чайнике'},
|
||||
{id:'wnd', cat:'conv', html:'ветры в атмосфере'},
|
||||
{id:'sun', cat:'rad', html:'Солнце греет Землю'},
|
||||
{id:'fire', cat:'rad', html:'тепло от костра на расстоянии'},
|
||||
{id:'lamp', cat:'rad', html:'нагрев руки под лампой накаливания'}
|
||||
];
|
||||
const dnd = setupSorter({ poolId:'p5-dnd-pool', scopeSelector:'#sec-p5', cats:['cond','conv','rad'], items, columnLayout:false });
|
||||
document.getElementById('p5-dnd-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p5-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='✓ Идеально! +20 XP. Ты освоил все 3 вида теплопередачи.'; addXp(20,'p5-dnd'); bumpProgress('p5', 25); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'. Проверь: контакт = проводность; потоки = конвекция; через пустоту = излучение.'; }
|
||||
});
|
||||
document.getElementById('p5-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p5-dnd-fb'); fb.style.display='none'; });
|
||||
}
|
||||
|
||||
function _initP5_mcq(){
|
||||
const QS = [
|
||||
{q:'Через какую среду НЕ может идти излучение?', opts:['Через воздух','Через стекло','Через вакуум','Излучение проходит через всё перечисленное'], ans:3, why:'Излучение распространяется и в среде, и в вакууме.'},
|
||||
{q:'Какая поверхность нагреется сильнее на Солнце?', opts:['Зеркальная','Белая','Чёрная','Прозрачная'], ans:2, why:'Чёрная поглощает больше всего излучения.'},
|
||||
{q:'Какие тела излучают?', opts:['Только горячие','Только светящиеся','Любые с $T > 0$ К','Только Солнце'], ans:2, why:'Тепловое излучение существует у любого тела с ненулевой температурой.'},
|
||||
{q:'Какой вид теплопередачи переносит тепло от Солнца к Земле?', opts:['Теплопроводность','Конвекция','Излучение','Все три'], ans:2, why:'Между Солнцем и Землёй вакуум — работает только излучение.'},
|
||||
{q:'Чем покрашен правильный термос изнутри?', opts:['Чёрной краской','Зеркальной плёнкой','Деревом','Стеклом'], ans:1, why:'Зеркальная поверхность плохо излучает — тепло сохраняется.'},
|
||||
{q:'Чёрная футболка в жару нагревается потому что…', opts:['легче белой','лучше поглощает излучение','быстрее остывает','тоньше'], ans:1, why:'Тёмная ткань поглощает большую часть солнечного излучения.'}
|
||||
];
|
||||
let i = 0, ok = 0, done = 0, awarded = false;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p5-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>';
|
||||
h += '<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="p5-mcq-fb"></div><div class="actions"><button class="btn" id="p5-mcq-next">Следующий вопрос</button></div>';
|
||||
wrap.innerHTML = h;
|
||||
document.getElementById('p5-mcq-i').textContent = (i+1);
|
||||
document.getElementById('p5-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('p5-mcq-fb');
|
||||
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(2,'p5-mcq'); bumpProgress('p5', 3); }
|
||||
else { done++; fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p5-mcq-ok').textContent = ok;
|
||||
renderMath(wrap);
|
||||
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf = document.getElementById('p5-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='✓ +15 XP — тренажёр пройден ('+ok+' из '+QS.length+').'; addXp(15,'p5-mcq-bonus'); bumpProgress('p5', 15); }, 600); }
|
||||
});
|
||||
});
|
||||
const nb = document.getElementById('p5-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
renderMath(wrap);
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
function init(){
|
||||
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
|
||||
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
|
||||
|
||||
Reference in New Issue
Block a user