feat(chemistry7): визуал V1-хвост — §9 валентные связи + §12 подсчёт атомов
§9: добавлена схема «связей-крючков» (Chem7Anim.valenceLink, SVG) — атомы A и B с чёрточками валентности, связи прорисовываются (draw-in); число связей = НОК. §12: под балансировщиком — анимированный подсчёт атомов (реагенты vs продукты), атомы-точки появляются масштабированием; подтверждается баланс слева=справа. Все интерактивы Химии 7 анимированы. Тесты chem7: 16/16; полный прогон 162/165. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -295,23 +295,30 @@
|
||||
function gcd(a, b) { return b ? gcd(b, a % b) : a; }
|
||||
var VA = [ ['Na', 1], ['K', 1], ['H', 1], ['Mg', 2], ['Ca', 2], ['Zn', 2], ['Cu', 2], ['Al', 3], ['C', 4] ];
|
||||
var VB = [ ['O', 2], ['Cl', 1], ['S', 2] ];
|
||||
var BCOL = { O:'#ef4444', Cl:'#22c55e', S:'#eab308' };
|
||||
function mount_p9() {
|
||||
var m = $('p9-bld'); if (!m || m._built) return; m._built = 1;
|
||||
var vanim = null;
|
||||
function optA(){ return VA.map(function(e,i){ return '<option value="'+i+'"'+(e[0]==='Al'?' selected':'')+'>'+e[0]+' (валентность '+'I'.repeat(e[1]).replace('IIII','IV')+')</option>'; }).join(''); }
|
||||
function optB(){ return VB.map(function(e,i){ return '<option value="'+i+'">'+e[0]+' (валентность '+'I'.repeat(e[1])+')</option>'; }).join(''); }
|
||||
m.innerHTML = '<div class="fld"><label>Элемент A</label><select id="p9-a">'+optA()+'</select>'
|
||||
+'<label>Элемент B</label><select id="p9-b">'+optB()+'</select></div><div class="out" id="p9-bout"></div>';
|
||||
+'<label>Элемент B</label><select id="p9-b">'+optB()+'</select></div>'
|
||||
+'<div id="p9-vis" style="margin:8px 0;display:flex;justify-content:center"></div>'
|
||||
+'<div class="out" id="p9-bout"></div>';
|
||||
function upd() {
|
||||
var a = VA[+$('p9-a').value], b = VB[+$('p9-b').value];
|
||||
var lcm = a[1] * b[1] / gcd(a[1], b[1]);
|
||||
var ia = lcm / a[1], ib = lcm / b[1];
|
||||
var raw = a[0] + (ia > 1 ? ia : '') + b[0] + (ib > 1 ? ib : '');
|
||||
if (vanim) { vanim.stop(); vanim = null; }
|
||||
if (W.Chem7Anim) vanim = W.Chem7Anim.valenceLink($('p9-vis'), {
|
||||
a: { el:a[0], val:a[1], n:ia, color:'#6366f1' },
|
||||
b: { el:b[0], val:b[1], n:ib, color:BCOL[b[0]] || '#ef4444' } });
|
||||
var out = $('p9-bout'); out.className = 'out ok';
|
||||
out.innerHTML = '<span class="bd">Валентности: ' + a[0] + ' = ' + 'I'.repeat(a[1]).replace('IIII','IV') + ', ' + b[0] + ' = ' + 'I'.repeat(b[1]) + '<br>'
|
||||
+ 'Наименьшее общее кратное валентностей = <b>' + lcm + '</b><br>'
|
||||
+ 'Индексы: ' + a[0] + ' → ' + ia + ', ' + b[0] + ' → ' + ib + '<br>'
|
||||
+ 'Формула: <b style="font-size:1.15rem">' + (C().formula ? C().formula(raw) : raw) + '</b><br>'
|
||||
+ 'Проверка: ' + ia + '·' + a[1] + ' = ' + ib + '·' + b[1] + ' = ' + lcm + ' единиц валентности — совпало.</span>';
|
||||
+ 'Каждая чёрточка-связь соединена — все валентности заняты.<br>'
|
||||
+ 'НОК валентностей = <b>' + lcm + '</b>; индексы: ' + a[0] + ' → ' + ia + ', ' + b[0] + ' → ' + ib + '<br>'
|
||||
+ 'Формула: <b style="font-size:1.15rem">' + (C().formula ? C().formula(raw) : raw) + '</b></span>';
|
||||
}
|
||||
$('p9-a').addEventListener('change', upd); $('p9-b').addEventListener('change', upd); upd();
|
||||
}
|
||||
@@ -394,10 +401,34 @@
|
||||
render();
|
||||
}
|
||||
|
||||
/* §12 — балансировщик уравнений (переиспользуем Chem8.equationBalancer) */
|
||||
/* §12 — балансировщик + анимированный подсчёт атомов (слева/справа) */
|
||||
var ELC = { H:'#cbd5e1', O:'#ef4444', C:'#334155', N:'#3b82f6', S:'#eab308', Fe:'#b45309', P:'#f97316', Cl:'#22c55e', Mg:'#22c55e', Ca:'#a78bfa', Na:'#a78bfa', Cu:'#ea580c', Zn:'#64748b', Al:'#6366f1', K:'#a78bfa' };
|
||||
function mount_p12() {
|
||||
var pick = $('p12-pick'), mount = $('p12-mount'); if (!pick || pick._built || !C().equationBalancer) return; pick._built = 1;
|
||||
function build() { var parts = pick.value.split('|'); C().equationBalancer(mount, { skeleton: parts[0], solution: parts[1].split(',').map(Number) }); }
|
||||
if (!$('p12-tally')) mount.insertAdjacentHTML('afterend', '<div id="p12-tally" style="margin-top:10px"></div>');
|
||||
function sumSide(list, coeffs, off) {
|
||||
var tot = {};
|
||||
list.forEach(function (sp, i) { var cnt = C().elementCounts ? C().elementCounts(sp) : {}; var co = coeffs[off + i] || 1; for (var e in cnt) tot[e] = (tot[e] || 0) + cnt[e] * co; });
|
||||
return tot;
|
||||
}
|
||||
function dots(el, n) { var s = ''; for (var i = 0; i < n; i++) s += '<span class="c7-atom" style="display:inline-block;width:13px;height:13px;border-radius:50%;margin:1px;background:' + (ELC[el] || '#94a3b8') + ';transform:scale(.2);opacity:0;transition:transform .3s ease,opacity .3s ease"></span>'; return s; }
|
||||
function col(title, tot) { return '<div style="flex:1;min-width:140px"><div style="font-weight:700;font-size:.82rem;margin-bottom:4px">' + title + '</div>' + Object.keys(tot).map(function (e) { return '<div style="display:flex;align-items:center;gap:6px;margin:2px 0"><b style="width:26px">' + e + '</b>' + dots(e, tot[e]) + '<span style="color:var(--muted);font-size:.8rem">× ' + tot[e] + '</span></div>'; }).join('') + '</div>'; }
|
||||
function tally(skeleton, coeffs) {
|
||||
var t = $('p12-tally'); if (!t) return;
|
||||
var sides = skeleton.split(/->|=/);
|
||||
var L = sides[0].split('+').map(function (s) { return s.trim(); });
|
||||
var Rr = (sides[1] || '').split('+').map(function (s) { return s.trim(); });
|
||||
var left = sumSide(L, coeffs, 0), right = sumSide(Rr, coeffs, L.length);
|
||||
var ok = Object.keys(left).every(function (e) { return left[e] === right[e]; }) && Object.keys(right).every(function (e) { return left[e] === right[e]; });
|
||||
t.innerHTML = '<div style="display:flex;gap:14px;flex-wrap:wrap">' + col('Реагенты — атомы', left) + col('Продукты — атомы', right) + '</div>'
|
||||
+ '<div class="out ' + (ok ? 'ok' : 'bad') + '" style="margin-top:6px">' + (ok ? '✓ Число атомов каждого элемента слева и справа <b>совпадает</b> — уравнение сбалансировано.' : 'Атомы не уравнены.') + '</div>';
|
||||
if (W.Chem7Anim && !W.Chem7Anim.HEADLESS) {
|
||||
var a = t.querySelectorAll('.c7-atom');
|
||||
a.forEach(function (d, i) { d.style.transitionDelay = (i * 28) + 'ms'; });
|
||||
W.requestAnimationFrame(function () { W.requestAnimationFrame(function () { a.forEach(function (d) { d.style.transform = 'scale(1)'; d.style.opacity = '1'; }); }); });
|
||||
}
|
||||
}
|
||||
function build() { var parts = pick.value.split('|'); var coeffs = parts[1].split(',').map(Number); C().equationBalancer(mount, { skeleton: parts[0], solution: coeffs }); tally(parts[0], coeffs); }
|
||||
pick.addEventListener('change', build); build();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user