feat(chemistry7): визуал V3 (Глава 3) — пузырьки, морфинг цвета, индикаторы
Подключён chem7_anim.js в Главу 3. - §21 ряд активности (звёздный): клик металла левее H₂ → анимация пузырьков H₂ (bubbleField); правее (Cu, Ag) — «реакция не идёт»; - §19 восстановление CuO: colorBlock плавно чёрный→красный (медь); горение — пламя водорода; - §20/ЛО3 индикаторы: блок плавно меняет цвет на цвет индикатора в кислоте. Тесты chem7: 16/16; полный прогон 162/165 (3 — baseline Auth). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -194,8 +194,10 @@ test('ch3 Волна 1: §18 + §19 + §20 + ЛО3 монтируются', asyn
|
||||
assert.ok(doc.querySelector('#p18-card svg'), 'паспорт водорода §18');
|
||||
doc.defaultView.goTo('p19'); await wait(100);
|
||||
assert.ok(doc.querySelector('#p19-rx #p19-pick'), 'реакции водорода §19');
|
||||
assert.ok(doc.querySelector('#p19-stage div'), 'анимация реакции §19');
|
||||
doc.defaultView.goTo('p20'); await wait(100);
|
||||
assert.ok(doc.querySelector('#p20-ind #p20-ind-ind'), 'индикаторы §20');
|
||||
assert.ok(doc.querySelector('#p20-ind-drop div'), 'анимация индикатора §20');
|
||||
assert.ok(doc.querySelector('#p20-acids table'), 'таблица кислот §20');
|
||||
doc.defaultView.goTo('lo3'); await wait(100);
|
||||
assert.ok(doc.querySelector('#lo3-ind #lo3-ind-ind'), 'индикаторы ЛО3');
|
||||
@@ -206,6 +208,9 @@ test('ch3 Волна 2: §21 + ЛО4 + §22 + ПР3 + финал главы мо
|
||||
const { doc, errors } = await loadDom('chemistry_7_ch3.html');
|
||||
doc.defaultView.goTo('p21'); await wait(100);
|
||||
assert.ok(doc.querySelector('#p21-act .act-cell'), 'ряд активности §21');
|
||||
// клик по Zn (левее H₂) → пузырьки H₂
|
||||
doc.querySelector('#p21-act .act-cell[data-i="5"]').dispatchEvent(new doc.defaultView.Event('click', { bubbles: true })); await wait(40);
|
||||
assert.ok(doc.querySelector('#p21-tube div'), 'пузырьки H₂ при реакции металла с кислотой §21');
|
||||
doc.defaultView.goTo('lo4'); await wait(100);
|
||||
assert.ok(doc.querySelector('#lo4-rx #lo4-go'), 'опыт металл+кислота ЛО4');
|
||||
doc.defaultView.goTo('p22'); await wait(100);
|
||||
|
||||
@@ -35,14 +35,21 @@
|
||||
];
|
||||
function mount_p19() {
|
||||
var m = $('p19-rx'); if (!m || m._built) return; m._built = 1;
|
||||
var idx = 0;
|
||||
var idx = 0, anim = null;
|
||||
function stopAnim(){ if(anim){anim.stop();anim=null;} }
|
||||
function render(){
|
||||
stopAnim();
|
||||
var r = RX[idx];
|
||||
var swatch = idx===1 ? '<div style="margin-top:6px"><span style="display:inline-block;width:46px;height:20px;background:#1f2937;border-radius:4px;vertical-align:middle"></span> CuO (чёрный) → <span style="display:inline-block;width:46px;height:20px;background:#b45309;border-radius:4px;vertical-align:middle"></span> Cu (красный)</div>' : '';
|
||||
m.innerHTML = '<div class="fld"><label>Реакция</label><select id="p19-pick">'
|
||||
+ RX.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) + '</div><div style="font-size:.86rem;color:var(--muted);margin-top:6px">' + esc(r.note) + '</div>' + swatch + '</div>';
|
||||
$('p19-pick').addEventListener('change', function(e){ idx=+e.target.value; m._built=0; render(); });
|
||||
+ '<div id="p19-stage" style="margin:8px 0"></div>'
|
||||
+ '<div class="out ok" style="margin-top:8px"><div style="font-size:1.05rem">' + ceq(r.eq) + '</div><div style="font-size:.86rem;color:var(--muted);margin-top:6px">' + esc(r.note) + '</div></div>';
|
||||
var stage = $('p19-stage');
|
||||
if (stage && W.Chem7Anim) {
|
||||
if (idx === 1) anim = W.Chem7Anim.colorBlock(stage, '#1f2937', '#b45309', 'CuO (чёрный) → Cu (красная медь)', 1800);
|
||||
else anim = W.Chem7Anim.flameBox(stage, { color: '#93c5fd' });
|
||||
}
|
||||
$('p19-pick').addEventListener('change', function(e){ idx=+e.target.value; render(); });
|
||||
}
|
||||
render();
|
||||
}
|
||||
@@ -60,17 +67,20 @@
|
||||
};
|
||||
function indicatorWidget(mountId, withAcidPick) {
|
||||
var m = $(mountId); if (!m || m._built) return; m._built = 1;
|
||||
var ind = 'Лакмус', acid = 0;
|
||||
var ind = 'Лакмус', acid = 0, anim = null;
|
||||
function strip(color){ return '<div style="width:120px;height:34px;border-radius:8px;border:1.5px solid var(--border);background:'+color+';display:inline-block;vertical-align:middle"></div>'; }
|
||||
function render(){
|
||||
if (anim) { anim.stop(); anim = null; }
|
||||
var a = ACIDS[acid], col = INDIC[ind];
|
||||
m.innerHTML = '<div class="fld"><label>Индикатор</label><select id="'+mountId+'-ind">'
|
||||
+ Object.keys(INDIC).map(function(k){ return '<option'+(k===ind?' selected':'')+'>'+k+'</option>'; }).join('') + '</select>'
|
||||
+ (withAcidPick ? '<label>Кислота</label><select id="'+mountId+'-acid">' + ACIDS.map(function(x,i){ return '<option value="'+i+'"'+(i===acid?' selected':'')+'>'+fml(x.f)+' ('+x.name+')</option>'; }).join('') + '</select>' : '') + '</div>'
|
||||
+ '<div id="'+mountId+'-drop" style="margin-top:8px"></div>'
|
||||
+ '<div class="out ok" style="margin-top:8px">В нейтральной среде: ' + strip(col.neutral[0]) + ' <b>'+col.neutral[1]+'</b><br>'
|
||||
+ 'В кислоте' + (withAcidPick?(' ('+fml(a.f)+')'):'') + ': ' + strip(col.acid[0]) + ' <b>'+col.acid[1]+'</b></div>';
|
||||
$(mountId+'-ind').addEventListener('change', function(e){ ind=e.target.value; m._built=0; render(); });
|
||||
if (withAcidPick) $(mountId+'-acid').addEventListener('change', function(e){ acid=+e.target.value; m._built=0; render(); });
|
||||
if (W.Chem7Anim) anim = W.Chem7Anim.colorBlock($(mountId+'-drop'), col.neutral[0], col.acid[0], ind + ' в кислоте → ' + col.acid[1], 900);
|
||||
$(mountId+'-ind').addEventListener('change', function(e){ ind=e.target.value; render(); });
|
||||
if (withAcidPick) $(mountId+'-acid').addEventListener('change', function(e){ acid=+e.target.value; render(); });
|
||||
}
|
||||
render();
|
||||
}
|
||||
@@ -87,21 +97,26 @@
|
||||
var ROW = ['K','Ca','Na','Mg','Al','Zn','Fe','Ni','Sn','Pb','H','Cu','Hg','Ag','Pt','Au'];
|
||||
function mount_p21() {
|
||||
var m = $('p21-act'); if (!m || m._built) return; m._built = 1;
|
||||
var hIdx = ROW.indexOf('H');
|
||||
var hIdx = ROW.indexOf('H'), anim = null;
|
||||
function stopAnim(){ if(anim){anim.stop();anim=null;} }
|
||||
m.innerHTML = '<div style="display:flex;flex-wrap:wrap;gap:4px">'
|
||||
+ ROW.map(function(el,i){ var isH=el==='H'; return '<button class="act-cell" data-i="'+i+'" style="padding:6px 9px;border-radius:7px;border:1.5px solid '+(isH?'#dc2626':'var(--border)')+';background:'+(isH?'#fee2e2':'var(--card)')+';color:var(--text);font-weight:700;cursor:'+(isH?'default':'pointer')+'">'+(isH?'H₂':el)+'</button>'; }).join('') + '</div>'
|
||||
+ '<div style="font-size:.8rem;color:var(--muted);margin-top:4px">Слева активность убывает вправо. Граница — водород H₂.</div>'
|
||||
+ '<div style="font-size:.8rem;color:var(--muted);margin-top:4px">Слева активность убывает вправо. Граница — водород H₂. Кликни металл — «опусти» его в кислоту.</div>'
|
||||
+ '<div id="p21-tube" style="margin-top:8px"></div>'
|
||||
+ '<div class="out" id="p21-act-out" style="margin-top:8px">Кликни по металлу — узнаешь, вытесняет ли он водород из кислоты.</div>';
|
||||
var out = $('p21-act-out');
|
||||
m.querySelectorAll('.act-cell').forEach(function(b){
|
||||
b.addEventListener('click', function(){
|
||||
var i=+b.dataset.i, el=ROW[i]; if(el==='H'){ out.className='out'; out.innerHTML='<b>Водород H₂</b> — граница ряда активности.'; return; }
|
||||
var i=+b.dataset.i, el=ROW[i], tube=$('p21-tube'); stopAnim();
|
||||
if(el==='H'){ out.className='out'; out.innerHTML='<b>Водород H₂</b> — граница ряда активности.'; if(tube)tube.innerHTML=''; return; }
|
||||
out.className='out ok';
|
||||
if(i<hIdx){
|
||||
var extra = (i<=2) ? ' <span style="color:#dc2626">Внимание: очень активный металл — с кислотами реагирует бурно (для получения водорода используют Zn, Fe).</span>' : '';
|
||||
out.innerHTML = '<b>'+el+'</b> стоит левее H₂ → <b>вытесняет водород</b> из соляной и серной кислот: образуются соль и $H_2\\uparrow$.'+extra;
|
||||
if (tube && W.Chem7Anim) anim = W.Chem7Anim.bubbleField(tube, { color:'rgba(255,255,255,.85)', h:96 });
|
||||
} else {
|
||||
out.innerHTML = '<b>'+el+'</b> стоит правее H₂ → водород из кислот <b>не вытесняет</b> (например, медь и серебро с этими кислотами не реагируют).';
|
||||
if (tube) tube.innerHTML = '<div class="out" style="text-align:center;color:var(--muted)">реакция не идёт — пузырьков нет</div>';
|
||||
}
|
||||
if (W.chem8RenderMath) try { W.chem8RenderMath(out); } catch(e){}
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ html.dark{--bg:#140a24;--border:#3b2a63;--pri-soft:rgba(124,58,237,.18);--sec-ac
|
||||
<script src="/js/biochem-core.js" defer></script>
|
||||
<script src="/js/chem8_svg.js" defer></script>
|
||||
<script src="/js/chem7_svg.js" defer></script>
|
||||
<script src="/js/chem7_anim.js" defer></script>
|
||||
<script src="/js/chem7_ch3_widgets.js" defer></script>
|
||||
<script src="/js/chem8_engine.js" defer></script>
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user