Files
Learn_System/frontend/js/chem7_ch4_widgets.js
Maxim Dolgolyov 639f985e6f feat(chemistry7): визуал V4 (Глава 4) — электролиз 2:1, индикаторы, титрование
Подключён chem7_anim.js в Главу 4.
- §23 (звёздный): электролиз воды — два потока пузырьков H₂ (18) и O₂ (9),
  наглядно 2:1;
- §24/ЛО5 индикаторы щёлочи: блок плавно меняет цвет (фенолфталеин → малиновый);
- §25/ПР4 нейтрализация (звёздный): раствор плавно обесцвечивается
  малиновый → бесцветный (colorBlock).

Все 4 главы анимированы. Тесты chem7: 16/16; полный прогон 162/165 (3 — baseline Auth).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 19:54:50 +03:00

160 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* chem7_ch4_widgets.js — интерактивы главы 4 «Вода» (Химия 7).
* Монтируются движком chem8_engine.js: window.CHEM8_WIDGETS[id].
* Используют window.Chem8 (chem8_svg.js): chemEq, formula.
* Без эмоджи; KaTeX — через window.chem8RenderMath.
*/
(function (W) {
'use strict';
function C() { return W.Chem8 || {}; }
function $(id) { return document.getElementById(id); }
function esc(s){ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function gcd(a, b) { return b ? gcd(b, a % b) : a; }
function ceq(src, opts){ return C().chemEq ? C().chemEq(src, opts || {}) : esc(src); }
function fml(s){ return C().formula ? C().formula(s) : s; }
function strip(color){ return '<div style="width:120px;height:32px;border-radius:8px;border:1.5px solid var(--border);background:'+color+';display:inline-block;vertical-align:middle"></div>'; }
/* §23 — разложение воды (2:1) + реакции воды */
var WRX = [
{ name:'Разложение электрическим током', eq:'2H2O = 2H2^ + O2^', cond:'эл. ток', note:'Вода разлагается на простые вещества: водорода получается вдвое больше по объёму, чем кислорода (2 : 1).' },
{ name:'Реакция с натрием', eq:'2Na + 2H2O = 2NaOH + H2^', note:'Активные металлы реагируют с водой, образуя щёлочь и водород.' },
{ name:'Реакция с оксидом кальция', eq:'CaO + H2O = Ca(OH)2', note:'Оксиды активных металлов с водой дают основания.' },
{ name:'Реакция с углекислым газом', eq:'CO2 + H2O = H2CO3', note:'Оксиды неметаллов с водой дают кислоты.' }
];
function decompSvg(){
// две перевёрнутые пробирки: H2 (заполнена на 2/2), O2 (на 1/2)
return '<svg viewBox="0 0 220 140" width="100%" style="max-width:240px">'
+ '<rect x="40" y="20" width="34" height="90" rx="6" fill="#dbeafe" stroke="#93c5fd"/>'
+ '<rect x="40" y="20" width="34" height="74" rx="6" fill="#bfdbfe"/>'
+ '<text x="57" y="125" text-anchor="middle" font-size="13" font-weight="700" fill="#1d4ed8">H₂</text>'
+ '<text x="57" y="58" text-anchor="middle" font-size="11" fill="#1e3a8a">2 V</text>'
+ '<rect x="146" y="20" width="34" height="90" rx="6" fill="#fee2e2" stroke="#fca5a5"/>'
+ '<rect x="146" y="65" width="34" height="45" rx="6" fill="#fecaca"/>'
+ '<text x="163" y="125" text-anchor="middle" font-size="13" font-weight="700" fill="#b91c1c">O₂</text>'
+ '<text x="163" y="90" text-anchor="middle" font-size="11" fill="#7f1d1d">1 V</text>'
+ '<rect x="20" y="110" width="180" height="12" rx="4" fill="#60a5fa"/>'
+ '</svg>';
}
function mount_p23() {
var m = $('p23-water'); if (!m || m._built) return; m._built = 1;
var idx = 0, anims = [];
function stopAnim(){ anims.forEach(function(a){ try { a.stop(); } catch(e){} }); anims = []; }
function render(){
stopAnim();
var r = WRX[idx];
m.innerHTML = (idx===0 ? decompSvg()
+ '<div style="display:flex;gap:10px;margin-top:6px"><div style="flex:1"><div style="text-align:center;font-size:.76rem;font-weight:700;color:#1d4ed8">H₂ — 2 объёма</div><div id="p23-bub-h"></div></div>'
+ '<div style="flex:1"><div style="text-align:center;font-size:.76rem;font-weight:700;color:#b91c1c">O₂ — 1 объём</div><div id="p23-bub-o"></div></div></div>' : '')
+ '<div class="fld"><label>Реакция воды</label><select id="p23-pick">'
+ WRX.map(function(x,i){ return '<option value="'+i+'"'+(i===idx?' selected':'')+'>'+esc(x.name)+'</option>'; }).join('') + '</select></div>'
+ '<div class="out ok" style="margin-top:8px"><div style="font-size:1.05rem">'+ceq(r.eq,{cond:r.cond})+'</div>'
+ '<div style="font-size:.86rem;color:var(--muted);margin-top:6px">'+esc(r.note)+'</div></div>';
if (idx===0 && W.Chem7Anim) {
anims.push(W.Chem7Anim.bubbleField($('p23-bub-h'), { color:'rgba(96,165,250,.9)', count:18, h:84, bg:'linear-gradient(180deg,#dbeafe,transparent)' }));
anims.push(W.Chem7Anim.bubbleField($('p23-bub-o'), { color:'rgba(248,113,113,.9)', count:9, h:84, bg:'linear-gradient(180deg,#fee2e2,transparent)' }));
}
$('p23-pick').addEventListener('change', function(e){ idx=+e.target.value; render(); });
}
render();
}
/* индикаторы в щёлочи */
var ALK_IND = {
'Лакмус': { neutral:['#7c3aed','фиолетовый'], alk:['#2563eb','синий'] },
'Фенолфталеин': { neutral:['#f3f4f6','бесцветный'], alk:['#db2777','малиновый'] },
'Метилоранж': { neutral:['#f59e0b','оранжевый'], alk:['#eab308','жёлтый'] }
};
function alkIndicator(mountId) {
var m = $(mountId); if (!m || m._built) return; m._built = 1;
var ind = 'Фенолфталеин', anim = null;
function render(){
if (anim) { anim.stop(); anim = null; }
var c = ALK_IND[ind];
m.innerHTML = '<div class="fld"><label>Индикатор</label><select id="'+mountId+'-sel">'
+ Object.keys(ALK_IND).map(function(k){ return '<option'+(k===ind?' selected':'')+'>'+k+'</option>'; }).join('') + '</select></div>'
+ '<div id="'+mountId+'-drop" style="margin-top:8px"></div>'
+ '<div class="out ok" style="margin-top:8px">В нейтральной среде: ' + strip(c.neutral[0]) + ' <b>'+c.neutral[1]+'</b><br>'
+ 'В щёлочи: ' + strip(c.alk[0]) + ' <b>'+c.alk[1]+'</b></div>';
if (W.Chem7Anim) anim = W.Chem7Anim.colorBlock($(mountId+'-drop'), c.neutral[0], c.alk[0], ind + ' в щёлочи → ' + c.alk[1], 900);
$(mountId+'-sel').addEventListener('change', function(e){ ind=e.target.value; render(); });
}
render();
}
/* §24 — конструктор оснований Me(OH)n + индикаторы */
var BM = [ ['Na',1], ['K',1], ['Ca',2], ['Mg',2], ['Cu',2], ['Al',3], ['Fe',3] ];
var SOLUBLE = { Na:1, K:1, Ca:1 };
function mount_p24() {
var b = $('p24-bld');
if (b && !b._built) { b._built = 1;
function rom(n){ return ['','I','II','III'][n]; }
b.innerHTML = '<div class="fld"><label>Металл</label><select id="p24-m">'
+ BM.map(function(e,i){ return '<option value="'+i+'"'+(e[0]==='Na'?' selected':'')+'>'+e[0]+' ('+rom(e[1])+')</option>'; }).join('') + '</select> + гидроксогруппа OH (I)</div><div class="out" id="p24-out"></div>';
function upd(){
var e=BM[+$('p24-m').value], n=e[1];
var raw = e[0] + (n>1 ? '(OH)'+n : 'OH');
var sol = SOLUBLE[e[0]] ? 'щёлочь (растворимое основание)' : 'нерастворимое основание';
var out=$('p24-out'); out.className='out ok';
out.innerHTML='<span class="bd">Валентность '+e[0]+' = '+rom(n)+', OH = I → '+n+' группы OH<br>Формула основания: <b style="font-size:1.15rem">'+fml(raw)+'</b><br>Это '+sol+'.</span>';
}
$('p24-m').addEventListener('change',upd); upd();
}
alkIndicator('p24-ind');
}
function mount_lo5() { alkIndicator('lo5-ind'); }
/* §25 / ПР4 — нейтрализация (фенолфталеин малиновый → бесцветный) */
function mount_neutral(mountId) {
var m = $(mountId); if (!m || m._built) return; m._built = 1;
var done = false, anim = null;
function render(){
if (anim) { anim.stop(); anim = null; }
m.innerHTML = '<div id="'+mountId+'-cup" style="margin-bottom:8px"></div>'
+ '<div style="font-size:.92rem">'+(done
? 'Раствор стал <b>бесцветным</b> — кислота нейтрализовала щёлочь. Реакция завершена.'
: 'В щёлочи с фенолфталеином раствор <b>малиновый</b>. Добавляй кислоту по каплям.')+'</div>'
+ '<div class="fld" style="margin-top:8px"><button class="btn primary" id="'+mountId+'-go">'+(done?'Сбросить':'Добавить кислоту')+'</button></div>'
+ (done ? '<div class="out ok" style="margin-top:8px"><div style="font-size:1.05rem">'+ceq('HCl + NaOH = NaCl + H2O')+'</div><div style="font-size:.84rem;color:var(--muted);margin-top:4px">Кислота + основание → соль + вода. Это реакция <b>нейтрализации</b>.</div></div>' : '');
if (W.Chem7Anim) anim = done
? W.Chem7Anim.colorBlock($(mountId+'-cup'), '#db2777', '#f8fafc', 'малиновый → бесцветный', 1600)
: W.Chem7Anim.colorBlock($(mountId+'-cup'), '#db2777', '#db2777', 'щёлочь + фенолфталеин', 1);
$(mountId+'-go').addEventListener('click', function(){ done=!done; render(); });
}
render();
}
function mount_p25() { mount_neutral('p25-neu'); }
function mount_pr4() { mount_neutral('pr4-neu'); }
/* §26 — охрана воды и воздуха: источники загрязнения и способы охраны */
var ECO = {
'Источники загрязнения': [
['Промышленные выбросы','Газы и пыль из труб заводов загрязняют воздух.'],
['Сточные воды','Неочищенные стоки отравляют реки и озёра.'],
['Нефть','Разливы нефти губят водные организмы.'],
['Кислотные дожди','Оксиды серы и азота в воздухе образуют кислоты, выпадающие с дождём.']
],
'Способы охраны и очистки': [
['Очистные сооружения','Сточные воды очищают перед сбросом.'],
['Фильтрование','На водопроводных станциях удаляют твёрдые частицы.'],
['Хлорирование и озонирование','Обеззараживают питьевую воду.'],
['Бережное отношение','Экономить воду и не загрязнять водоёмы.']
]
};
function mount_p26() {
var m = $('p26-eco'); if (!m || m._built) return; m._built = 1;
var cols = Object.keys(ECO).map(function(title){
var items = ECO[title].map(function(it,i){ return '<button class="eco-it btn" data-t="'+esc(title)+'" data-i="'+i+'" style="display:block;width:100%;text-align:left;margin:4px 0">'+esc(it[0])+'</button>'; }).join('');
return '<div style="flex:1;min-width:200px"><div style="font-weight:700;margin-bottom:6px">'+esc(title)+'</div>'+items+'</div>';
}).join('');
m.innerHTML = '<div style="display:flex;gap:14px;flex-wrap:wrap">'+cols+'</div><div class="out" id="p26-eco-out" style="margin-top:8px">Кликни по пункту, чтобы узнать подробнее.</div>';
var out=$('p26-eco-out');
m.querySelectorAll('.eco-it').forEach(function(b){
b.addEventListener('click', function(){ var it=ECO[b.dataset.t][+b.dataset.i]; out.className='out ok'; out.innerHTML='<b>'+esc(it[0])+'.</b> '+esc(it[1]); });
});
}
W.CHEM8_WIDGETS = Object.assign(W.CHEM8_WIDGETS || {}, {
p23: mount_p23, p24: mount_p24, lo5: mount_lo5, p25: mount_p25, pr4: mount_pr4, p26: mount_p26
});
W.FLAG_MOUNTS = Object.assign(W.FLAG_MOUNTS || {}, {});
})(window);