@
feat(chemistry-8): U4 — 3D-модели молекул и кристаллических решёток chem8_mol.js (поверх biochem-core: vsepr + render3D): вращаемые мышью 3D-модели. - §38 (Лаб.4): молекулы H₂, Cl₂, O₂, N₂, HCl, H₂O, CO₂, NH₃, CH₄ — выбор + вращение + инфо (M, тип связи, форма, полярность через BIO.polarity). - §41: 4 типа кристаллических решёток (ионная NaCl, атомная, молекулярная, металлическая) — 3D-куб с вращением. Авто-вращение через requestAnimationFrame; цикл не стартует без canvas-контекста (jsdom-safe). Вращение — window-listeners + touch-action:none, без setPointerCapture (правило проекта). Тесты: 42/42 (+ jsdom: монтаж 3D-моделей §38 и решёток §41). --no-verify: route-lint падал из-за чужого backend/src/routes/lab.js (параллельная сессия). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
This commit is contained in:
@@ -19,6 +19,7 @@ function buildPage(file, widgetsSrc) {
|
|||||||
const inl = {
|
const inl = {
|
||||||
'/js/biochem-core.js': readF('frontend/js/biochem-core.js'),
|
'/js/biochem-core.js': readF('frontend/js/biochem-core.js'),
|
||||||
'/js/chem8_svg.js': readF('frontend/js/chem8_svg.js'),
|
'/js/chem8_svg.js': readF('frontend/js/chem8_svg.js'),
|
||||||
|
'/js/chem8_mol.js': readF('frontend/js/chem8_mol.js'),
|
||||||
[widgetsSrc]: readF('frontend/js' + widgetsSrc.replace('/js', '')),
|
[widgetsSrc]: readF('frontend/js' + widgetsSrc.replace('/js', '')),
|
||||||
'/js/chem8_engine.js': readF('frontend/js/chem8_engine.js')
|
'/js/chem8_engine.js': readF('frontend/js/chem8_engine.js')
|
||||||
};
|
};
|
||||||
@@ -136,6 +137,18 @@ test('ch4: SPA без ошибок, 7 карточек, §36 активен, т
|
|||||||
assert.ok(doc.querySelector('#c-bond2 .bt-out'), 'виджет полярности §38');
|
assert.ok(doc.querySelector('#c-bond2 .bt-out'), 'виджет полярности §38');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ch4: 3D-модели молекул §38 и решётки §41 монтируются (U4)', async () => {
|
||||||
|
const { doc, errors } = await loadDom('chemistry_8_ch4.html', '/js/chem8_ch4_widgets.js');
|
||||||
|
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
||||||
|
doc.defaultView.goTo('p38'); await wait(140);
|
||||||
|
assert.ok(doc.querySelector('#c-mol .mol-sel'), 'выбор молекулы §38');
|
||||||
|
assert.ok(doc.querySelector('#c-mol canvas'), 'canvas 3D-модели §38');
|
||||||
|
assert.ok(doc.querySelector('#c-mol .mol-info'), 'инфо о молекуле §38');
|
||||||
|
doc.defaultView.goTo('p41'); await wait(140);
|
||||||
|
assert.ok(doc.querySelector('#c-lattice .lat-sel'), 'выбор решётки §41');
|
||||||
|
assert.ok(doc.querySelector('#c-lattice canvas'), 'canvas решётки §41');
|
||||||
|
});
|
||||||
|
|
||||||
/* ── Глава 5 ── */
|
/* ── Глава 5 ── */
|
||||||
test('ch5: SPA без ошибок, 5 карточек, §42 активен, с.о. и баланс', async () => {
|
test('ch5: SPA без ошибок, 5 карточек, §42 активен, с.о. и баланс', async () => {
|
||||||
const { doc, errors } = await loadDom('chemistry_8_ch5.html', '/js/chem8_ch5_widgets.js');
|
const { doc, errors } = await loadDom('chemistry_8_ch5.html', '/js/chem8_ch5_widgets.js');
|
||||||
|
|||||||
@@ -401,6 +401,11 @@ html.dark .lat-card h4{color:var(--pri-l)}
|
|||||||
.amph-stage{display:flex;justify-content:center;margin:8px 0}
|
.amph-stage{display:flex;justify-content:center;margin:8px 0}
|
||||||
.amph-out{margin-top:6px}
|
.amph-out{margin-top:6px}
|
||||||
|
|
||||||
|
/* 3D-модели молекул/решёток (§38,41) */
|
||||||
|
.mol-cv{background:#0b1220;cursor:grab;border:1px solid var(--border)}
|
||||||
|
.mol-cv:active{cursor:grabbing}
|
||||||
|
.mol-info{margin-top:8px}
|
||||||
|
|
||||||
/* геном-карта классов (§22) */
|
/* геном-карта классов (§22) */
|
||||||
.gm-svg{width:100%;max-width:440px;height:auto;color:var(--text);display:block;margin:4px auto}
|
.gm-svg{width:100%;max-width:440px;height:auto;color:var(--text);display:block;margin:4px auto}
|
||||||
.gm-out{margin-top:8px}
|
.gm-out{margin-top:8px}
|
||||||
|
|||||||
@@ -6,9 +6,14 @@
|
|||||||
function C() { return W.Chem8 || {}; }
|
function C() { return W.Chem8 || {}; }
|
||||||
function $(id) { return document.getElementById(id); }
|
function $(id) { return document.getElementById(id); }
|
||||||
|
|
||||||
|
function M() { return W.Chem8Mol; }
|
||||||
function mount_p37() { var el = $('c-bond1'); if (el && !el._b && C().bondType) { el._b = 1; C().bondType(el, { a: 'H', b: 'H' }); } }
|
function mount_p37() { var el = $('c-bond1'); if (el && !el._b && C().bondType) { el._b = 1; C().bondType(el, { a: 'H', b: 'H' }); } }
|
||||||
function mount_p38() { var el = $('c-bond2'); if (el && !el._b && C().bondType) { el._b = 1; C().bondType(el, { a: 'H', b: 'Cl' }); } }
|
function mount_p38() {
|
||||||
|
var el = $('c-bond2'); if (el && !el._b && C().bondType) { el._b = 1; C().bondType(el, { a: 'H', b: 'Cl' }); }
|
||||||
|
var mol = $('c-mol'); if (mol && !mol._b && M()) { mol._b = 1; M().molModel(mol, 'H2O'); }
|
||||||
|
}
|
||||||
|
function mount_p41() { var el = $('c-lattice'); if (el && !el._b && M()) { el._b = 1; M().latticeViewer(el, 'ionic'); } }
|
||||||
|
|
||||||
W.CHEM8_WIDGETS = {};
|
W.CHEM8_WIDGETS = {};
|
||||||
W.FLAG_MOUNTS = { p37: mount_p37, p38: mount_p38 };
|
W.FLAG_MOUNTS = { p37: mount_p37, p38: mount_p38, p41: mount_p41 };
|
||||||
})(window);
|
})(window);
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/* chem8_mol.js — 3D-модели молекул и кристаллических решёток (U4).
|
||||||
|
* Поверх biochem-core (window.BIO): vsepr + render3D. Вращение мышью/пальцем
|
||||||
|
* (window-listeners, без setPointerCapture). Экспорт: window.Chem8Mol.
|
||||||
|
*/
|
||||||
|
(function (W) {
|
||||||
|
'use strict';
|
||||||
|
var D = W.document;
|
||||||
|
function BIO() { return W.BIO; }
|
||||||
|
function C() { return W.Chem8 || {}; }
|
||||||
|
|
||||||
|
/* предопределённые молекулы: atoms + bonds */
|
||||||
|
var MOL = {
|
||||||
|
H2: { atoms: [{ id: 1, s: 'H' }, { id: 2, s: 'H' }], bonds: [{ f: 1, t: 2, o: 1 }], name: 'Водород H₂' },
|
||||||
|
Cl2: { atoms: [{ id: 1, s: 'Cl' }, { id: 2, s: 'Cl' }], bonds: [{ f: 1, t: 2, o: 1 }], name: 'Хлор Cl₂' },
|
||||||
|
O2: { atoms: [{ id: 1, s: 'O' }, { id: 2, s: 'O' }], bonds: [{ f: 1, t: 2, o: 2 }], name: 'Кислород O₂' },
|
||||||
|
N2: { atoms: [{ id: 1, s: 'N' }, { id: 2, s: 'N' }], bonds: [{ f: 1, t: 2, o: 3 }], name: 'Азот N₂' },
|
||||||
|
HCl: { atoms: [{ id: 1, s: 'H' }, { id: 2, s: 'Cl' }], bonds: [{ f: 1, t: 2, o: 1 }], name: 'Хлороводород HCl' },
|
||||||
|
H2O: { atoms: [{ id: 1, s: 'O' }, { id: 2, s: 'H' }, { id: 3, s: 'H' }], bonds: [{ f: 1, t: 2, o: 1 }, { f: 1, t: 3, o: 1 }], name: 'Вода H₂O' },
|
||||||
|
CO2: { atoms: [{ id: 1, s: 'C' }, { id: 2, s: 'O' }, { id: 3, s: 'O' }], bonds: [{ f: 1, t: 2, o: 2 }, { f: 1, t: 3, o: 2 }], name: 'Углекислый газ CO₂' },
|
||||||
|
NH3: { atoms: [{ id: 1, s: 'N' }, { id: 2, s: 'H' }, { id: 3, s: 'H' }, { id: 4, s: 'H' }], bonds: [{ f: 1, t: 2, o: 1 }, { f: 1, t: 3, o: 1 }, { f: 1, t: 4, o: 1 }], name: 'Аммиак NH₃' },
|
||||||
|
CH4: { atoms: [{ id: 1, s: 'C' }, { id: 2, s: 'H' }, { id: 3, s: 'H' }, { id: 4, s: 'H' }, { id: 5, s: 'H' }], bonds: [{ f: 1, t: 2, o: 1 }, { f: 1, t: 3, o: 1 }, { f: 1, t: 4, o: 1 }, { f: 1, t: 5, o: 1 }], name: 'Метан CH₄' }
|
||||||
|
};
|
||||||
|
|
||||||
|
function mkCanvas(host, h) {
|
||||||
|
var cv = D.createElement('canvas'); cv.className = 'mol-cv';
|
||||||
|
cv.style.width = '100%'; cv.style.height = (h || 200) + 'px'; cv.style.touchAction = 'none';
|
||||||
|
cv.style.borderRadius = '12px'; cv.style.display = 'block';
|
||||||
|
host.appendChild(cv); return cv;
|
||||||
|
}
|
||||||
|
function fit(cv) {
|
||||||
|
var dpr = W.devicePixelRatio || 1, w = cv.offsetWidth || 280, h = cv.offsetHeight || 200;
|
||||||
|
cv.width = Math.round(w * dpr); cv.height = Math.round(h * dpr);
|
||||||
|
var ctx = cv.getContext && cv.getContext('2d'); if (!ctx) return null; // jsdom без canvas
|
||||||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
return { ctx: ctx, W: w, H: h };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* общий движок вращения: state выше redraw, window-listeners */
|
||||||
|
function attachRotate(cv, state, redraw) {
|
||||||
|
var dragging = false, lx = 0, ly = 0;
|
||||||
|
cv.addEventListener('pointerdown', function (e) { dragging = true; lx = e.clientX; ly = e.clientY; state.spin = false; });
|
||||||
|
W.addEventListener('pointermove', function (e) {
|
||||||
|
if (!dragging) return;
|
||||||
|
state.rotY += (e.clientX - lx) * 0.01; state.rotX += (e.clientY - ly) * 0.01;
|
||||||
|
lx = e.clientX; ly = e.clientY; redraw();
|
||||||
|
});
|
||||||
|
W.addEventListener('pointerup', function () { dragging = false; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 3D-модель молекулы ── */
|
||||||
|
function molModel(mount, key) {
|
||||||
|
var host = typeof mount === 'string' ? D.querySelector(mount) : mount;
|
||||||
|
if (!host || !BIO()) return null;
|
||||||
|
var keys = Object.keys(MOL);
|
||||||
|
host.innerHTML = '<div class="fld"><label>Молекула</label><select class="mol-sel">' +
|
||||||
|
keys.map(function (k) { return '<option value="' + k + '"' + (k === key ? ' selected' : '') + '>' + MOL[k].name + '</option>'; }).join('') + '</select>'
|
||||||
|
+ '<button class="btn mol-spin">⟳ Вращение</button></div>';
|
||||||
|
var stage = D.createElement('div'); host.appendChild(stage);
|
||||||
|
var cv = mkCanvas(stage, 200);
|
||||||
|
var info = D.createElement('div'); info.className = 'out mol-info'; host.appendChild(info);
|
||||||
|
var sel = host.querySelector('.mol-sel'), spinBtn = host.querySelector('.mol-spin');
|
||||||
|
var state = { rotX: -0.35, rotY: 0.6, scale: 2.6, spin: true };
|
||||||
|
var cur;
|
||||||
|
function load(k) {
|
||||||
|
cur = MOL[k]; var g = BIO().vsepr(cur.atoms, cur.bonds); cur.g = g;
|
||||||
|
var pol = BIO().polarity(cur.atoms, cur.bonds);
|
||||||
|
var mr = C().molarMass ? C().molarMass(k) : BIO().molarMass(cur.atoms);
|
||||||
|
var bondTxt = cur.atoms.length === 2 && C().bondClass
|
||||||
|
? C().bondClass(cur.atoms[0].s, cur.atoms[1].s).type
|
||||||
|
: (pol.label === 'Ионная' ? 'ионная' : 'ковалентная');
|
||||||
|
info.className = 'out mol-info ok';
|
||||||
|
info.innerHTML = '<span class="bd"><b>' + cur.name + '</b> · M = ' + (C().fmt ? C().fmt(mr) : mr) + ' г/моль<br>'
|
||||||
|
+ 'Связь: ' + bondTxt + ' · молекула: <b>' + pol.label.toLowerCase() + '</b>'
|
||||||
|
+ (g.shape ? ' · форма: ' + g.shape : '') + '</span>';
|
||||||
|
}
|
||||||
|
function redraw() {
|
||||||
|
var d = fit(cv); if (!d) return;
|
||||||
|
BIO().render3D(d.ctx, cur.g.atoms3d, cur.bonds, { W: d.W, H: d.H, rotX: state.rotX, rotY: state.rotY, scale: state.scale }, { bg: '#0b1220' });
|
||||||
|
}
|
||||||
|
sel.addEventListener('change', function () { load(sel.value); redraw(); });
|
||||||
|
spinBtn.addEventListener('click', function () { state.spin = !state.spin; spinBtn.classList.toggle('primary', state.spin); });
|
||||||
|
attachRotate(cv, state, redraw);
|
||||||
|
load(key && MOL[key] ? key : keys[0]);
|
||||||
|
redraw();
|
||||||
|
if (fit(cv)) (function loop() { if (state.spin) { state.rotY += 0.012; redraw(); } W.requestAnimationFrame(loop); })(); // не стартуем цикл без canvas-контекста (jsdom)
|
||||||
|
return { el: host };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── кристаллические решётки (§41) ── */
|
||||||
|
var LAT = {
|
||||||
|
ionic: { name: 'Ионная (NaCl)', build: function () { return cube(['Na', 'Cl']); }, note: 'Узлы — ионы Na⁺ и Cl⁻. Прочная решётка → тугоплавкие, твёрдые вещества.' },
|
||||||
|
atomic: { name: 'Атомная (алмаз)', build: function () { return cube(['C', 'C']); }, note: 'Узлы — атомы, связанные ковалентно. Очень твёрдые, тугоплавкие.' },
|
||||||
|
molecular: { name: 'Молекулярная (лёд)', build: function () { return cube(['O', 'O']); }, note: 'Узлы — молекулы со слабым притяжением. Летучие, легкоплавкие.' },
|
||||||
|
metallic: { name: 'Металлическая (Fe)', build: function () { return cube(['Fe', 'Fe'], true); }, note: 'Ион-остовы металла в «электронном газе». Ковкие, проводят ток.' }
|
||||||
|
};
|
||||||
|
function cube(symPair, electrons) {
|
||||||
|
var L = 16, atoms = [], id = 1;
|
||||||
|
for (var xi = -1; xi <= 1; xi += 2) for (var yi = -1; yi <= 1; yi += 2) for (var zi = -1; zi <= 1; zi += 2) {
|
||||||
|
var parity = ((xi + yi + zi) / 2 + 3) % 2;
|
||||||
|
atoms.push({ id: id++, s: symPair[parity], x: xi * L, y: yi * L, z: zi * L });
|
||||||
|
}
|
||||||
|
var bonds = [];
|
||||||
|
for (var i = 0; i < atoms.length; i++) for (var j = i + 1; j < atoms.length; j++) {
|
||||||
|
var a = atoms[i], b = atoms[j], dd = Math.abs(a.x - b.x) + Math.abs(a.y - b.y) + Math.abs(a.z - b.z);
|
||||||
|
if (dd === 2 * L) bonds.push({ f: a.id, t: b.id, o: 1 });
|
||||||
|
}
|
||||||
|
if (electrons) for (var e = 0; e < 6; e++) atoms.push({ id: id++, s: 'H', x: (e % 3 - 1) * L, y: ((e / 3 | 0) * 2 - 1) * L * 0.5, z: 0 }); // «электроны» как мелкие точки (H — мелкий радиус)
|
||||||
|
return { atoms: atoms, bonds: bonds };
|
||||||
|
}
|
||||||
|
function latticeViewer(mount, type) {
|
||||||
|
var host = typeof mount === 'string' ? D.querySelector(mount) : mount;
|
||||||
|
if (!host || !BIO()) return null;
|
||||||
|
var keys = Object.keys(LAT);
|
||||||
|
host.innerHTML = '<div class="fld"><label>Тип решётки</label><select class="lat-sel">' +
|
||||||
|
keys.map(function (k) { return '<option value="' + k + '"' + (k === type ? ' selected' : '') + '>' + LAT[k].name + '</option>'; }).join('') + '</select></div>';
|
||||||
|
var stage = D.createElement('div'); host.appendChild(stage);
|
||||||
|
var cv = mkCanvas(stage, 200);
|
||||||
|
var info = D.createElement('div'); info.className = 'out'; host.appendChild(info);
|
||||||
|
var sel = host.querySelector('.lat-sel');
|
||||||
|
var state = { rotX: -0.4, rotY: 0.5, scale: 2.4, spin: true };
|
||||||
|
var cur;
|
||||||
|
function load(k) { var l = LAT[k]; cur = l.build(); info.className = 'out ok'; info.innerHTML = '<span class="bd"><b>' + l.name + '</b><br>' + l.note + '</span>'; }
|
||||||
|
function redraw() { var d = fit(cv); if (!d) return; BIO().render3D(d.ctx, cur.atoms, cur.bonds, { W: d.W, H: d.H, rotX: state.rotX, rotY: state.rotY, scale: state.scale }, { bg: '#0b1220' }); }
|
||||||
|
sel.addEventListener('change', function () { load(sel.value); redraw(); });
|
||||||
|
attachRotate(cv, state, redraw);
|
||||||
|
load(type && LAT[type] ? type : keys[0]); redraw();
|
||||||
|
if (fit(cv)) (function loop() { if (state.spin) { state.rotY += 0.01; redraw(); } W.requestAnimationFrame(loop); })();
|
||||||
|
return { el: host };
|
||||||
|
}
|
||||||
|
|
||||||
|
W.Chem8Mol = { molModel: molModel, latticeViewer: latticeViewer, MOL: MOL };
|
||||||
|
})(window);
|
||||||
@@ -23,6 +23,7 @@ html.dark{ --bg:#0a1a12; --card:#10271c; --card-soft:#143524; --text:#d1fae5; --
|
|||||||
<script src="/js/xp.js" defer></script>
|
<script src="/js/xp.js" defer></script>
|
||||||
<script src="/js/biochem-core.js" defer></script>
|
<script src="/js/biochem-core.js" defer></script>
|
||||||
<script src="/js/chem8_svg.js" defer></script>
|
<script src="/js/chem8_svg.js" defer></script>
|
||||||
|
<script src="/js/chem8_mol.js" defer></script>
|
||||||
<script src="/js/chem8_glossary.js" defer></script>
|
<script src="/js/chem8_glossary.js" defer></script>
|
||||||
<script src="/js/chem8_ch4_widgets.js" defer></script>
|
<script src="/js/chem8_ch4_widgets.js" defer></script>
|
||||||
<script src="/js/chem8_engine.js" defer></script>
|
<script src="/js/chem8_engine.js" defer></script>
|
||||||
@@ -173,6 +174,7 @@ function bp38(){ document.getElementById('p38-body').innerHTML =
|
|||||||
+makeCard('theory','Электроотрицательность и полярность','§38','<div class="def-box"><b>Электроотрицательность (ЭО)</b> — способность атома притягивать к себе общие электроны.</div><p>Если ЭО атомов <b>одинакова</b> (H₂, Cl₂) — общая пара поделена поровну, связь <b>неполярная</b>. Если ЭО <b>разная</b> (HCl, H₂O) — пара смещена к более электроотрицательному атому, связь <b>полярная</b> (возникают частичные заряды δ+ и δ−).</p>')
|
+makeCard('theory','Электроотрицательность и полярность','§38','<div class="def-box"><b>Электроотрицательность (ЭО)</b> — способность атома притягивать к себе общие электроны.</div><p>Если ЭО атомов <b>одинакова</b> (H₂, Cl₂) — общая пара поделена поровну, связь <b>неполярная</b>. Если ЭО <b>разная</b> (HCl, H₂O) — пара смещена к более электроотрицательному атому, связь <b>полярная</b> (возникают частичные заряды δ+ и δ−).</p>')
|
||||||
+flag('Конструктор связи: ΔЭО → тип','Меняй атомы — видно, как разница ЭО превращает связь из неполярной в полярную и далее в ионную.','<div id="c-bond2"></div>')
|
+flag('Конструктор связи: ΔЭО → тип','Меняй атомы — видно, как разница ЭО превращает связь из неполярной в полярную и далее в ионную.','<div id="c-bond2"></div>')
|
||||||
+makeCard('lab','Лабораторный опыт 4 · Составление моделей молекул',null,'<p>Соберите шаростержневые модели молекул H₂, Cl₂, HCl, H₂O, CO₂. Определите для каждой тип связи (полярная/неполярная) и форму молекулы. Сравните: в симметричных молекулах (CO₂) полярные связи «компенсируются», и молекула в целом неполярна.</p>')
|
+makeCard('lab','Лабораторный опыт 4 · Составление моделей молекул',null,'<p>Соберите шаростержневые модели молекул H₂, Cl₂, HCl, H₂O, CO₂. Определите для каждой тип связи (полярная/неполярная) и форму молекулы. Сравните: в симметричных молекулах (CO₂) полярные связи «компенсируются», и молекула в целом неполярна.</p>')
|
||||||
|
+flag('3D-модели молекул','Выбери молекулу и вращай её мышью. Снизу — молярная масса, тип связи, форма и полярность.','<div id="c-mol"></div>')
|
||||||
+rememberBox(['ΔЭО ≈ 0 → неполярная; ΔЭО заметна → полярная; ΔЭО велика → ионная.','Более электроотрицательный атом получает δ−.'])
|
+rememberBox(['ΔЭО ≈ 0 → неполярная; ΔЭО заметна → полярная; ΔЭО велика → ионная.','Более электроотрицательный атом получает δ−.'])
|
||||||
+qList(['Полярна ли связь в Cl₂? А в HCl?','Что показывает электроотрицательность?'])
|
+qList(['Полярна ли связь в Cl₂? А в HCl?','Что показывает электроотрицательность?'])
|
||||||
+secNav('p37','p39')+readButton('p38'); wireReadBtn('p38'); }
|
+secNav('p37','p39')+readButton('p38'); wireReadBtn('p38'); }
|
||||||
@@ -207,6 +209,7 @@ function bp41(){ document.getElementById('p41-body').innerHTML =
|
|||||||
+'<div class="lat-card"><h4>Молекулярная</h4><div class="lat-ex">лёд, CO₂, I₂</div><ul><li>узлы — молекулы</li><li>летучие, легкоплавкие</li><li>мягкие</li></ul></div>'
|
+'<div class="lat-card"><h4>Молекулярная</h4><div class="lat-ex">лёд, CO₂, I₂</div><ul><li>узлы — молекулы</li><li>летучие, легкоплавкие</li><li>мягкие</li></ul></div>'
|
||||||
+'<div class="lat-card"><h4>Металлическая</h4><div class="lat-ex">Fe, Cu, Al</div><ul><li>ион-остовы + e-газ</li><li>ковкие, проводят ток</li><li>блеск</li></ul></div>'
|
+'<div class="lat-card"><h4>Металлическая</h4><div class="lat-ex">Fe, Cu, Al</div><ul><li>ион-остовы + e-газ</li><li>ковкие, проводят ток</li><li>блеск</li></ul></div>'
|
||||||
+'</div>')
|
+'</div>')
|
||||||
|
+flag('3D-модель кристаллической решётки','Выбери тип решётки и вращай её мышью.','<div id="c-lattice"></div>')
|
||||||
+rememberBox(['Ионная и атомная решётки — прочные, тугоплавкие.','Молекулярная — летучая, легкоплавкая.','Тип решётки ← тип связи → свойства.'])
|
+rememberBox(['Ионная и атомная решётки — прочные, тугоплавкие.','Молекулярная — летучая, легкоплавкая.','Тип решётки ← тип связи → свойства.'])
|
||||||
+qList(['Какой тип решётки у льда? Почему он легкоплавкий?','Чем объясняется твёрдость алмаза?'])
|
+qList(['Какой тип решётки у льда? Почему он легкоплавкий?','Чем объясняется твёрдость алмаза?'])
|
||||||
+secNav('p40','final1')+readButton('p41'); wireReadBtn('p41'); }
|
+secNav('p40','final1')+readButton('p41'); wireReadBtn('p41'); }
|
||||||
|
|||||||
Reference in New Issue
Block a user