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:
@@ -264,9 +264,40 @@
|
||||
return { stop: function () { try { host.innerHTML = ''; host.style.height = ''; } catch (e) {} }, el: b };
|
||||
}
|
||||
|
||||
/* ---- валентные «крючки»: атомы A и B с чёрточками-связями, соединяющимися (§9) ---- */
|
||||
function valenceLink(host, spec) {
|
||||
var ns = 'http://www.w3.org/2000/svg';
|
||||
var na = spec.a.n, nb = spec.b.n, va = spec.a.val, vb = spec.b.val, lcm = va * na;
|
||||
var W0 = 300, r = 17, lx = 50, rx = W0 - 50;
|
||||
var H0 = Math.max(na, nb, 1) * 50 + 20;
|
||||
function ycol(n, k) { var gap = 50, top = (H0 - (n - 1) * gap) / 2; return top + k * gap; }
|
||||
function spread(idx, val) { return (idx - (val - 1) / 2) * 9; }
|
||||
var colA = spec.a.color || '#6366f1', colB = spec.b.color || '#ef4444';
|
||||
var bonds = '';
|
||||
for (var t = 0; t < lcm; t++) {
|
||||
var la = Math.floor(t / va), rb = Math.floor(t / vb);
|
||||
var sy = ycol(na, la) + spread(t % va, va), ey = ycol(nb, rb) + spread(t % vb, vb);
|
||||
var sx = lx + r, ex = rx - r, len = Math.hypot(ex - sx, ey - sy);
|
||||
bonds += '<line x1="' + sx + '" y1="' + sy.toFixed(1) + '" x2="' + ex + '" y2="' + ey.toFixed(1) + '" stroke="#94a3b8" stroke-width="3" stroke-linecap="round" stroke-dasharray="' + len.toFixed(1) + '" stroke-dashoffset="' + (HEADLESS ? 0 : len.toFixed(1)) + '" style="transition:stroke-dashoffset .5s ease ' + (t * 0.08).toFixed(2) + 's"/>';
|
||||
}
|
||||
function atom(x, y, el, col) {
|
||||
return '<circle cx="' + x + '" cy="' + y.toFixed(1) + '" r="' + r + '" fill="' + col + '" stroke="rgba(0,0,0,.2)"/>'
|
||||
+ '<text x="' + x + '" y="' + (y + 4).toFixed(1) + '" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">' + el + '</text>';
|
||||
}
|
||||
var atoms = '';
|
||||
for (var i = 0; i < na; i++) atoms += atom(lx, ycol(na, i), spec.a.el, colA);
|
||||
for (var j = 0; j < nb; j++) atoms += atom(rx, ycol(nb, j), spec.b.el, colB);
|
||||
host.innerHTML = '<svg viewBox="0 0 ' + W0 + ' ' + H0 + '" width="100%" style="max-width:' + W0 + 'px;height:auto">' + bonds + atoms + '</svg>';
|
||||
if (!HEADLESS && !reduced()) {
|
||||
var lines = host.querySelectorAll('line');
|
||||
W.requestAnimationFrame(function () { W.requestAnimationFrame(function () { lines.forEach(function (l) { l.setAttribute('stroke-dashoffset', '0'); }); }); });
|
||||
}
|
||||
return { stop: function () { try { host.innerHTML = ''; } catch (e) {} } };
|
||||
}
|
||||
|
||||
W.Chem7Anim = {
|
||||
HEADLESS: HEADLESS, reduced: reduced, ease: ease, loop: loop, sceneCanvas: sceneCanvas,
|
||||
molecule3d: molecule3d, separation: separation, colorMorph: colorMorph, confettiSmall: confettiSmall, observeVisible: observeVisible,
|
||||
bubbleField: bubbleField, precipField: precipField, flameBox: flameBox, colorBlock: colorBlock
|
||||
bubbleField: bubbleField, precipField: precipField, flameBox: flameBox, colorBlock: colorBlock, valenceLink: valenceLink
|
||||
};
|
||||
})(window);
|
||||
|
||||
Reference in New Issue
Block a user