feat(chemistry7): визуал V2 — звёздный флагман §15 «Горение» (анимация пламени)

Подключён chem7_anim.js в Главу 2. §15: статичное SVG-пламя заменено на
анимированный flameBox с достоверным цветом по веществу — углерод оранжевое,
сера синее, фосфор ярко-белое, железо/магний с искрами; продукт-оксид и
уравнение всплывают. Тесты chem7: 16/16 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 19:45:53 +03:00
parent 41985a93eb
commit e8cb95be55
3 changed files with 17 additions and 10 deletions
+1
View File
@@ -171,6 +171,7 @@ test('ch2 Волна 1: интерактивы §13 + ЛО2 + §14 + §15 мон
assert.ok(doc.querySelector('#p15-burn #p15-go'), 'симулятор горения §15'); assert.ok(doc.querySelector('#p15-burn #p15-go'), 'симулятор горения §15');
doc.defaultView.goTo('p15'); doc.getElementById('p15-go').dispatchEvent(new doc.defaultView.Event('click', { bubbles: true })); doc.defaultView.goTo('p15'); doc.getElementById('p15-go').dispatchEvent(new doc.defaultView.Event('click', { bubbles: true }));
assert.match(doc.querySelector('#p15-out').textContent, /оксид/, 'горение даёт оксид'); assert.match(doc.querySelector('#p15-out').textContent, /оксид/, 'горение даёт оксид');
assert.ok(doc.querySelector('#p15-stage div'), 'анимация пламени §15');
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | ')); assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
}); });
+15 -10
View File
@@ -86,28 +86,33 @@
/* §15 — симулятор горения: вещество + O₂ → оксид */ /* §15 — симулятор горения: вещество + O₂ → оксид */
var FUELS = [ var FUELS = [
{ el:'C', name:'углерод', eq:'C + O2 = CO2', note:'горит с образованием углекислого газа' }, { el:'C', name:'углерод', eq:'C + O2 = CO2', flame:'#f97316', sparks:false, note:'горит с образованием углекислого газа' },
{ el:'S', name:'сера', eq:'S + O2 = SO2', note:'горит синим пламенем, резкий запах' }, { el:'S', name:'сера', eq:'S + O2 = SO2', flame:'#3b82f6', sparks:false, note:'горит синим пламенем, резкий запах' },
{ el:'P', name:'фосфор', eq:'4P + 5O2 = 2P2O5', note:'горит ярко, белый дым' }, { el:'P', name:'фосфор', eq:'4P + 5O2 = 2P2O5', flame:'#fde68a', sparks:false, note:'горит ярко, с белым дымом' },
{ el:'Fe', name:'железо', eq:'3Fe + 2O2 = Fe3O4', note:'горит, разбрасывая искры' }, { el:'Fe', name:'железо', eq:'3Fe + 2O2 = Fe3O4', flame:'#f59e0b', sparks:true, note:'горит, разбрасывая искры' },
{ el:'Mg', name:'магний', eq:'2Mg + O2 = 2MgO', note:'ослепительно яркое пламя' } { el:'Mg', name:'магний', eq:'2Mg + O2 = 2MgO', flame:'#e0f2fe', sparks:true, note:'горит ослепительно ярким пламенем' }
]; ];
function flame(){ function flame(){
return '<svg viewBox="0 0 60 70" width="56" height="66" style="vertical-align:middle"><path d="M30 8 C40 26 48 34 38 52 C46 46 46 60 30 64 C14 60 14 46 22 52 C12 34 22 26 30 8 Z" fill="#f97316"/><path d="M30 22 C36 34 40 40 33 52 C39 48 38 58 30 60 C22 58 22 50 26 52 C20 40 26 34 30 22 Z" fill="#fde047"/></svg>'; return '<svg viewBox="0 0 60 70" width="56" height="66" style="vertical-align:middle"><path d="M30 8 C40 26 48 34 38 52 C46 46 46 60 30 64 C14 60 14 46 22 52 C12 34 22 26 30 8 Z" fill="#f97316"/><path d="M30 22 C36 34 40 40 33 52 C39 48 38 58 30 60 C22 58 22 50 26 52 C20 40 26 34 30 22 Z" fill="#fde047"/></svg>';
} }
function mount_p15() { function mount_p15() {
var m = $('p15-burn'); if (!m || m._built) return; m._built = 1; var m = $('p15-burn'); 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(){ function render(){
var f = FUELS[idx]; stopAnim();
m.innerHTML = '<div class="fld"><label>Вещество</label><select id="p15-pick">' m.innerHTML = '<div class="fld"><label>Вещество</label><select id="p15-pick">'
+ FUELS.map(function(x,i){ return '<option value="'+i+'"'+(i===idx?' selected':'')+'>'+esc(x.name)+' ('+x.el+')</option>'; }).join('') + '</select>' + FUELS.map(function(x,i){ return '<option value="'+i+'"'+(i===idx?' selected':'')+'>'+esc(x.name)+' ('+x.el+')</option>'; }).join('') + '</select>'
+ '<button class="btn primary" id="p15-go">Поджечь в кислороде</button></div>' + '<button class="btn primary" id="p15-go">Поджечь в кислороде</button></div>'
+ '<div id="p15-stage" style="margin:8px 0"></div>'
+ '<div class="out" id="p15-out" style="margin-top:8px">Выбери вещество и подожги его в кислороде.</div>'; + '<div class="out" id="p15-out" style="margin-top:8px">Выбери вещество и подожги его в кислороде.</div>';
$('p15-pick').addEventListener('change', function(e){ idx=+e.target.value; m._built=0; render(); }); $('p15-pick').addEventListener('change', function(e){ idx=+e.target.value; render(); });
$('p15-go').addEventListener('click', function(){ $('p15-go').addEventListener('click', function(){
var out = $('p15-out'); out.className='out ok'; var f = FUELS[idx], out = $('p15-out');
out.innerHTML = flame() + ' <b>' + esc(f.name[0].toUpperCase()+f.name.slice(1)) + ' горит в кислороде:</b> ' + esc(f.note) + '.<br>' stopAnim();
if (W.Chem7Anim) anim = W.Chem7Anim.flameBox($('p15-stage'), { color: f.flame, sparks: f.sparks });
out.className='out ok';
out.innerHTML = '<b>' + esc(f.name[0].toUpperCase()+f.name.slice(1)) + ' горит в кислороде:</b> ' + esc(f.note) + '.<br>'
+ '<div style="margin-top:6px;font-size:1.05rem">' + ceq(f.eq) + '</div>' + '<div style="margin-top:6px;font-size:1.05rem">' + ceq(f.eq) + '</div>'
+ '<div style="font-size:.84rem;color:var(--muted);margin-top:4px">Продукт — <b>оксид</b> (соединение элемента с кислородом).</div>'; + '<div style="font-size:.84rem;color:var(--muted);margin-top:4px">Продукт — <b>оксид</b> (соединение элемента с кислородом).</div>';
}); });
+1
View File
@@ -23,6 +23,7 @@ html.dark{--bg:#08191c;--border:#164e5b;--pri-soft:rgba(8,145,178,.18);--sec-acc
<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/chem7_svg.js" defer></script> <script src="/js/chem7_svg.js" defer></script>
<script src="/js/chem7_anim.js" defer></script>
<script src="/js/chem7_ch2_widgets.js" defer></script> <script src="/js/chem7_ch2_widgets.js" defer></script>
<script src="/js/chem8_engine.js" defer></script> <script src="/js/chem8_engine.js" defer></script>
</head> </head>