639f985e6f
Подключён 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>
160 lines
12 KiB
JavaScript
160 lines
12 KiB
JavaScript
/* 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,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||
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);
|