feat(labs): 4 школьные хим. симы + визуальная прокачка лаборатории
4 НОВЫЕ СИМЫ (школьная программа 8-11 классов): Органика (organic.js, 1545 строк): - Конструктор молекул: drag атомов C/H/O/N/Cl/S, валентности, click-pair bonds - Авто-определение класса: алкан/алкен/алкин/спирт/альдегид/кислота/эфир/амин/аромат - IUPAC-имена для C1-C10 - Гомологические ряды: 7 рядов с slider количества углеродов, M, T_кип, T_пл - 6 качественных реакций: Br₂ вода, KMnO₄, Ag₂O/NH₃ (серебряное зеркало), Cu(OH)₂, FeCl₃, I₂ Периодическая таблица (periodic.js, 118 элементов): - Стандартный вид 18×9 + лантаноиды/актиноиды - Карточка элемента: Z, M, конфигурация, степени окисления, ЭО, ρ, T_пл/T_кип - Боровская модель электронных оболочек (анимированная) - Подсветка: 11 типов / s/p/d/f-блоки / без подсветки - Графики свойств по периоду/группе (ЭО, M, плотность, T_пл/T_кип) - Поиск по символу/имени/Z/массе Качественный анализ (qualanalysis.js, 24 иона): - 15 катионов: Na/K/NH₄/Mg/Ca/Ba/Al/Fe²⁺/Fe³⁺/Cu/Ag/Pb/Zn/H/OH - 10 анионов: Cl/Br/I/SO₄/SO₃/CO₃/NO₃/PO₄/S²/CH₃COO - 9 реактивов + пламя - 2 режима: «определи ион» и «неизвестное вещество» с логом наблюдений - Анимация капли, осадка с цветом, газовых пузырей, пламени Растворы (solutions.js, 4 режима): - Калькулятор: m_в, m_р-ра, ρ, T → ω, ν, C_М, C_Н с понятной логикой пересчёта - Разбавление с before/after визуализацией - Смешивание двух растворов с правилом рычага - Кривые растворимости 8 веществ + задача перекристаллизации - 15 пресетов веществ (NaCl, NaOH, H₂SO₄, CuSO₄·5H₂O, глюкоза, сахароза, ...) ВИЗУАЛЬНАЯ ПРОКАЧКА (_chem_visuals.js, helper file): 12 функций школьной лабораторной графики: - drawErlenmeyer / drawBeaker / drawBurette / drawTube — proper SVG-paths со шкалой - drawSpiritLamp — стеклянный резервуар + фитиль + анимированное пламя - animateGasBubbles / animatePrecipitateFall — анимация продуктов - drawProductLabel — fade-in/out стрелка ↑/↓ с подписью - drawEduTooltip — bubble с пояснением реакции - drawDeskBackground / drawVesselShadow — лабораторный фон - drawPHStrip — pH-индикаторная полоса с маркером Прокачено 6 chem-сим: chemsandbox, flask, titration, electrolysis, ionexchange, redox Каждая получила: фон парты, тени под колбами, анимированные стрелки продуктов, educational tooltips из поля 'why' реакции. Спиртовка с пламенем в flask. pH-полоса в titration. Каталог теперь: 39 симуляций (было 35 + 4 новых). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,12 @@ class RedoxSim {
|
||||
this._oParts = [];
|
||||
this._precip = [];
|
||||
this._gas = [];
|
||||
/* edu-tooltip + product labels */
|
||||
this._eduTooltipAge = -1;
|
||||
this._eduTooltipLines = [];
|
||||
this._prodLabelAge = -1;
|
||||
this._prodLabelText = '';
|
||||
this._prodLabelType = 'precip';
|
||||
this.W = 0; this.H = 0;
|
||||
this.onUpdate = null;
|
||||
this.fit();
|
||||
@@ -233,6 +239,39 @@ class RedoxSim {
|
||||
p.phase += dt;
|
||||
this._clamp(p);
|
||||
});
|
||||
/* trigger product label + edu-tooltip once */
|
||||
if (window.ChemVisuals && this._prodLabelAge < 0) {
|
||||
const rxn = RedoxSim.RXN[this.rxnId];
|
||||
if (rxn.precip) {
|
||||
this._prodLabelText = (rxn.pname || '') + ' ';
|
||||
this._prodLabelType = 'precip';
|
||||
this._prodLabelAge = 0;
|
||||
} else if (rxn.gas) {
|
||||
this._prodLabelText = (rxn.gname || '') + ' ';
|
||||
this._prodLabelType = 'gas';
|
||||
this._prodLabelAge = 0;
|
||||
}
|
||||
if (this._eduTooltipAge < 0 && rxn.eq_mol) {
|
||||
const stripSVG = s => (s || '').replace(/<[^>]+>/g, '->');
|
||||
const eqClean = stripSVG(rxn.eq_ion || rxn.eq_mol).slice(0, 38);
|
||||
this._eduTooltipLines = [
|
||||
(rxn.name || '').slice(0, 34),
|
||||
'e⁻: от восстановителя к окислителю',
|
||||
eqClean,
|
||||
].filter(Boolean).slice(0, 4);
|
||||
this._eduTooltipAge = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* advance ages */
|
||||
if (this._eduTooltipAge >= 0) {
|
||||
this._eduTooltipAge += dt / 4.0;
|
||||
if (this._eduTooltipAge >= 1.0) this._eduTooltipAge = -1;
|
||||
}
|
||||
if (this._prodLabelAge >= 0) {
|
||||
this._prodLabelAge += dt / 3.0;
|
||||
if (this._prodLabelAge >= 1.0) this._prodLabelAge = -1;
|
||||
}
|
||||
|
||||
/* Electrons — quadratic bezier arc */
|
||||
@@ -343,6 +382,12 @@ class RedoxSim {
|
||||
}
|
||||
}
|
||||
|
||||
/* desk */
|
||||
if (window.ChemVisuals) {
|
||||
ChemVisuals.drawDeskBackground(ctx, W, H, H * 0.82);
|
||||
ChemVisuals.drawVesselShadow(ctx, W / 2, H * 0.82, W * 0.38);
|
||||
}
|
||||
|
||||
this._drawBeaker(ctx, W, H);
|
||||
this._drawParticles(ctx, rxn);
|
||||
this._drawElectrons(ctx);
|
||||
@@ -350,6 +395,22 @@ class RedoxSim {
|
||||
if (rxn.gas) this._drawGas(ctx, rxn);
|
||||
this._drawPanel(ctx, W, H, rxn);
|
||||
if (window.LabFX) LabFX.particles.draw(this.ctx);
|
||||
|
||||
/* animated product label */
|
||||
if (window.ChemVisuals && this._prodLabelAge >= 0) {
|
||||
const labelY = this._prodLabelType === 'gas' ? H * 0.12 : H * 0.76;
|
||||
ChemVisuals.drawProductLabel(ctx, W / 2, labelY, this._prodLabelText, this._prodLabelType, this._prodLabelAge);
|
||||
if (this._prodLabelType === 'gas') {
|
||||
ChemVisuals.animateGasBubbles(ctx, W / 2, H * 0.16, rxn.gcolor || 'rgba(200,235,255,0.8)', this._t);
|
||||
} else {
|
||||
ChemVisuals.animatePrecipitateFall(ctx, W / 2, H * 0.72, rxn.pcolor || '#CCC', this._t);
|
||||
}
|
||||
}
|
||||
|
||||
/* edu tooltip */
|
||||
if (window.ChemVisuals && this._eduTooltipAge >= 0 && this._eduTooltipLines.length > 0) {
|
||||
ChemVisuals.drawEduTooltip(ctx, W / 2, H * 0.10, 210, this._eduTooltipLines, this._eduTooltipAge);
|
||||
}
|
||||
}
|
||||
|
||||
_drawBeaker(ctx, W, H) {
|
||||
|
||||
Reference in New Issue
Block a user