feat(phys10 ch1 wave1): §1 «МКТ» + §2 «Количество вещества» + симуляция броуновского движения
This commit is contained in:
@@ -670,34 +670,620 @@ function wireReadBtn(paraId){
|
||||
function build_p1(){
|
||||
const box = document.getElementById('p1-body');
|
||||
let html = '';
|
||||
|
||||
/* THEORY 1 — положения МКТ */
|
||||
html += makeCard('theory', "Основные положения МКТ", "§1", `
|
||||
<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>
|
||||
<p><b>Молекулярно-кинетическая теория</b> (МКТ) объясняет строение и свойства веществ на основе движения и взаимодействия частиц (атомов, молекул, ионов).</p>
|
||||
<p style="margin-top:10px"><b>Три основных положения МКТ:</b></p>
|
||||
<ol style="margin:8px 0 8px 22px;line-height:1.75">
|
||||
<li><b>Вещество состоит из частиц.</b> Молекулы и атомы имеют размер около $10^{-10}$ м.</li>
|
||||
<li><b>Частицы движутся непрерывно и хаотически.</b> Это движение называется <b>тепловым</b>.</li>
|
||||
<li><b>Частицы взаимодействуют между собой</b>: на малых расстояниях — отталкиваются, на больших — притягиваются.</li>
|
||||
</ol>
|
||||
<p>Все макроскопические свойства (температура, давление, плотность, теплопроводность и т. д.) объясняются микроскопическими процессами.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 2 — опытные подтверждения */
|
||||
html += makeCard('rule', "Опытные подтверждения МКТ", "§1", `
|
||||
<p><b>Броуновское движение</b> (Р. Броун, 1827): хаотическое движение мелких частиц (например, пыльцы в воде) под действием ударов молекул. Чем меньше частица и выше температура — тем интенсивнее движение.</p>
|
||||
<p style="margin-top:8px"><b>Диффузия</b> — самопроизвольное перемешивание веществ за счёт движения молекул. Скорость диффузии возрастает с температурой.</p>
|
||||
<p style="margin-top:8px"><b>Сжимаемость газов</b> и <b>тепловое расширение</b> тел — также прямое следствие движения и взаимодействия молекул.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 3 — размеры и числа */
|
||||
html += makeCard('example', "Размеры и количество молекул", "§1", `
|
||||
<ul style="margin:0 0 8px 22px;line-height:1.75">
|
||||
<li>Размер молекулы: $\\sim 10^{-10}$ м (нанометр или ангстрем).</li>
|
||||
<li>Размер атома водорода: $\\sim 0{,}5 \\cdot 10^{-10}$ м.</li>
|
||||
<li>В $1 \\text{ см}^3$ воды примерно $3{,}3 \\cdot 10^{22}$ молекул.</li>
|
||||
<li>В $1$ моль вещества — $N_A = 6{,}022 \\cdot 10^{23}$ молекул (число Авогадро).</li>
|
||||
</ul>
|
||||
<p><b>Пример:</b> если выпить стакан воды (200 мл) и подождать долгое время, в любом другом стакане воды через некоторое время будут несколько молекул из выпитого — настолько их много в любом веществе.</p>
|
||||
`);
|
||||
|
||||
/* INTERACTIVE 1 — Симуляция броуновского движения */
|
||||
html += `<div class="wg" id="p1-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Симуляция броуновского движения</div></div>
|
||||
<div class="wg-help">Оранжевая «броуновская» частица среди синих молекул газа. Меняй температуру — наблюдай хаотическое движение.</div>
|
||||
<div class="sliders">
|
||||
<label>Температура: <b id="p1-iv1-tL">300</b> К <input type="range" id="p1-iv1-T" min="100" max="500" value="300" step="10"></label>
|
||||
</div>
|
||||
<div style="background:#0f172a;border-radius:9px;padding:10px;text-align:center">
|
||||
<svg id="p1-iv1-svg" viewBox="0 0 420 320" width="100%" style="max-width:420px;height:auto;background:#0f172a;border-radius:6px"></svg>
|
||||
</div>
|
||||
<div class="actions" style="justify-content:center">
|
||||
<button class="btn primary" id="p1-iv1-pause">Пауза</button>
|
||||
<button class="btn" id="p1-iv1-reset">Сброс</button>
|
||||
</div>
|
||||
<div style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.88rem;line-height:1.5">
|
||||
Чем выше $T$, тем сильнее удары молекул, тем хаотичнее движется броуновская частица. Жёлтый след показывает её траекторию.
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — DnD сортер: положение МКТ ↔ опыт */
|
||||
html += `<div class="wg" id="p1-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Положение МКТ ↔ опыт</div></div>
|
||||
<div class="wg-help">Перетащи каждое явление в соответствующее положение МКТ.</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="p1-iv2-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="c1">1. Вещество из частиц</h5><div class="drop-items" data-cat="c1"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="c2">2. Частицы движутся</h5><div class="drop-items" data-cat="c2"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="c3">3. Частицы взаимодействуют</h5><div class="drop-items" data-cat="c3"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p1-iv2-check">Проверить</button><button class="btn" id="p1-iv2-reset">Сначала</button></div>
|
||||
<div class="feedback" id="p1-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — Квикфайр: Верно/Неверно */
|
||||
html += `<div class="wg" id="p1-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Проверь знания МКТ</div></div>
|
||||
<div class="wg-help">8 утверждений. Выбери «Верно» или «Неверно». Допуск ошибок — 1.</div>
|
||||
<div class="score-display"><span>Задача <b id="p1-iv3-i">1</b> / 8</span><span>Очки: <b id="p1-iv3-s">0</b> / 8</span></div>
|
||||
<div id="p1-iv3-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 id="p1-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||||
<div class="feedback" id="p1-iv3-fb"></div>
|
||||
<div class="actions"><button class="btn" id="p1-iv3-restart">Начать заново</button></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — тренажёр чисел */
|
||||
html += `<div class="wg" id="p1-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: размеры и числа</div></div>
|
||||
<div class="wg-help">5 задач на число Авогадро, размеры молекул, моли. Допуск $\\pm 5\\%$. Используй $N_A = 6 \\cdot 10^{23}$.</div>
|
||||
<div class="score-display"><span>Задача <b id="p1-iv4-i">1</b> / 5</span><span>Очки: <b id="p1-iv4-s">0</b> / 5</span></div>
|
||||
<div id="p1-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="p1-iv4-ans" class="tinp" style="width:130px;text-align:center" step="any">
|
||||
<button class="btn primary" id="p1-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p1-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p1-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav(null, 'p2');
|
||||
html += readButton('p1');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Броуновское движение */
|
||||
(function(){
|
||||
const svg = document.getElementById('p1-iv1-svg');
|
||||
const tInp = document.getElementById('p1-iv1-T');
|
||||
const tLab = document.getElementById('p1-iv1-tL');
|
||||
const btnPause = document.getElementById('p1-iv1-pause');
|
||||
const btnReset = document.getElementById('p1-iv1-reset');
|
||||
const W = 420, H = 320;
|
||||
const baseSpeed = 80;
|
||||
const tempChanges = new Set();
|
||||
let _xpDone = false;
|
||||
let raf = null, lastT = 0, paused = false;
|
||||
let sim = null;
|
||||
let brown = null;
|
||||
let trail = [];
|
||||
|
||||
function makeSim(){
|
||||
sim = PHYS.createGasSim({W, H, N: 50, speed: baseSpeed, r: 3});
|
||||
brown = { x: W/2, y: H/2, vx: 0, vy: 0, r: 12 };
|
||||
trail = [];
|
||||
// initial temp
|
||||
applyTemp(+tInp.value);
|
||||
}
|
||||
function applyTemp(T){
|
||||
// baseSpeed соответствует T0=300 К; scale = sqrt(T/T0)
|
||||
const targetScale = Math.sqrt(T/300);
|
||||
// компенсируем текущую среднюю скорость
|
||||
let curAvg = 0;
|
||||
for(const p of sim.particles){ curAvg += Math.hypot(p.vx, p.vy); }
|
||||
curAvg /= sim.particles.length;
|
||||
const targetAvg = baseSpeed * targetScale;
|
||||
if(curAvg > 0.01) sim.setSpeed(targetAvg / curAvg);
|
||||
}
|
||||
function frame(t){
|
||||
raf = requestAnimationFrame(frame);
|
||||
if(!lastT){ lastT = t; return; }
|
||||
let dt = (t - lastT) / 1000;
|
||||
lastT = t;
|
||||
if(paused){ render(); return; }
|
||||
if(dt > 0.06) dt = 0.06;
|
||||
sim.step(dt);
|
||||
|
||||
// обновляем броуновскую частицу: суммируем импульс от ближних молекул
|
||||
let fx = 0, fy = 0;
|
||||
const R = brown.r + 4;
|
||||
for(const p of sim.particles){
|
||||
const dx = brown.x - p.x, dy = brown.y - p.y;
|
||||
const d2 = dx*dx + dy*dy;
|
||||
if(d2 < R*R && d2 > 1){
|
||||
const d = Math.sqrt(d2);
|
||||
const f = (R - d) / R;
|
||||
fx += dx/d * f * 60;
|
||||
fy += dy/d * f * 60;
|
||||
}
|
||||
}
|
||||
// демпфирование + импульс
|
||||
brown.vx = brown.vx * 0.88 + fx * dt;
|
||||
brown.vy = brown.vy * 0.88 + fy * dt;
|
||||
brown.x += brown.vx * dt;
|
||||
brown.y += brown.vy * dt;
|
||||
// отражение от стенок
|
||||
if(brown.x < brown.r){ brown.x = brown.r; brown.vx = -brown.vx*0.6; }
|
||||
if(brown.x > W - brown.r){ brown.x = W - brown.r; brown.vx = -brown.vx*0.6; }
|
||||
if(brown.y < brown.r){ brown.y = brown.r; brown.vy = -brown.vy*0.6; }
|
||||
if(brown.y > H - brown.r){ brown.y = H - brown.r; brown.vy = -brown.vy*0.6; }
|
||||
|
||||
trail.push({x: brown.x, y: brown.y});
|
||||
if(trail.length > 60) trail.shift();
|
||||
render();
|
||||
}
|
||||
function render(){
|
||||
let g = '';
|
||||
// фон тёмный (уже в SVG), молекулы синие
|
||||
g += sim.render('#60a5fa');
|
||||
// след броуновской — polyline с убывающей opacity
|
||||
if(trail.length > 1){
|
||||
for(let i = 1; i < trail.length; i++){
|
||||
const op = i / trail.length;
|
||||
g += `<line x1="${trail[i-1].x.toFixed(1)}" y1="${trail[i-1].y.toFixed(1)}" x2="${trail[i].x.toFixed(1)}" y2="${trail[i].y.toFixed(1)}" stroke="#fbbf24" stroke-width="2" stroke-linecap="round" opacity="${op.toFixed(2)}"/>`;
|
||||
}
|
||||
}
|
||||
// броуновская частица — оранжевая
|
||||
g += `<circle cx="${brown.x.toFixed(1)}" cy="${brown.y.toFixed(1)}" r="${brown.r}" fill="#f97316" stroke="#fff" stroke-width="2"/>`;
|
||||
svg.innerHTML = g;
|
||||
}
|
||||
|
||||
makeSim();
|
||||
raf = requestAnimationFrame(frame);
|
||||
|
||||
tInp.addEventListener('input', () => {
|
||||
const T = +tInp.value;
|
||||
tLab.textContent = T;
|
||||
applyTemp(T);
|
||||
tempChanges.add(T);
|
||||
if(!_xpDone && tempChanges.size >= 4){
|
||||
_xpDone = true;
|
||||
addXp(10, 'p1-iv1');
|
||||
bumpProgress('p1', 15);
|
||||
}
|
||||
});
|
||||
btnPause.addEventListener('click', () => {
|
||||
paused = !paused;
|
||||
btnPause.textContent = paused ? 'Продолжить' : 'Пауза';
|
||||
});
|
||||
btnReset.addEventListener('click', () => {
|
||||
makeSim();
|
||||
trail = [];
|
||||
});
|
||||
// cleanup при потере фокуса (минимизируем нагрузку)
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if(document.hidden && raf){ cancelAnimationFrame(raf); raf = null; lastT = 0; }
|
||||
else if(!document.hidden && !raf){ raf = requestAnimationFrame(frame); }
|
||||
});
|
||||
})();
|
||||
|
||||
/* IV2 — DnD: положение ↔ опыт */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'q1', cat:'c2', html:'Запах духов распространяется в комнате' },
|
||||
{ id:'q2', cat:'c2', html:'Пыльца хаотично движется в воде' },
|
||||
{ id:'q3', cat:'c1', html:'Воздух сжимается в насосе' },
|
||||
{ id:'q4', cat:'c3', html:'Капля воды растекается по стеклу' },
|
||||
{ id:'q5', cat:'c1', html:'Стальной шарик падает в воде медленнее, чем в воздухе' },
|
||||
{ id:'q6', cat:'c2', html:'Газ заполняет весь сосуд' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p1-iv2-pool',
|
||||
scopeSelector:'#p1-iv2',
|
||||
items: items,
|
||||
cats:['c1','c2','c3'],
|
||||
columnLayout:false,
|
||||
});
|
||||
document.getElementById('p1-iv2-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p1-iv2-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,'p1-iv2'); bumpProgress('p1', 15); }
|
||||
else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
|
||||
});
|
||||
document.getElementById('p1-iv2-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p1-iv2-fb').style.display = 'none'; });
|
||||
})();
|
||||
|
||||
/* IV3 — квикфайр Верно/Неверно */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'Молекулы вещества движутся хаотически.', ans:true, why:'Тепловое движение — это хаотическое движение молекул.' },
|
||||
{ q:'При нагревании молекулы движутся медленнее.', ans:false, why:'Наоборот — при нагревании движение усиливается.' },
|
||||
{ q:'Атомы можно увидеть в обычный световой микроскоп.', ans:false, why:'Размер атома $\\sim 10^{-10}$ м — нужен электронный микроскоп.' },
|
||||
{ q:'Между молекулами действуют только силы притяжения.', ans:false, why:'На малых расстояниях — отталкивание, на больших — притяжение.' },
|
||||
{ q:'Размер молекулы порядка $10^{-10}$ м.', ans:true, why:'Это типичный размер молекулы (≈ 1 ангстрем).' },
|
||||
{ q:'Броуновское движение объясняется ударами молекул жидкости.', ans:true, why:'Хаотические удары молекул заставляют двигаться броуновские частицы.' },
|
||||
{ q:'Диффузия в газах происходит быстрее, чем в жидкостях.', ans:true, why:'В газах молекулы движутся свободнее и быстрее.' },
|
||||
{ q:'В 1 моль любого вещества одинаковое число молекул — $N_A$.', ans:true, why:'$N_A = 6{,}022 \\cdot 10^{23}$ — число Авогадро.' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
const qEl = document.getElementById('p1-iv3-q');
|
||||
const oEl = document.getElementById('p1-iv3-opts');
|
||||
const fb = document.getElementById('p1-iv3-fb');
|
||||
const iEl = document.getElementById('p1-iv3-i');
|
||||
const sEl = document.getElementById('p1-iv3-s');
|
||||
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
oEl.innerHTML = '';
|
||||
if(score === Q.length){ addXp(15, 'p1-iv3'); bumpProgress('p1', 25); }
|
||||
else if(score >= 5){ addXp(8, 'p1-iv3'); bumpProgress('p1', 15); }
|
||||
return;
|
||||
}
|
||||
iEl.textContent = (i+1);
|
||||
sEl.textContent = score;
|
||||
const item = Q[i];
|
||||
qEl.innerHTML = item.q;
|
||||
oEl.innerHTML = '<button class="btn primary" data-v="1">Верно</button><button class="btn primary" data-v="0" style="background:#ef4444;border-color:#ef4444">Неверно</button>';
|
||||
fb.style.display = 'none';
|
||||
renderMath(qEl);
|
||||
oEl.querySelectorAll('button').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
const v = b.dataset.v === '1';
|
||||
if(v === item.ans){ score++; feedback(fb, true, '✓ Верно! ' + item.why + ' Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. ' + item.why + ' Дальше ▶');
|
||||
sEl.textContent = score;
|
||||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||||
i++;
|
||||
setTimeout(show, 1400);
|
||||
});
|
||||
});
|
||||
}
|
||||
document.getElementById('p1-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — тренажёр чисел */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'В стакане воды $2{,}4 \\cdot 10^{24}$ молекул. Сколько это молей? ($N_A = 6 \\cdot 10^{23}$)', ans:4, hint:'$\\nu = N/N_A = 2{,}4 \\cdot 10^{24} / 6 \\cdot 10^{23} = 4$' },
|
||||
{ q:'В 18 г воды ($M = 18$ г/моль) сколько моль?', ans:1, hint:'$\\nu = m/M = 18/18 = 1$ моль' },
|
||||
{ q:'В 1 кг водорода ($M = 2$ г/моль) сколько моль?', ans:500, hint:'$\\nu = 1000/2 = 500$ моль' },
|
||||
{ q:'В 3 моль газа сколько молекул? Введи число $X$ для ответа $X \\cdot 10^{23}$', ans:18, hint:'$N = 3 \\cdot 6 \\cdot 10^{23} = 18 \\cdot 10^{23}$' },
|
||||
{ q:'Размер молекулы $\\sim 10^{-10}$ м. Сколько молекул в ряд поместится на $1$ мм? Введи порядок: $10^X$, $X = ?$', ans:7, hint:'$10^{-3}/10^{-10} = 10^{7}$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p1-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15, 'p1-iv4'); bumpProgress('p1', 25); }
|
||||
else if(score >= 3){ addXp(8, 'p1-iv4'); bumpProgress('p1', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p1-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p1-iv4-s').textContent = score;
|
||||
document.getElementById('p1-iv4-q').innerHTML = Q[i].q;
|
||||
document.getElementById('p1-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p1-iv4-q'));
|
||||
document.getElementById('p1-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p1-iv4-fb');
|
||||
const raw = document.getElementById('p1-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){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
|
||||
document.getElementById('p1-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1500);
|
||||
}
|
||||
document.getElementById('p1-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p1-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p1-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p1');
|
||||
}
|
||||
|
||||
function build_p2(){
|
||||
const box = document.getElementById('p2-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', "Масса и размеры молекул. Количество вещества", "§2", `
|
||||
<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', "Количество вещества и моль", "§2", `
|
||||
<p><b>Количество вещества</b> $\\nu$ (греч. «ню») — физическая величина, равная числу частиц вещества, делённому на число Авогадро:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\nu = \\dfrac{N}{N_A}$$</p>
|
||||
<p>Единица: <b>моль</b>. Один моль — количество вещества, содержащее столько же частиц, сколько атомов в $12$ г углерода-12.</p>
|
||||
<p style="margin-top:8px"><b>Число Авогадро:</b> $N_A = 6{,}022 \\cdot 10^{23}$ моль$^{-1}$.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 2 — молярная масса */
|
||||
html += makeCard('rule', "Молярная масса. Связь с массой", "§2", `
|
||||
<p><b>Молярная масса</b> $M$ — масса одного моля вещества:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$M = m_0 \\cdot N_A$$</p>
|
||||
<p>где $m_0$ — масса одной молекулы. Единица СИ — <b>кг/моль</b>, привычная — <b>г/моль</b>.</p>
|
||||
<p style="margin-top:8px"><b>Связь количества вещества с массой:</b></p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\nu = \\dfrac{m}{M} = \\dfrac{N}{N_A}$$</p>
|
||||
<p><b>Относительная молекулярная масса</b> $M_r$ — безразмерна: $M_r = M$ (в г/моль). Например, $M_r(\\text{H}_2\\text{O}) = 18$, $M(\\text{H}_2\\text{O}) = 18$ г/моль $= 0{,}018$ кг/моль.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 3 — масса молекулы */
|
||||
html += makeCard('example', "Масса одной молекулы и концентрация", "§2", `
|
||||
<p>Зная молярную массу и число Авогадро, найдём массу одной молекулы:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$m_0 = \\dfrac{M}{N_A}$$</p>
|
||||
<p><b>Примеры:</b></p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li>Молекула воды ($M = 18$ г/моль): $m_0 = \\dfrac{18 \\cdot 10^{-3}}{6{,}022 \\cdot 10^{23}} \\approx 3 \\cdot 10^{-26}$ кг.</li>
|
||||
<li>Атом водорода ($M = 1$ г/моль): $m_0 \\approx 1{,}66 \\cdot 10^{-27}$ кг (= 1 а.е.м.).</li>
|
||||
<li>Атом углерода ($M = 12$ г/моль): $m_0 \\approx 2 \\cdot 10^{-26}$ кг.</li>
|
||||
</ul>
|
||||
<p><b>Концентрация молекул</b> $n$ — число молекул в единице объёма:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$n = \\dfrac{N}{V}$$</p>
|
||||
<p>Единица: $\\text{м}^{-3}$.</p>
|
||||
`);
|
||||
|
||||
/* INTERACTIVE 1 — калькулятор Авогадро */
|
||||
html += `<div class="wg" id="p2-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Калькулятор Авогадро</div></div>
|
||||
<div class="wg-help">Введи массу $m$ (г) и молярную массу $M$ (г/моль) — получи количество вещества $\\nu$, число молекул $N$ и массу одной молекулы $m_0$.</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px">
|
||||
<label style="display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">Масса $m$, г <input type="number" id="p2-iv1-m" class="tinp" style="width:100%;margin-top:6px" value="18" step="any"></label>
|
||||
<label style="display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">Молярная масса $M$, г/моль <input type="number" id="p2-iv1-M" class="tinp" style="width:100%;margin-top:6px" value="18" step="any"></label>
|
||||
</div>
|
||||
<div style="margin-bottom:10px">
|
||||
<span style="font-size:.84rem;color:var(--muted);margin-right:6px">Эталон:</span>
|
||||
<select id="p2-iv1-preset" class="tinp" style="padding:6px 10px">
|
||||
<option value="">— выбери вещество —</option>
|
||||
<option value="18">Вода H₂O (M = 18)</option>
|
||||
<option value="44">Углекислый газ CO₂ (M = 44)</option>
|
||||
<option value="32">Кислород O₂ (M = 32)</option>
|
||||
<option value="28">Азот N₂ (M = 28)</option>
|
||||
<option value="2">Водород H₂ (M = 2)</option>
|
||||
<option value="56">Железо Fe (M = 56)</option>
|
||||
<option value="12">Углерод C (M = 12)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="actions" style="justify-content:center">
|
||||
<button class="btn primary" id="p2-iv1-go">Вычислить</button>
|
||||
</div>
|
||||
<div id="p2-iv1-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="p2-iv1-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — DnD сортер: вещество ↔ молярная масса */
|
||||
html += `<div class="wg" id="p2-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сопоставь массу с веществом</div></div>
|
||||
<div class="wg-help">Перетащи каждое вещество в ящик с его молярной массой.</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 веществ — 6 значений</div>
|
||||
<div id="p2-iv2-pool"></div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:8px">
|
||||
<div class="drop-box"><h5 data-cat="m2">2 г/моль</h5><div class="drop-items" data-cat="m2"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="m16">16 г/моль</h5><div class="drop-items" data-cat="m16"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="m18">18 г/моль</h5><div class="drop-items" data-cat="m18"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="m28">28 г/моль</h5><div class="drop-items" data-cat="m28"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="m32">32 г/моль</h5><div class="drop-items" data-cat="m32"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="m44">44 г/моль</h5><div class="drop-items" data-cat="m44"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p2-iv2-check">Проверить</button><button class="btn" id="p2-iv2-reset">Сначала</button></div>
|
||||
<div class="feedback" id="p2-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — квикфайр «Что больше?» */
|
||||
html += `<div class="wg" id="p2-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="score-display"><span>Задача <b id="p2-iv3-i">1</b> / 6</span><span>Очки: <b id="p2-iv3-s">0</b> / 6</span></div>
|
||||
<div id="p2-iv3-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 id="p2-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||||
<div class="feedback" id="p2-iv3-fb"></div>
|
||||
<div class="actions"><button class="btn" id="p2-iv3-restart">Начать заново</button></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — тренажёр количества вещества */
|
||||
html += `<div class="wg" id="p2-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: количество вещества</div></div>
|
||||
<div class="wg-help">6 задач на $\\nu = m/M$ и $N = \\nu N_A$. Допуск $\\pm 5\\%$. Используй $N_A = 6 \\cdot 10^{23}$.</div>
|
||||
<div class="score-display"><span>Задача <b id="p2-iv4-i">1</b> / 6</span><span>Очки: <b id="p2-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p2-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="p2-iv4-ans" class="tinp" style="width:130px;text-align:center" step="any">
|
||||
<button class="btn primary" id="p2-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p2-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p2-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p1', 'p3');
|
||||
html += readButton('p2');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — калькулятор Авогадро */
|
||||
(function(){
|
||||
const mI = document.getElementById('p2-iv1-m');
|
||||
const MI = document.getElementById('p2-iv1-M');
|
||||
const sel = document.getElementById('p2-iv1-preset');
|
||||
const out = document.getElementById('p2-iv1-out');
|
||||
const fb = document.getElementById('p2-iv1-fb');
|
||||
const go = document.getElementById('p2-iv1-go');
|
||||
const used = new Set();
|
||||
let _done = false;
|
||||
const NA = 6e23;
|
||||
|
||||
function calc(){
|
||||
const m = parseFloat(mI.value);
|
||||
const M = parseFloat(MI.value);
|
||||
if(!isFinite(m) || m <= 0){ feedback(fb, false, '✗ Введи положительную массу.'); return; }
|
||||
if(!isFinite(M) || M <= 0){ feedback(fb, false, '✗ Введи положительную молярную массу.'); return; }
|
||||
const nu = m / M;
|
||||
const N = nu * NA;
|
||||
// m0 в кг: m0 = M(кг/моль) / N_A = M*1e-3 / N_A
|
||||
const m0 = (M * 1e-3) / NA;
|
||||
const NstrExp = Math.floor(Math.log10(N));
|
||||
const NstrCoef = N / Math.pow(10, NstrExp);
|
||||
const m0Exp = Math.floor(Math.log10(m0));
|
||||
const m0Coef = m0 / Math.pow(10, m0Exp);
|
||||
|
||||
out.innerHTML =
|
||||
'<div style="margin-bottom:8px"><b>Количество вещества:</b> $\\nu = \\dfrac{m}{M} = \\dfrac{'+(+m.toFixed(3))+'}{'+(+M.toFixed(3))+'} \\approx '+(+nu.toFixed(4))+'$ моль</div>'
|
||||
+ '<div style="margin-bottom:8px"><b>Число молекул:</b> $N = \\nu \\cdot N_A = '+(+nu.toFixed(4))+' \\cdot 6 \\cdot 10^{23} \\approx '+(+NstrCoef.toFixed(3))+' \\cdot 10^{'+NstrExp+'}$</div>'
|
||||
+ '<div><b>Масса одной молекулы:</b> $m_0 = \\dfrac{M}{N_A} \\approx '+(+m0Coef.toFixed(3))+' \\cdot 10^{'+m0Exp+'}$ кг</div>';
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ Вычислено.');
|
||||
used.add((+M.toFixed(2))+':'+(+m.toFixed(2)));
|
||||
if(!_done && used.size >= 4){ _done = true; addXp(10, 'p2-iv1'); bumpProgress('p2', 15); }
|
||||
}
|
||||
sel.addEventListener('change', () => {
|
||||
if(sel.value){ MI.value = sel.value; }
|
||||
});
|
||||
go.addEventListener('click', calc);
|
||||
mI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
|
||||
MI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
|
||||
})();
|
||||
|
||||
/* IV2 — DnD: вещество ↔ молярная масса */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'w1', cat:'m18', html:'Вода H$_2$O' },
|
||||
{ id:'w2', cat:'m44', html:'Углекислый газ CO$_2$' },
|
||||
{ id:'w3', cat:'m32', html:'Кислород O$_2$' },
|
||||
{ id:'w4', cat:'m28', html:'Азот N$_2$' },
|
||||
{ id:'w5', cat:'m2', html:'Водород H$_2$' },
|
||||
{ id:'w6', cat:'m16', html:'Метан CH$_4$' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p2-iv2-pool',
|
||||
scopeSelector:'#p2-iv2',
|
||||
items: items,
|
||||
cats:['m2','m16','m18','m28','m32','m44'],
|
||||
columnLayout:false,
|
||||
});
|
||||
document.getElementById('p2-iv2-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p2-iv2-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,'p2-iv2'); bumpProgress('p2', 15); }
|
||||
else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
|
||||
});
|
||||
document.getElementById('p2-iv2-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p2-iv2-fb').style.display = 'none'; });
|
||||
})();
|
||||
|
||||
/* IV3 — квикфайр «Что больше?» */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'Что весит больше: $1$ моль воды (M=18) или $1$ моль водорода (M=2)?', a:'1 моль воды (18 г)', b:'1 моль водорода (2 г)', ans:0, why:'Молярная масса воды 18 г/моль > 2 г/моль.' },
|
||||
{ q:'Что больше по массе: $36$ г воды или $1$ моль воды (M=18)?', a:'36 г воды', b:'1 моль воды (18 г)', ans:0, why:'36 г = 2 моль воды > 1 моль (18 г).' },
|
||||
{ q:'Что весит больше: $N_A$ молекул кислорода (M=32) или $2 N_A$ молекул водорода (M=2)?', a:'$N_A$ молекул O$_2$ (32 г)', b:'$2 N_A$ молекул H$_2$ (4 г)', ans:0, why:'32 г > 4 г.' },
|
||||
{ q:'Что больше по массе: $1$ моль CO$_2$ (M=44) или $1$ кг воды?', a:'1 моль CO$_2$ (44 г)', b:'1 кг воды (1000 г)', ans:1, why:'1000 г > 44 г.' },
|
||||
{ q:'Что больше по числу частиц: $10^{23}$ молекул железа или $10^{22}$ молекул свинца?', a:'$10^{23}$ молекул Fe', b:'$10^{22}$ молекул Pb', ans:0, why:'$10^{23} > 10^{22}$ в 10 раз.' },
|
||||
{ q:'Что больше по массе: $5$ моль H$_2$ (M=2) или $1$ моль O$_2$ (M=32)?', a:'5 моль H$_2$ (10 г)', b:'1 моль O$_2$ (32 г)', ans:1, why:'$5 \\cdot 2 = 10$ г против $32$ г.' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
const qEl = document.getElementById('p2-iv3-q');
|
||||
const oEl = document.getElementById('p2-iv3-opts');
|
||||
const fb = document.getElementById('p2-iv3-fb');
|
||||
const iEl = document.getElementById('p2-iv3-i');
|
||||
const sEl = document.getElementById('p2-iv3-s');
|
||||
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
oEl.innerHTML = '';
|
||||
if(score === Q.length){ addXp(15, 'p2-iv3'); bumpProgress('p2', 25); }
|
||||
else if(score >= 4){ addXp(8, 'p2-iv3'); bumpProgress('p2', 15); }
|
||||
return;
|
||||
}
|
||||
iEl.textContent = (i+1);
|
||||
sEl.textContent = score;
|
||||
const item = Q[i];
|
||||
qEl.innerHTML = item.q;
|
||||
oEl.innerHTML = '<button class="btn primary" data-v="0">Первое: '+item.a+'</button><button class="btn primary" data-v="1">Второе: '+item.b+'</button>';
|
||||
fb.style.display = 'none';
|
||||
renderMath(qEl); renderMath(oEl);
|
||||
oEl.querySelectorAll('button').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
const v = +b.dataset.v;
|
||||
if(v === item.ans){ score++; feedback(fb, true, '✓ Верно! ' + item.why + ' Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. ' + item.why + ' Дальше ▶');
|
||||
sEl.textContent = score;
|
||||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||||
i++;
|
||||
setTimeout(show, 1500);
|
||||
});
|
||||
});
|
||||
}
|
||||
document.getElementById('p2-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — тренажёр количества вещества */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'В 36 г воды (M=18 г/моль) сколько моль?', ans:2, hint:'$\\nu = m/M = 36/18 = 2$ моль' },
|
||||
{ q:'В 88 г углекислого газа (M=44 г/моль) сколько моль?', ans:2, hint:'$\\nu = 88/44 = 2$ моль' },
|
||||
{ q:'Сколько молекул в 1 моль вещества? Введи показатель $X$ для $6 \\cdot 10^X$', ans:23, hint:'$N_A = 6 \\cdot 10^{23}$' },
|
||||
{ q:'M = 64 г/моль. Сколько моль в 32 г этого вещества?', ans:0.5, hint:'$\\nu = 32/64 = 0{,}5$ моль' },
|
||||
{ q:'Сколько грамм в 3 моль воды (M=18 г/моль)?', ans:54, hint:'$m = \\nu M = 3 \\cdot 18 = 54$ г' },
|
||||
{ q:'В 36 г воды сколько молекул? Введи $X$ для ответа $X \\cdot 10^{23}$', ans:12, hint:'$\\nu = 2$ моль, $N = 2 \\cdot 6 \\cdot 10^{23} = 12 \\cdot 10^{23}$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p2-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15, 'p2-iv4'); bumpProgress('p2', 25); }
|
||||
else if(score >= 4){ addXp(8, 'p2-iv4'); bumpProgress('p2', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p2-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p2-iv4-s').textContent = score;
|
||||
document.getElementById('p2-iv4-q').innerHTML = Q[i].q;
|
||||
document.getElementById('p2-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p2-iv4-q'));
|
||||
document.getElementById('p2-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p2-iv4-fb');
|
||||
const raw = document.getElementById('p2-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){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
|
||||
document.getElementById('p2-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1500);
|
||||
}
|
||||
document.getElementById('p2-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p2-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p2-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p2');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user