feat(phys10 ch1 wave3): §5 «Уравнение состояния» + §6 «Изопроцессы» (PV/VT/PT диаграммы)
This commit is contained in:
@@ -2004,34 +2004,799 @@ function build_p4(){
|
||||
function build_p5(){
|
||||
const box = document.getElementById('p5-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', "Уравнение состояния идеального газа", "§5", `
|
||||
<p><b>Уравнение состояния идеального газа</b> — этот параграф в разработке (Phase 1+).</p>
|
||||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 1+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||||
</p>
|
||||
|
||||
/* THEORY 1 — Уравнение Менделеева–Клапейрона */
|
||||
html += makeCard('theory', "Уравнение Менделеева–Клапейрона", "§5", `
|
||||
<p><b>Уравнение состояния идеального газа</b> связывает три макропараметра — давление $p$, объём $V$ и температуру $T$. Для $\\nu$ молей газа:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$pV = \\nu R T$$</p>
|
||||
<p>где:</p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li>$p$ — давление (Па);</li>
|
||||
<li>$V$ — объём (м³);</li>
|
||||
<li>$T$ — абсолютная температура (К);</li>
|
||||
<li>$\\nu$ — количество вещества (моль);</li>
|
||||
<li>$R = 8{,}314$ Дж/(моль·К) — универсальная газовая постоянная.</li>
|
||||
</ul>
|
||||
<p style="margin-top:8px"><b>Через массу газа</b> ($\\nu = m/M$):</p>
|
||||
<p style="text-align:center;margin:10px 0">$$pV = \\dfrac{m}{M} R T$$</p>
|
||||
<p><b>Через концентрацию</b> ($n = N/V$, $\\nu = N/N_A$, $R = N_A k_B$):</p>
|
||||
<p style="text-align:center;margin:10px 0">$$p = n k_B T$$</p>
|
||||
<p>Это разные формы одного и того же уравнения состояния.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 2 — Объединённый газовый закон */
|
||||
html += makeCard('rule', "Объединённый газовый закон", "§5", `
|
||||
<p>Для <b>постоянной массы</b> идеального газа при переходе из состояния 1 в состояние 2:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\dfrac{p V}{T} = \\text{const} \\quad \\Rightarrow \\quad \\dfrac{p_1 V_1}{T_1} = \\dfrac{p_2 V_2}{T_2}$$</p>
|
||||
<p>Это позволяет связать два состояния газа без знания $\\nu$ или $M$.</p>
|
||||
<p style="margin-top:10px"><b>Пример.</b> Газ при $T_1 = 300$ К, $p_1 = 1$ атм, $V_1 = 10$ л нагрели до $T_2 = 450$ К, объём стал $V_2 = 12$ л. Найти $p_2$.</p>
|
||||
<p style="text-align:center;margin:10px 0">$$p_2 = \\dfrac{p_1 V_1 T_2}{T_1 V_2} = \\dfrac{1 \\cdot 10 \\cdot 450}{300 \\cdot 12} = 1{,}25 \\text{ атм}$$</p>
|
||||
`);
|
||||
|
||||
/* THEORY 3 — Нормальные условия и молярный объём */
|
||||
html += makeCard('example', "Нормальные условия и молярный объём", "§5", `
|
||||
<p><b>Нормальные условия</b> (н.у.):</p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li>$T_0 = 273$ К ($0°$C);</li>
|
||||
<li>$p_0 = 101{,}3$ кПа $\\approx 1$ атм.</li>
|
||||
</ul>
|
||||
<p>При нормальных условиях <b>молярный объём</b> любого идеального газа одинаков:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$V_M = \\dfrac{R T_0}{p_0} = \\dfrac{8{,}314 \\cdot 273}{101300} \\approx 22{,}4 \\text{ л/моль}$$</p>
|
||||
<p>1 моль <b>любого</b> идеального газа (водорода, азота, кислорода, углекислого газа…) при $0°$C и 1 атм занимает $22{,}4$ литра.</p>
|
||||
<p style="margin-top:8px"><b>Закон Авогадро:</b> в равных объёмах разных газов при одинаковых $p$ и $T$ содержится одинаковое число молекул.</p>
|
||||
`);
|
||||
|
||||
/* INTERACTIVE 1 — Визуализатор уравнения состояния */
|
||||
html += `<div class="wg" id="p5-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Визуализатор уравнения состояния</div></div>
|
||||
<div class="wg-help">Меняй $\\nu$, $T$, $V$ — получай давление по формуле $p = \\nu R T / V$. Цвет «контейнера» — по $T$, число точек — по $\\nu$.</div>
|
||||
<div class="sliders">
|
||||
<label>Кол-во вещества $\\nu$: <b id="p5-iv1-nL">1.0</b> моль <input type="range" id="p5-iv1-n" min="0.1" max="5" value="1" step="0.1"></label>
|
||||
<label>Температура $T$: <b id="p5-iv1-tL">300</b> К <input type="range" id="p5-iv1-t" min="100" max="800" value="300" step="10"></label>
|
||||
<label>Объём $V$: <b id="p5-iv1-vL">10</b> л <input type="range" id="p5-iv1-v" min="1" max="50" value="10" step="0.5"></label>
|
||||
</div>
|
||||
<div style="background:#0f172a;border-radius:9px;padding:10px;text-align:center">
|
||||
<svg id="p5-iv1-svg" viewBox="0 0 380 200" width="100%" style="max-width:380px;height:auto;background:#0f172a;border-radius:6px"></svg>
|
||||
</div>
|
||||
<div style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.85">
|
||||
<div>$pV = \\nu R T = $ <b id="p5-iv1-rhs">—</b> Дж</div>
|
||||
<div>Давление: $p = \\dfrac{\\nu R T}{V} \\approx $ <b id="p5-iv1-pPa">—</b> Па $\\approx$ <b id="p5-iv1-pAtm">—</b> атм</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — Калькулятор Менделеева–Клапейрона */
|
||||
html += `<div class="wg" id="p5-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор Менделеева–Клапейрона</div></div>
|
||||
<div class="wg-help">Выбери искомую величину — введи остальные параметры. Используется $pV = (m/M) R T$.</div>
|
||||
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p5-iv2-mode" value="p" checked> Найти $p$</label>
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p5-iv2-mode" value="V"> Найти $V$</label>
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p5-iv2-mode" value="T"> Найти $T$</label>
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p5-iv2-mode" value="m"> Найти $m$</label>
|
||||
</div>
|
||||
<div id="p5-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px"></div>
|
||||
<div class="actions" style="justify-content:center">
|
||||
<button class="btn primary" id="p5-iv2-go">Вычислить</button>
|
||||
</div>
|
||||
<div id="p5-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:80px;line-height:1.85"></div>
|
||||
<div class="feedback" id="p5-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — DnD: Какое отношение работает? */
|
||||
html += `<div class="wg" id="p5-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какое отношение работает?</div></div>
|
||||
<div class="wg-help">Перетащи 6 формул в 3 ящика — какой раздел теории газа описывает каждая.</div>
|
||||
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 6 формул — 3 категории</div>
|
||||
<div id="p5-iv3-pool"></div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-top:8px">
|
||||
<div class="drop-box"><h5 data-cat="eq">Уравнение состояния</h5><div class="drop-items" data-cat="eq"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="comb">Объединённый газовый закон</h5><div class="drop-items" data-cat="comb"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="sp">Частный случай / константа</h5><div class="drop-items" data-cat="sp"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p5-iv3-check">Проверить</button><button class="btn" id="p5-iv3-reset">Сначала</button></div>
|
||||
<div class="feedback" id="p5-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — Тренажёр уравнения состояния */
|
||||
html += `<div class="wg" id="p5-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр уравнения состояния</div></div>
|
||||
<div class="wg-help">6 задач. Используй $R = 8{,}3$ Дж/(моль·К), $1$ атм $= 10^5$ Па, $V_M = 22{,}4$ л/моль.</div>
|
||||
<div class="score-display"><span>Задача <b id="p5-iv4-i">1</b> / 6</span><span>Очки: <b id="p5-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p5-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
|
||||
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
|
||||
<input type="number" id="p5-iv4-ans" class="tinp" style="width:130px;text-align:center" step="any">
|
||||
<button class="btn primary" id="p5-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p5-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p5-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p4', 'p6');
|
||||
html += readButton('p5');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Визуализатор уравнения состояния */
|
||||
(function(){
|
||||
const svg = document.getElementById('p5-iv1-svg');
|
||||
const nInp = document.getElementById('p5-iv1-n');
|
||||
const tInp = document.getElementById('p5-iv1-t');
|
||||
const vInp = document.getElementById('p5-iv1-v');
|
||||
const nLab = document.getElementById('p5-iv1-nL');
|
||||
const tLab = document.getElementById('p5-iv1-tL');
|
||||
const vLab = document.getElementById('p5-iv1-vL');
|
||||
const rhsEl = document.getElementById('p5-iv1-rhs');
|
||||
const pPaEl = document.getElementById('p5-iv1-pPa');
|
||||
const pAtmEl = document.getElementById('p5-iv1-pAtm');
|
||||
const R = 8.314;
|
||||
const seen = new Set();
|
||||
let _xpDone = false;
|
||||
|
||||
function tColor(T){
|
||||
const t = Math.max(0, Math.min(1, (T - 100) / 700));
|
||||
const r = Math.round(255 * (0.16 + t * (0.94 - 0.16)));
|
||||
const g = Math.round(255 * (0.5 + t * (0.27 - 0.5)));
|
||||
const b = Math.round(255 * (0.96 + t * (0.21 - 0.96)));
|
||||
return 'rgb('+r+','+g+','+b+')';
|
||||
}
|
||||
function fmtSci(x){
|
||||
if(x === 0) return '0';
|
||||
const a = Math.abs(x);
|
||||
if(a >= 1e4 || a < 1e-2){
|
||||
const exp = Math.floor(Math.log10(a));
|
||||
const m = x / Math.pow(10, exp);
|
||||
return m.toFixed(2) + '·10^{' + exp + '}';
|
||||
}
|
||||
return (+x.toFixed(2)).toString();
|
||||
}
|
||||
function render(){
|
||||
const nu = +nInp.value;
|
||||
const T = +tInp.value;
|
||||
const Vl = +vInp.value;
|
||||
nLab.textContent = nu.toFixed(1);
|
||||
tLab.textContent = T;
|
||||
vLab.textContent = Vl.toFixed(1);
|
||||
|
||||
const Vm3 = Vl * 1e-3;
|
||||
const pPa = nu * R * T / Vm3;
|
||||
const pAtm = pPa / 1e5;
|
||||
const rhs = nu * R * T;
|
||||
|
||||
rhsEl.textContent = rhs.toFixed(1);
|
||||
pPaEl.textContent = (pPa / 1e5).toFixed(2) + '·10⁵';
|
||||
pAtmEl.textContent = pAtm.toFixed(2);
|
||||
|
||||
// SVG: прямоугольник, ширина по V (1..50 л → 60..360 px)
|
||||
const W = 380, H = 200, pad = 10;
|
||||
const boxW = 60 + (Vl - 1) * (300 / 49);
|
||||
const boxH = 130;
|
||||
const bx = (W - boxW) / 2, by = pad + 30;
|
||||
const col = tColor(T);
|
||||
|
||||
let g = '';
|
||||
g += '<rect x="'+bx+'" y="'+by+'" width="'+boxW+'" height="'+boxH+'" rx="6" fill="'+col+'" fill-opacity="0.18" stroke="'+col+'" stroke-width="2"/>';
|
||||
// молекулы — пропорционально nu (1..30 точек)
|
||||
const Nshow = Math.max(3, Math.round(nu * 8));
|
||||
for(let i = 0; i < Nshow; i++){
|
||||
const px = bx + 6 + Math.random() * (boxW - 12);
|
||||
const py = by + 6 + Math.random() * (boxH - 12);
|
||||
g += '<circle cx="'+px.toFixed(1)+'" cy="'+py.toFixed(1)+'" r="2.8" fill="#60a5fa" opacity="0.9"/>';
|
||||
}
|
||||
g += '<text x="'+(W/2)+'" y="20" text-anchor="middle" fill="'+col+'" font-family="Inter,sans-serif" font-size="13" font-weight="700">T = '+T+' K · V = '+Vl.toFixed(1)+' л · ν = '+nu.toFixed(1)+' моль</text>';
|
||||
g += '<text x="'+(W/2)+'" y="'+(by + boxH + 18)+'" text-anchor="middle" fill="#cbd5e1" font-family="JetBrains Mono,monospace" font-size="12">p ≈ '+pAtm.toFixed(2)+' атм</text>';
|
||||
svg.innerHTML = g;
|
||||
|
||||
seen.add(Math.round(nu*10)+':'+T+':'+Math.round(Vl*2));
|
||||
if(!_xpDone && seen.size >= 4){ _xpDone = true; addXp(10, 'p5-iv1'); bumpProgress('p5', 15); }
|
||||
}
|
||||
nInp.addEventListener('input', render);
|
||||
tInp.addEventListener('input', render);
|
||||
vInp.addEventListener('input', render);
|
||||
render();
|
||||
})();
|
||||
|
||||
/* IV2 — Калькулятор Менделеева–Клапейрона */
|
||||
(function(){
|
||||
const inpsBox = document.getElementById('p5-iv2-inputs');
|
||||
const out = document.getElementById('p5-iv2-out');
|
||||
const fb = document.getElementById('p5-iv2-fb');
|
||||
const go = document.getElementById('p5-iv2-go');
|
||||
const R = 8.314;
|
||||
const calcCount = new Set();
|
||||
let _done = false;
|
||||
|
||||
function fieldHTML(id, label, val){
|
||||
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
|
||||
}
|
||||
function build(mode){
|
||||
let h = '';
|
||||
// Всегда показываем M (молярную массу)
|
||||
h += fieldHTML('p5-iv2-M', '$M$, г/моль', '32');
|
||||
if(mode !== 'p') h += fieldHTML('p5-iv2-p', '$p$, атм', '1');
|
||||
if(mode !== 'V') h += fieldHTML('p5-iv2-V', '$V$, л', '22.4');
|
||||
if(mode !== 'T') h += fieldHTML('p5-iv2-T', '$T$, К', '273');
|
||||
if(mode !== 'm') h += fieldHTML('p5-iv2-m', '$m$, г', '32');
|
||||
inpsBox.innerHTML = h;
|
||||
renderMath(inpsBox);
|
||||
}
|
||||
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
|
||||
function getMode(){
|
||||
const r = document.querySelector('input[name="p5-iv2-mode"]:checked');
|
||||
return r ? r.value : 'p';
|
||||
}
|
||||
function calc(){
|
||||
const mode = getMode();
|
||||
const Mg = num('p5-iv2-M');
|
||||
if(!isFinite(Mg) || Mg <= 0){ feedback(fb, false, '✗ Введи положительную $M$ (г/моль).'); return; }
|
||||
const M = Mg * 1e-3;
|
||||
let pPa, Vm3, T, mKg;
|
||||
if(mode !== 'p'){ const pa = num('p5-iv2-p'); if(!isFinite(pa)||pa<=0){feedback(fb,false,'✗ Введи положительное $p$.');return;} pPa = pa * 1e5; }
|
||||
if(mode !== 'V'){ const vl = num('p5-iv2-V'); if(!isFinite(vl)||vl<=0){feedback(fb,false,'✗ Введи положительный $V$.');return;} Vm3 = vl * 1e-3; }
|
||||
if(mode !== 'T'){ T = num('p5-iv2-T'); if(!isFinite(T)||T<=0){feedback(fb,false,'✗ Введи положительную $T$.');return;} }
|
||||
if(mode !== 'm'){ const mg = num('p5-iv2-m'); if(!isFinite(mg)||mg<=0){feedback(fb,false,'✗ Введи положительную $m$.');return;} mKg = mg * 1e-3; }
|
||||
|
||||
let res = '', resVal = 0, unit = '';
|
||||
if(mode === 'p'){
|
||||
// p = (m/M) R T / V
|
||||
pPa = (mKg / M) * R * T / Vm3;
|
||||
resVal = pPa / 1e5; unit = 'атм';
|
||||
res = '$p = \\dfrac{m R T}{M V} \\approx '+(pPa/1e5).toFixed(3)+' \\cdot 10^5$ Па $\\approx '+resVal.toFixed(3)+'$ атм';
|
||||
} else if(mode === 'V'){
|
||||
Vm3 = (mKg / M) * R * T / pPa;
|
||||
resVal = Vm3 * 1000; unit = 'л';
|
||||
res = '$V = \\dfrac{m R T}{M p} \\approx '+(Vm3*1000).toFixed(3)+'$ л';
|
||||
} else if(mode === 'T'){
|
||||
T = pPa * Vm3 * M / (mKg * R);
|
||||
resVal = T; unit = 'К';
|
||||
res = '$T = \\dfrac{p V M}{m R} \\approx '+T.toFixed(1)+'$ К';
|
||||
} else { // m
|
||||
mKg = pPa * Vm3 * M / (R * T);
|
||||
resVal = mKg * 1000; unit = 'г';
|
||||
res = '$m = \\dfrac{p V M}{R T} \\approx '+(mKg*1000).toFixed(3)+'$ г';
|
||||
}
|
||||
out.innerHTML = '<div style="margin-bottom:8px"><b>Уравнение:</b> $pV = \\dfrac{m}{M} R T$</div>'
|
||||
+ '<div style="margin-bottom:8px"><b>Решение:</b> '+res+'</div>'
|
||||
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2)">'+(+resVal.toFixed(3))+' '+unit+'</span></div>';
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ Вычислено.');
|
||||
calcCount.add(mode + ':' + Math.round(resVal*100));
|
||||
if(!_done && calcCount.size >= 3){ _done = true; addXp(10, 'p5-iv2'); bumpProgress('p5', 15); }
|
||||
}
|
||||
document.querySelectorAll('input[name="p5-iv2-mode"]').forEach(r => r.addEventListener('change', () => build(getMode())));
|
||||
go.addEventListener('click', calc);
|
||||
build('p');
|
||||
})();
|
||||
|
||||
/* IV3 — DnD сортер формул */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'f1', cat:'eq', html:'$pV = \\nu R T$' },
|
||||
{ id:'f2', cat:'comb', html:'$\\dfrac{p_1 V_1}{T_1} = \\dfrac{p_2 V_2}{T_2}$' },
|
||||
{ id:'f3', cat:'eq', html:'$p = n k_B T$' },
|
||||
{ id:'f4', cat:'eq', html:'$pV = \\dfrac{m}{M} R T$' },
|
||||
{ id:'f5', cat:'sp', html:'$V_M = 22{,}4$ л/моль' },
|
||||
{ id:'f6', cat:'comb', html:'$\\dfrac{V_1}{T_1} = \\dfrac{V_2}{T_2}$ (при $p$=const)' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p5-iv3-pool',
|
||||
scopeSelector:'#p5-iv3',
|
||||
items: items,
|
||||
cats:['eq','comb','sp'],
|
||||
columnLayout:false,
|
||||
});
|
||||
document.getElementById('p5-iv3-check').addEventListener('click', () => {
|
||||
const fb = document.getElementById('p5-iv3-fb');
|
||||
const placedCount = items.filter(it => sorter.placed[it.id]).length;
|
||||
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
|
||||
if(placedCount < items.length){ feedback(fb, false, '✗ Размести все 6 формул.'); return; }
|
||||
if(correct === items.length){ feedback(fb, true, '✓ Все 6 верно! +10 XP'); addXp(10,'p5-iv3'); bumpProgress('p5', 15); }
|
||||
else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
|
||||
});
|
||||
document.getElementById('p5-iv3-reset').addEventListener('click', () => { sorter.reset(); document.getElementById('p5-iv3-fb').style.display = 'none'; });
|
||||
})();
|
||||
|
||||
/* IV4 — Тренажёр уравнения состояния */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'1 моль газа при $T = 300$ К, $p = 1$ атм. Найти $V$ в литрах. ($R = 8{,}3$)', ans:24.9, hint:'$V = \\nu R T / p = 1 \\cdot 8{,}3 \\cdot 300 / 10^5 = 0{,}0249$ м³ = 24,9 л' },
|
||||
{ q:'2 моль газа при $T = 273$ К, $V = 44{,}8$ л. Найти $p$ в атм.', ans:1, hint:'$p = \\nu R T / V = 2 \\cdot 8{,}3 \\cdot 273 / 0{,}0448 \\approx 10^5$ Па = 1 атм' },
|
||||
{ q:'При нагревании от $T_1 = 300$ К до $T_2 = 600$ К при $V$ = const, во сколько раз изменится $p$?', ans:2, hint:'$p \\sim T$ при $V$=const, $p_2/p_1 = 600/300 = 2$' },
|
||||
{ q:'4 г водорода ($M = 2$ г/моль) при $T = 273$ К, $p = 1$ атм. $V$ в литрах?', ans:44.8, hint:'$\\nu = m/M = 4/2 = 2$ моль. $V = 2 \\cdot V_M = 2 \\cdot 22{,}4 = 44{,}8$ л' },
|
||||
{ q:'$V_1 = 10$ л при $T_1 = 300$ К, $p_1 = 1$ атм. Нагрев до $T_2 = 360$ К при $p$ = const. $V_2 = ?$ л', ans:12, hint:'$V_2 = V_1 T_2/T_1 = 10 \\cdot 360/300 = 12$' },
|
||||
{ q:'6 г кислорода ($M = 32$ г/моль). Сколько это моль? (до 0,01)', ans:0.19, hint:'$\\nu = m/M = 6/32 = 0{,}1875 \\approx 0{,}19$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p5-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15, 'p5-iv4'); bumpProgress('p5', 25); }
|
||||
else if(score >= 4){ addXp(8, 'p5-iv4'); bumpProgress('p5', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p5-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p5-iv4-s').textContent = score;
|
||||
document.getElementById('p5-iv4-q').innerHTML = Q[i].q;
|
||||
document.getElementById('p5-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p5-iv4-q'));
|
||||
document.getElementById('p5-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p5-iv4-fb');
|
||||
const raw = document.getElementById('p5-iv4-ans').value.replace(',', '.');
|
||||
const ans = parseFloat(raw);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
let tol;
|
||||
if(Q[i].ans === 0.19) tol = 0.015;
|
||||
else if(Q[i].ans === 24.9 || Q[i].ans === 44.8) tol = 0.6;
|
||||
else tol = Math.max(0.05 * Math.abs(Q[i].ans), 0.05);
|
||||
if(Math.abs(ans - Q[i].ans) < tol + 0.001){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
|
||||
document.getElementById('p5-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1800);
|
||||
}
|
||||
document.getElementById('p5-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p5-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p5-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p5');
|
||||
}
|
||||
|
||||
function build_p6(){
|
||||
const box = document.getElementById('p6-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', "Изопроцессы", "§6", `
|
||||
<p><b>Изопроцессы</b> — этот параграф в разработке (Phase 1+).</p>
|
||||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 1+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||||
</p>
|
||||
|
||||
/* THEORY 1 — Три изопроцесса */
|
||||
html += makeCard('theory', "Три изопроцесса", "§6", `
|
||||
<p><b>Изопроцесс</b> — процесс, при котором один из параметров идеального газа остаётся постоянным (при <b>постоянной массе</b> газа).</p>
|
||||
<p style="margin-top:10px"><b>1) Изотермический</b> ($T = \\text{const}$) — <b>закон Бойля–Мариотта</b>:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$pV = \\text{const} \\quad \\Leftrightarrow \\quad p_1 V_1 = p_2 V_2$$</p>
|
||||
<p><b>2) Изобарный</b> ($p = \\text{const}$) — <b>закон Гей-Люссака</b>:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\dfrac{V}{T} = \\text{const} \\quad \\Leftrightarrow \\quad \\dfrac{V_1}{T_1} = \\dfrac{V_2}{T_2}$$</p>
|
||||
<p><b>3) Изохорный</b> ($V = \\text{const}$) — <b>закон Шарля</b>:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\dfrac{p}{T} = \\text{const} \\quad \\Leftrightarrow \\quad \\dfrac{p_1}{T_1} = \\dfrac{p_2}{T_2}$$</p>
|
||||
<p style="margin-top:8px">Все три закона — частные случаи объединённого газового закона $pV/T = \\text{const}$.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 2 — Графики */
|
||||
html += makeCard('rule', "Графики изопроцессов", "§6", `
|
||||
<p>В трёх системах координат изопроцессы выглядят так:</p>
|
||||
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
|
||||
<thead>
|
||||
<tr style="background:var(--sec-acc-soft)">
|
||||
<th style="padding:8px;border:1px solid var(--border);text-align:left">Процесс</th>
|
||||
<th style="padding:8px;border:1px solid var(--border)">$p$–$V$</th>
|
||||
<th style="padding:8px;border:1px solid var(--border)">$V$–$T$</th>
|
||||
<th style="padding:8px;border:1px solid var(--border)">$p$–$T$</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid var(--border)"><b style="color:#ea580c">Изотерма</b> ($T$=const)</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">гипербола</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">прямая через 0</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">прямая через 0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid var(--border)"><b style="color:#2563eb">Изобара</b> ($p$=const)</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">горизонт. прямая</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">прямая через 0</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">горизонт. прямая</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px;border:1px solid var(--border)"><b style="color:#10b981">Изохора</b> ($V$=const)</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">вертик. прямая</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">горизонт. прямая</td>
|
||||
<td style="padding:8px;border:1px solid var(--border);text-align:center">прямая через 0</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Главные характеристики:</p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li><b>Изотерма</b> на $p$–$V$ — гипербола: $p = \\nu R T / V$.</li>
|
||||
<li>Изобара и изохора на $V$–$T$ и $p$–$T$ — прямые, проходящие <b>через начало координат</b>.</li>
|
||||
<li>Чем больше $T$ у изотермы, тем дальше гипербола от осей.</li>
|
||||
</ul>
|
||||
`);
|
||||
|
||||
/* THEORY 3 — Примеры */
|
||||
html += makeCard('example', "Применение и примеры", "§6", `
|
||||
<p><b>Изотермический процесс:</b> воздушный шарик медленно сжимают руками — температура почти не меняется (теплообмен со средой). $p_1 V_1 = p_2 V_2$.</p>
|
||||
<p style="margin-top:8px"><b>Изобарный процесс:</b> воздух в открытом цилиндре с поршнем нагревают — давление равно атмосферному (= const), объём растёт пропорционально $T$. $V_1 / T_1 = V_2 / T_2$.</p>
|
||||
<p style="margin-top:8px"><b>Изохорный процесс:</b> газ в герметичном сосуде нагревают — объём фиксирован, давление растёт пропорционально $T$. $p_1 / T_1 = p_2 / T_2$.</p>
|
||||
<p style="margin-top:10px"><b>Пример.</b> Газ в шине автомобиля изохорно нагревают летом от $T_1 = 290$ К до $T_2 = 320$ К. Если $p_1 = 2$ атм:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$p_2 = p_1 \\dfrac{T_2}{T_1} = 2 \\cdot \\dfrac{320}{290} \\approx 2{,}21 \\text{ атм}$$</p>
|
||||
`);
|
||||
|
||||
/* INTERACTIVE 1 — Графики изопроцессов (главный визуал) */
|
||||
html += `<div class="wg" id="p6-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Графики изопроцессов: $p$–$V$, $V$–$T$, $p$–$T$</div></div>
|
||||
<div class="wg-help">Переключай тип процесса и параметр — смотри, как меняются 3 диаграммы. Текущая кривая ярче, две альтернативные — бледнее.</div>
|
||||
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p6-iv1-proc" value="iso" checked> <span style="color:#ea580c;font-weight:700">Изотерма</span></label>
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p6-iv1-proc" value="bar"> <span style="color:#2563eb;font-weight:700">Изобара</span></label>
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;font-size:.92rem"><input type="radio" name="p6-iv1-proc" value="hor"> <span style="color:#10b981;font-weight:700">Изохора</span></label>
|
||||
</div>
|
||||
<div class="sliders">
|
||||
<label id="p6-iv1-paramLbl">Температура $T$: <b id="p6-iv1-pL">300</b> К <input type="range" id="p6-iv1-param" min="200" max="600" value="300" step="10"></label>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:10px;margin-top:10px">
|
||||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px"><div style="text-align:center;font-weight:700;font-size:.86rem;margin-bottom:4px">$p$–$V$ диаграмма</div><svg id="p6-iv1-pv" viewBox="0 0 380 260" width="100%" style="height:auto"></svg></div>
|
||||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px"><div style="text-align:center;font-weight:700;font-size:.86rem;margin-bottom:4px">$V$–$T$ диаграмма</div><svg id="p6-iv1-vt" viewBox="0 0 380 260" width="100%" style="height:auto"></svg></div>
|
||||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px"><div style="text-align:center;font-weight:700;font-size:.86rem;margin-bottom:4px">$p$–$T$ диаграмма</div><svg id="p6-iv1-pt" viewBox="0 0 380 260" width="100%" style="height:auto"></svg></div>
|
||||
</div>
|
||||
<div style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.93rem;line-height:1.85" id="p6-iv1-info"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — Калькулятор изопроцессов */
|
||||
html += `<div class="wg" id="p6-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор изопроцессов</div></div>
|
||||
<div class="wg-help">Выбери тип процесса, введи $p_1, V_1, T_1$ и одну величину состояния 2 — получи недостающие.</div>
|
||||
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p6-iv2-tabs">
|
||||
<button class="btn primary" data-mode="iso" style="background:#ea580c;border-color:#ea580c">Изотермический</button>
|
||||
<button class="btn" data-mode="bar">Изобарный</button>
|
||||
<button class="btn" data-mode="hor">Изохорный</button>
|
||||
</div>
|
||||
<div id="p6-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-bottom:10px"></div>
|
||||
<div class="actions" style="justify-content:center">
|
||||
<button class="btn primary" id="p6-iv2-go">Вычислить</button>
|
||||
</div>
|
||||
<div id="p6-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:80px;line-height:1.85"></div>
|
||||
<div class="feedback" id="p6-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — DnD: какой процесс */
|
||||
html += `<div class="wg" id="p6-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какой процесс?</div></div>
|
||||
<div class="wg-help">Перетащи 6 ситуаций в нужный процесс.</div>
|
||||
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 6 ситуаций — 3 процесса</div>
|
||||
<div id="p6-iv3-pool"></div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-top:8px">
|
||||
<div class="drop-box"><h5 data-cat="iso" style="color:#ea580c">Изотермический</h5><div class="drop-items" data-cat="iso"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="bar" style="color:#2563eb">Изобарный</h5><div class="drop-items" data-cat="bar"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="hor" style="color:#10b981">Изохорный</h5><div class="drop-items" data-cat="hor"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p6-iv3-check">Проверить</button><button class="btn" id="p6-iv3-reset">Сначала</button></div>
|
||||
<div class="feedback" id="p6-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — Тренажёр изопроцессов */
|
||||
html += `<div class="wg" id="p6-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр изопроцессов</div></div>
|
||||
<div class="wg-help">6 задач. Допуск $\\pm 5\\%$.</div>
|
||||
<div class="score-display"><span>Задача <b id="p6-iv4-i">1</b> / 6</span><span>Очки: <b id="p6-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p6-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
|
||||
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
|
||||
<input type="number" id="p6-iv4-ans" class="tinp" style="width:130px;text-align:center" step="any">
|
||||
<button class="btn primary" id="p6-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p6-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p6-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p5', 'p7');
|
||||
html += readButton('p6');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Графики изопроцессов */
|
||||
(function(){
|
||||
const R = 8.314;
|
||||
const NU = 1; // 1 моль для расчётов
|
||||
const pvSvg = document.getElementById('p6-iv1-pv');
|
||||
const vtSvg = document.getElementById('p6-iv1-vt');
|
||||
const ptSvg = document.getElementById('p6-iv1-pt');
|
||||
let paramInpRef = document.getElementById('p6-iv1-param');
|
||||
let paramLabRef = document.getElementById('p6-iv1-pL');
|
||||
const paramLbl = document.getElementById('p6-iv1-paramLbl');
|
||||
const info = document.getElementById('p6-iv1-info');
|
||||
const COL = { iso:'#ea580c', bar:'#2563eb', hor:'#10b981' };
|
||||
const seen = new Set();
|
||||
let _xpDone = false;
|
||||
|
||||
function getMode(){ const r = document.querySelector('input[name="p6-iv1-proc"]:checked'); return r ? r.value : 'iso'; }
|
||||
|
||||
function paramConfig(mode){
|
||||
if(mode === 'iso') return { label:'Температура $T$', unit:'К', min:200, max:600, step:10, val:300, set:[250, 400, 550] };
|
||||
if(mode === 'bar') return { label:'Давление $p$', unit:'атм', min:0.5, max:3, step:0.1, val:1, set:[0.7, 1.5, 2.5] };
|
||||
return { label:'Объём $V$', unit:'л', min:5, max:20, step:0.5, val:10, set:[6, 12, 18] };
|
||||
}
|
||||
|
||||
function applyParam(mode){
|
||||
const cfg = paramConfig(mode);
|
||||
// Перестраиваем label целиком, чтобы корректно работал и до, и после KaTeX-рендера
|
||||
paramLbl.innerHTML = cfg.label + ': <b id="p6-iv1-pL">'+cfg.val+'</b> '+cfg.unit+' <input type="range" id="p6-iv1-param" min="'+cfg.min+'" max="'+cfg.max+'" value="'+cfg.val+'" step="'+cfg.step+'">';
|
||||
renderMath(paramLbl);
|
||||
// Обновляем ссылки на пересозданные элементы
|
||||
paramInpRef = document.getElementById('p6-iv1-param');
|
||||
paramLabRef = document.getElementById('p6-iv1-pL');
|
||||
paramInpRef.addEventListener('input', render);
|
||||
}
|
||||
|
||||
|
||||
// Диаграмма p-V: x ∈ [1, 25] л, y ∈ [0, 4] атм
|
||||
function plotPV(active, others, activeVal){
|
||||
const W=380, H=260, pad=34;
|
||||
const a = axes2D(W, H, pad, 0, 25, 0, 4);
|
||||
let g = a.content;
|
||||
g += '<text x="'+(W-12)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">V, л</text>';
|
||||
g += '<text x="'+(pad-10)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">p, атм</text>';
|
||||
|
||||
function drawIsotherm(T, color, opacity){
|
||||
// p (атм) = ν R T / V(м³) / 10^5; для 1 моль: p = 8.314*T / (V_л * 10^-3 * 10^5) = 8.314*T/(V_л*100)
|
||||
const f = V => (NU * R * T) / (V * 1e-3) / 1e5;
|
||||
return '<g opacity="'+opacity+'">'+plotFunc(f, 0.5, 25, a.toX, a.toY, color, 220)+'</g>';
|
||||
}
|
||||
function drawIsobar(p, color, opacity){
|
||||
// горизонтальная прямая p = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(0)+'" y1="'+a.toY(p)+'" x2="'+a.toX(25)+'" y2="'+a.toY(p)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
function drawIsohor(V, color, opacity){
|
||||
// вертикальная прямая V = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(V)+'" y1="'+a.toY(0)+'" x2="'+a.toX(V)+'" y2="'+a.toY(4)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
others.forEach(v => {
|
||||
if(active === 'iso') g += drawIsotherm(v, COL.iso, 0.25);
|
||||
else if(active === 'bar') g += drawIsobar(v, COL.bar, 0.25);
|
||||
else g += drawIsohor(v, COL.hor, 0.25);
|
||||
});
|
||||
if(active === 'iso') g += drawIsotherm(activeVal, COL.iso, 1);
|
||||
else if(active === 'bar') g += drawIsobar(activeVal, COL.bar, 1);
|
||||
else g += drawIsohor(activeVal, COL.hor, 1);
|
||||
pvSvg.innerHTML = g;
|
||||
}
|
||||
|
||||
// Диаграмма V-T: x ∈ [0, 600] К, y ∈ [0, 25] л
|
||||
function plotVT(active, others, activeVal){
|
||||
const W=380, H=260, pad=34;
|
||||
const a = axes2D(W, H, pad, 0, 600, 0, 25);
|
||||
let g = a.content;
|
||||
g += '<text x="'+(W-12)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">T, К</text>';
|
||||
g += '<text x="'+(pad-10)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">V, л</text>';
|
||||
|
||||
function drawIsotherm(T, color, opacity){
|
||||
// изотерма на V-T — вертикальная прямая T = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(T)+'" y1="'+a.toY(0)+'" x2="'+a.toX(T)+'" y2="'+a.toY(25)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
function drawIsobar(p, color, opacity){
|
||||
// V = (ν R / p) T, p в Па; V в л = ν R T / p_Па * 1000
|
||||
const pPa = p * 1e5;
|
||||
const k = NU * R / pPa * 1000; // V_л = k * T
|
||||
return '<g opacity="'+opacity+'">'+plotFunc(T => k * T, 0, 600, a.toX, a.toY, color, 50)+'</g>';
|
||||
}
|
||||
function drawIsohor(V, color, opacity){
|
||||
// изохора на V-T — горизонтальная прямая V = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(0)+'" y1="'+a.toY(V)+'" x2="'+a.toX(600)+'" y2="'+a.toY(V)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
others.forEach(v => {
|
||||
if(active === 'iso') g += drawIsotherm(v, COL.iso, 0.25);
|
||||
else if(active === 'bar') g += drawIsobar(v, COL.bar, 0.25);
|
||||
else g += drawIsohor(v, COL.hor, 0.25);
|
||||
});
|
||||
if(active === 'iso') g += drawIsotherm(activeVal, COL.iso, 1);
|
||||
else if(active === 'bar') g += drawIsobar(activeVal, COL.bar, 1);
|
||||
else g += drawIsohor(activeVal, COL.hor, 1);
|
||||
vtSvg.innerHTML = g;
|
||||
}
|
||||
|
||||
// Диаграмма p-T: x ∈ [0, 600] К, y ∈ [0, 4] атм
|
||||
function plotPT(active, others, activeVal){
|
||||
const W=380, H=260, pad=34;
|
||||
const a = axes2D(W, H, pad, 0, 600, 0, 4);
|
||||
let g = a.content;
|
||||
g += '<text x="'+(W-12)+'" y="'+(H-pad+14)+'" font-size="10" fill="#0f172a">T, К</text>';
|
||||
g += '<text x="'+(pad-10)+'" y="'+(pad-6)+'" font-size="10" fill="#0f172a">p, атм</text>';
|
||||
|
||||
function drawIsotherm(T, color, opacity){
|
||||
// на p-T — вертикальная прямая T = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(T)+'" y1="'+a.toY(0)+'" x2="'+a.toX(T)+'" y2="'+a.toY(4)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
function drawIsobar(p, color, opacity){
|
||||
// на p-T — горизонтальная прямая p = const
|
||||
return '<g opacity="'+opacity+'"><line x1="'+a.toX(0)+'" y1="'+a.toY(p)+'" x2="'+a.toX(600)+'" y2="'+a.toY(p)+'" stroke="'+color+'" stroke-width="2"/></g>';
|
||||
}
|
||||
function drawIsohor(V, color, opacity){
|
||||
// p = (ν R / V) T, V в м³; p в атм = ν R T / V_м³ / 10^5
|
||||
const Vm3 = V * 1e-3;
|
||||
const k = NU * R / Vm3 / 1e5; // p_атм = k * T
|
||||
return '<g opacity="'+opacity+'">'+plotFunc(T => k * T, 0, 600, a.toX, a.toY, color, 50)+'</g>';
|
||||
}
|
||||
others.forEach(v => {
|
||||
if(active === 'iso') g += drawIsotherm(v, COL.iso, 0.25);
|
||||
else if(active === 'bar') g += drawIsobar(v, COL.bar, 0.25);
|
||||
else g += drawIsohor(v, COL.hor, 0.25);
|
||||
});
|
||||
if(active === 'iso') g += drawIsotherm(activeVal, COL.iso, 1);
|
||||
else if(active === 'bar') g += drawIsobar(activeVal, COL.bar, 1);
|
||||
else g += drawIsohor(activeVal, COL.hor, 1);
|
||||
ptSvg.innerHTML = g;
|
||||
}
|
||||
|
||||
function render(){
|
||||
const mode = getMode();
|
||||
const cfg = paramConfig(mode);
|
||||
const v = +paramInpRef.value;
|
||||
paramLabRef.textContent = (cfg.step < 1) ? v.toFixed(1) : v.toString();
|
||||
|
||||
plotPV(mode, cfg.set, v);
|
||||
plotVT(mode, cfg.set, v);
|
||||
plotPT(mode, cfg.set, v);
|
||||
|
||||
let infoHtml = '';
|
||||
if(mode === 'iso'){
|
||||
infoHtml = '<div><b style="color:#ea580c">Изотерма</b> ($T = '+v+'$ К): $pV = \\nu R T = '+(NU*R*v).toFixed(1)+'$ Дж. На $p$–$V$ — гипербола.</div>';
|
||||
} else if(mode === 'bar'){
|
||||
infoHtml = '<div><b style="color:#2563eb">Изобара</b> ($p = '+v.toFixed(1)+'$ атм): $V/T = \\nu R / p = $ const. На $V$–$T$ — прямая через 0.</div>';
|
||||
} else {
|
||||
infoHtml = '<div><b style="color:#10b981">Изохора</b> ($V = '+v.toFixed(1)+'$ л): $p/T = \\nu R / V = $ const. На $p$–$T$ — прямая через 0.</div>';
|
||||
}
|
||||
info.innerHTML = infoHtml;
|
||||
renderMath(info);
|
||||
|
||||
seen.add(mode + ':' + Math.round(v*10));
|
||||
if(!_xpDone && seen.size >= 5){ _xpDone = true; addXp(10, 'p6-iv1'); bumpProgress('p6', 15); }
|
||||
}
|
||||
|
||||
document.querySelectorAll('input[name="p6-iv1-proc"]').forEach(r => r.addEventListener('change', () => { applyParam(getMode()); render(); }));
|
||||
applyParam('iso');
|
||||
render();
|
||||
})();
|
||||
|
||||
/* IV2 — Калькулятор изопроцессов */
|
||||
(function(){
|
||||
const tabs = document.getElementById('p6-iv2-tabs');
|
||||
const inpsBox = document.getElementById('p6-iv2-inputs');
|
||||
const out = document.getElementById('p6-iv2-out');
|
||||
const fb = document.getElementById('p6-iv2-fb');
|
||||
const go = document.getElementById('p6-iv2-go');
|
||||
const used = new Set();
|
||||
let _done = false;
|
||||
let mode = 'iso';
|
||||
|
||||
function fieldHTML(id, label, val){
|
||||
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
|
||||
}
|
||||
function build(){
|
||||
let h = '';
|
||||
if(mode === 'iso'){
|
||||
h += fieldHTML('p6-iv2-p1', '$p_1$, атм', '1');
|
||||
h += fieldHTML('p6-iv2-V1', '$V_1$, л', '10');
|
||||
h += fieldHTML('p6-iv2-p2', '$p_2$, атм', '2');
|
||||
} else if(mode === 'bar'){
|
||||
h += fieldHTML('p6-iv2-V1', '$V_1$, л', '5');
|
||||
h += fieldHTML('p6-iv2-T1', '$T_1$, К', '250');
|
||||
h += fieldHTML('p6-iv2-T2', '$T_2$, К', '400');
|
||||
} else {
|
||||
h += fieldHTML('p6-iv2-p1', '$p_1$, атм', '2');
|
||||
h += fieldHTML('p6-iv2-T1', '$T_1$, К', '290');
|
||||
h += fieldHTML('p6-iv2-T2', '$T_2$, К', '348');
|
||||
}
|
||||
inpsBox.innerHTML = h;
|
||||
renderMath(inpsBox);
|
||||
out.innerHTML = '';
|
||||
fb.style.display = 'none';
|
||||
}
|
||||
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
|
||||
function calc(){
|
||||
let res = '', val = 0, unit = '';
|
||||
if(mode === 'iso'){
|
||||
const p1 = num('p6-iv2-p1'), V1 = num('p6-iv2-V1'), p2 = num('p6-iv2-p2');
|
||||
if(![p1,V1,p2].every(x => isFinite(x) && x>0)){ feedback(fb,false,'✗ Все значения должны быть положительными.'); return; }
|
||||
val = p1 * V1 / p2; unit = 'л';
|
||||
res = '$p_1 V_1 = p_2 V_2 \\Rightarrow V_2 = \\dfrac{p_1 V_1}{p_2} = \\dfrac{'+p1+' \\cdot '+V1+'}{'+p2+'} \\approx '+val.toFixed(3)+'$ л';
|
||||
} else if(mode === 'bar'){
|
||||
const V1 = num('p6-iv2-V1'), T1 = num('p6-iv2-T1'), T2 = num('p6-iv2-T2');
|
||||
if(![V1,T1,T2].every(x => isFinite(x) && x>0)){ feedback(fb,false,'✗ Все значения должны быть положительными.'); return; }
|
||||
val = V1 * T2 / T1; unit = 'л';
|
||||
res = '$\\dfrac{V_1}{T_1} = \\dfrac{V_2}{T_2} \\Rightarrow V_2 = \\dfrac{V_1 T_2}{T_1} = \\dfrac{'+V1+' \\cdot '+T2+'}{'+T1+'} \\approx '+val.toFixed(3)+'$ л';
|
||||
} else {
|
||||
const p1 = num('p6-iv2-p1'), T1 = num('p6-iv2-T1'), T2 = num('p6-iv2-T2');
|
||||
if(![p1,T1,T2].every(x => isFinite(x) && x>0)){ feedback(fb,false,'✗ Все значения должны быть положительными.'); return; }
|
||||
val = p1 * T2 / T1; unit = 'атм';
|
||||
res = '$\\dfrac{p_1}{T_1} = \\dfrac{p_2}{T_2} \\Rightarrow p_2 = \\dfrac{p_1 T_2}{T_1} = \\dfrac{'+p1+' \\cdot '+T2+'}{'+T1+'} \\approx '+val.toFixed(3)+'$ атм';
|
||||
}
|
||||
const lawName = mode==='iso' ? 'Бойля–Мариотта' : (mode==='bar' ? 'Гей-Люссака' : 'Шарля');
|
||||
out.innerHTML = '<div style="margin-bottom:8px"><b>Закон '+lawName+':</b></div>'
|
||||
+ '<div style="margin-bottom:8px">'+res+'</div>'
|
||||
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2)">'+(+val.toFixed(3))+' '+unit+'</span></div>';
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ Вычислено.');
|
||||
used.add(mode);
|
||||
if(!_done && used.size === 3){ _done = true; addXp(10, 'p6-iv2'); bumpProgress('p6', 15); }
|
||||
}
|
||||
tabs.querySelectorAll('button').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
mode = b.dataset.mode;
|
||||
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; x.style.background=''; x.style.borderColor=''; });
|
||||
b.className = 'btn primary';
|
||||
const colMap = { iso:'#ea580c', bar:'#2563eb', hor:'#10b981' };
|
||||
b.style.background = colMap[mode]; b.style.borderColor = colMap[mode];
|
||||
build();
|
||||
});
|
||||
});
|
||||
go.addEventListener('click', calc);
|
||||
build();
|
||||
})();
|
||||
|
||||
/* IV3 — DnD: какой процесс */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'s1', cat:'iso', html:'Газ медленно сжимают в баллоне при контакте с термостатом' },
|
||||
{ id:'s2', cat:'bar', html:'Газ нагревают в открытом цилиндре с подвижным поршнем' },
|
||||
{ id:'s3', cat:'hor', html:'Газ в герметичном баллоне нагревают электронагревателем' },
|
||||
{ id:'s4', cat:'hor', html:'Шину автомобиля надули зимой — летом давление в ней растёт' },
|
||||
{ id:'s5', cat:'iso', html:'Воздушный шарик медленно сжимают рукой' },
|
||||
{ id:'s6', cat:'bar', html:'В цилиндре с поршнем над газом — атмосфера; газ нагревают, поршень выдвигается' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p6-iv3-pool',
|
||||
scopeSelector:'#p6-iv3',
|
||||
items: items,
|
||||
cats:['iso','bar','hor'],
|
||||
columnLayout:false,
|
||||
});
|
||||
document.getElementById('p6-iv3-check').addEventListener('click', () => {
|
||||
const fb = document.getElementById('p6-iv3-fb');
|
||||
const placedCount = items.filter(it => sorter.placed[it.id]).length;
|
||||
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
|
||||
if(placedCount < items.length){ feedback(fb, false, '✗ Размести все 6 ситуаций.'); return; }
|
||||
if(correct === items.length){ feedback(fb, true, '✓ Все 6 верно! +10 XP'); addXp(10,'p6-iv3'); bumpProgress('p6', 15); }
|
||||
else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
|
||||
});
|
||||
document.getElementById('p6-iv3-reset').addEventListener('click', () => { sorter.reset(); document.getElementById('p6-iv3-fb').style.display = 'none'; });
|
||||
})();
|
||||
|
||||
/* IV4 — Тренажёр изопроцессов */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'Изотермически газ сжали от $V_1 = 10$ л до $V_2 = 4$ л. Во сколько раз изменилось давление?', ans:2.5, hint:'$p_2/p_1 = V_1/V_2 = 10/4 = 2{,}5$' },
|
||||
{ q:'Изобарно газ нагрели от $T_1 = 200$ К до $T_2 = 500$ К. Во сколько раз изменился объём?', ans:2.5, hint:'$V_2/V_1 = T_2/T_1 = 500/200 = 2{,}5$' },
|
||||
{ q:'Изохорно газ нагрели от $T_1 = 300$ К до $T_2 = 600$ К. Во сколько раз изменилось $p$?', ans:2, hint:'$p_2/p_1 = T_2/T_1 = 600/300 = 2$' },
|
||||
{ q:'В шине $p_1 = 2$ атм при $T_1 = 290$ К. Летом $T_2 = 348$ К. Найти $p_2$ в атм.', ans:2.4, hint:'$p_2 = p_1 T_2/T_1 = 2 \\cdot 348/290 \\approx 2{,}4$' },
|
||||
{ q:'В цилиндре с поршнем $V_1 = 5$ л при $T_1 = 250$ К. Изобарный нагрев до $T_2 = 400$ К. $V_2 = ?$ л', ans:8, hint:'$V_2 = V_1 T_2/T_1 = 5 \\cdot 400/250 = 8$' },
|
||||
{ q:'$p_1 = 4$ атм. Изотермически давление уменьшили до $p_2 = 1$ атм. Во сколько раз увеличился $V$?', ans:4, hint:'$V_2/V_1 = p_1/p_2 = 4/1 = 4$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p6-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15, 'p6-iv4'); bumpProgress('p6', 25); }
|
||||
else if(score >= 4){ addXp(8, 'p6-iv4'); bumpProgress('p6', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p6-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p6-iv4-s').textContent = score;
|
||||
document.getElementById('p6-iv4-q').innerHTML = Q[i].q;
|
||||
document.getElementById('p6-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p6-iv4-q'));
|
||||
document.getElementById('p6-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p6-iv4-fb');
|
||||
const raw = document.getElementById('p6-iv4-ans').value.replace(',', '.');
|
||||
const ans = parseFloat(raw);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
const tol = Math.max(0.05 * Math.abs(Q[i].ans), 0.05);
|
||||
if(Math.abs(ans - Q[i].ans) < tol + 0.001){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
|
||||
document.getElementById('p6-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1800);
|
||||
}
|
||||
document.getElementById('p6-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p6-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p6-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p6');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user