Files
Maxim Dolgolyov ff78851310 fix(alg7 fx): KaTeX-делимитеры $...$ в визуализаторах §12-§13
В alg7-fx.js renderMathInElement() вызывался без опций — KaTeX
auto-render по умолчанию узнаёт только \(...\) и \[...\], а
не $...$. Поэтому формулы в виз. квадрата суммы и разности
квадратов отображались как обычный текст (см. скриншот пользователя).

Фикс: общий хелпер ALG7.renderMath(root), который вызывает
renderMathInElement с теми же делимитерами, что прописаны в
страницах глав ($$, $, \[\], \(\)).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:27:54 +03:00

432 lines
22 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* alg7-fx.js — универсальные эффекты для всех глав Алгебры 7
* Подключается всеми ch1..ch4. Автоматически:
* - наблюдает за элементами .feedback, при появлении .ok → pulse + комбо,
* при .fail → shake + сброс комбо
* - показывает значок комбо при сериях 3/5/10 правильных подряд
* - даёт бонусный XP через global addXp(), если он определён
*
* Публичный API на window.ALG7:
* ALG7.shake(el), ALG7.pulse(el)
* ALG7.combo, ALG7.maxCombo, ALG7.resetCombo()
* ALG7.buildQuadSumViz(container) — виз. квадрата суммы (Ch2 §12)
* ALG7.buildDiffSquaresViz(container)— виз. разности квадратов (Ch2 §13)
*/
(function(){
'use strict';
if (window.ALG7 && window.ALG7.__installed) return;
const ALG7 = window.ALG7 = window.ALG7 || {};
ALG7.__installed = true;
ALG7.combo = 0;
ALG7.maxCombo = 0;
/* === KATEX HELPER (использует те же делимитеры, что и страницы) === */
const _KATEX_OPTS = {
delimiters: [
{ left:'$$', right:'$$', display:true },
{ left:'$', right:'$', display:false },
{ left:'\\[', right:'\\]', display:true },
{ left:'\\(', right:'\\)', display:false }
],
throwOnError: false
};
ALG7.renderMath = function(root){
if(!root || !window.renderMathInElement) return;
try{ window.renderMathInElement(root, _KATEX_OPTS); }catch(e){}
};
/* === ANIMATIONS === */
ALG7.shake = function(el){
if(!el) return;
el.classList.remove('alg7-shake');
void el.offsetWidth;
el.classList.add('alg7-shake');
setTimeout(()=>el.classList.remove('alg7-shake'), 600);
};
ALG7.pulse = function(el){
if(!el) return;
el.classList.remove('alg7-pulse');
void el.offsetWidth;
el.classList.add('alg7-pulse');
setTimeout(()=>el.classList.remove('alg7-pulse'), 950);
};
ALG7.resetCombo = function(silent){
if(ALG7.combo > 0 && !silent){
/* fade streak indicator */
const s = document.querySelector('.alg7-streak');
if(s){ s.style.opacity = '0'; setTimeout(()=>{ s.classList.remove('show'); s.style.opacity = ''; }, 300); }
}
ALG7.combo = 0;
};
function _ensureStreak(){
let s = document.querySelector('.alg7-streak');
if(!s){
s = document.createElement('div');
s.className = 'alg7-streak';
s.innerHTML = '<svg class="ic-fire" viewBox="0 0 24 24" fill="#fff7ed"><path d="M12 2c-1 4-5 5-5 10a5 5 0 0 0 10 0c0-3-2-5-2-7 0-1 1-1.5 0-3z M9 16c0-2 1.5-3 3-3 1 2 2 2 2 4a2.5 2.5 0 0 1-5 0z" /></svg><span class="streak-text">3</span>';
document.body.appendChild(s);
}
return s;
}
function _showStreak(n){
const s = _ensureStreak();
s.classList.add('show');
s.querySelector('.streak-text').textContent = '×' + n;
}
function _showComboBadge(n, bonusXp){
const badge = document.createElement('div');
badge.className = 'alg7-combo-badge';
badge.innerHTML = '<svg class="ic-fire" viewBox="0 0 24 24" fill="#fff7ed"><path d="M12 2c-1 4-5 5-5 10a5 5 0 0 0 10 0c0-3-2-5-2-7 0-1 1-1.5 0-3z M9 16c0-2 1.5-3 3-3 1 2 2 2 2 4a2.5 2.5 0 0 1-5 0z" /></svg> КОМБО <span class="badge-x">×' + n + '</span> +' + bonusXp + ' XP';
/* position center top-ish */
const vw = window.innerWidth, vh = window.innerHeight;
badge.style.left = (vw / 2 - 130) + 'px';
badge.style.top = (vh / 2 - 80) + 'px';
document.body.appendChild(badge);
setTimeout(()=>badge.remove(), 2300);
}
function _addSparkles(el){
if(!el) return;
const r = el.getBoundingClientRect();
for(let i = 0; i < 6; i++){
const sp = document.createElement('div');
sp.className = 'alg7-sparkle';
sp.style.left = (r.left + r.width * Math.random()) + 'px';
sp.style.top = (r.top + r.height * Math.random()) + 'px';
sp.style.position = 'fixed';
sp.style.zIndex = '9999';
sp.style.animationDelay = (i * 60) + 'ms';
document.body.appendChild(sp);
setTimeout(()=>sp.remove(), 1000);
}
}
ALG7.onCorrect = function(elm){
ALG7.combo++;
if(ALG7.combo > ALG7.maxCombo) ALG7.maxCombo = ALG7.combo;
if(elm) { ALG7.pulse(elm); _addSparkles(elm); }
if(ALG7.combo >= 3) _showStreak(ALG7.combo);
/* милестоны: 3, 5, 10 — бонусные XP */
const milestones = { 3:5, 5:15, 10:50, 15:75, 20:100 };
if(milestones[ALG7.combo]){
const bonus = milestones[ALG7.combo];
_showComboBadge(ALG7.combo, bonus);
if(typeof window.addXp === 'function') window.addXp(bonus, 'combo-' + ALG7.combo);
}
};
ALG7.onWrong = function(elm){
if(elm) ALG7.shake(elm);
ALG7.resetCombo();
};
/* === АВТО-ХУК НА FEEDBACK ЧЕРЕЗ MUTATIONOBSERVER === */
const _seenFeedback = new WeakMap();
function _processFeedback(el){
const display = window.getComputedStyle(el).display;
if(display === 'none') return;
const isOk = el.classList.contains('ok');
const isFail = el.classList.contains('fail');
if(!isOk && !isFail) return;
/* Дебаунс — каждый элемент обрабатываем не чаще раза в 700мс */
const now = Date.now();
const last = _seenFeedback.get(el) || 0;
if(now - last < 700) return;
_seenFeedback.set(el, now);
/* Найти ближайший .wg или родительскую карточку */
const target = el.closest('.wg') || el.closest('.card') || el.parentElement;
if(isOk) ALG7.onCorrect(target);
else ALG7.onWrong(target);
}
function _initObserver(){
if(!document.body) { setTimeout(_initObserver, 50); return; }
const obs = new MutationObserver(muts => {
for(const m of muts){
if(m.type === 'attributes'){
const t = m.target;
if(t && t.classList && t.classList.contains('feedback')) _processFeedback(t);
} else if(m.type === 'childList'){
m.addedNodes && m.addedNodes.forEach(n => {
if(n.nodeType === 1 && n.classList && n.classList.contains('feedback')) _processFeedback(n);
});
}
}
});
obs.observe(document.body, { subtree:true, attributes:true, childList:true, attributeFilter:['class','style'] });
}
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', _initObserver);
else _initObserver();
/* ============================================================
ВИЗУАЛИЗАТОР КВАДРАТА СУММЫ (Ch2 §12)
Геометрическое доказательство (a+b)² = a² + 2ab + b²
============================================================ */
ALG7.buildQuadSumViz = function(container){
if(!container) return;
container.innerHTML = ''
+ '<div class="alg7-qsum-stage">'
+ '<div class="alg7-qsum-controls">'
+ '<label>$a$ = <b id="alg7-qsum-a">3</b><input type="range" id="alg7-qsum-a-sl" min="1" max="6" step="1" value="3"></label>'
+ '<label>$b$ = <b id="alg7-qsum-b">2</b><input type="range" id="alg7-qsum-b-sl" min="1" max="5" step="1" value="2"></label>'
+ '</div>'
+ '<div class="alg7-qsum-mode">'
+ '<button data-mode="sum" class="active">Сумма $(a+b)$</button>'
+ '<button data-mode="diff">Разность $(a-b)$</button>'
+ '</div>'
+ '<svg class="alg7-qsum-svg" id="alg7-qsum-svg" viewBox="0 0 320 320"></svg>'
+ '<div class="alg7-qsum-formula" id="alg7-qsum-formula"></div>'
+ '<div style="font-size:.85rem;color:var(--muted);text-align:center;max-width:380px">Кликни цветную область — она «подсветится» в формуле</div>'
+ '</div>';
const aSl = container.querySelector('#alg7-qsum-a-sl');
const bSl = container.querySelector('#alg7-qsum-b-sl');
const aV = container.querySelector('#alg7-qsum-a');
const bV = container.querySelector('#alg7-qsum-b');
const svg = container.querySelector('#alg7-qsum-svg');
const formula = container.querySelector('#alg7-qsum-formula');
const modeButtons = container.querySelectorAll('.alg7-qsum-mode button');
let mode = 'sum';
function render(){
const a = +aSl.value;
const b = +bSl.value;
aV.textContent = a;
bV.textContent = b;
const isSum = mode === 'sum';
/* Масштаб: общая сторона = a + b (для sum) или a (для diff) */
const side = isSum ? (a + b) : a;
const PX = 300; /* размер квадрата в px */
const offX = 10, offY = 10;
const unit = PX / side;
const aPx = a * unit;
const bPx = b * unit;
let html = '';
if(isSum){
/* Большой квадрат (a+b) × (a+b) разбит на 4 области:
a×a (top-left), a×b (top-right), b×a (bottom-left), b×b (bottom-right) */
/* 1. a² (top-left, красный) */
html += '<rect class="area-aa" x="'+offX+'" y="'+offY+'" width="'+aPx+'" height="'+aPx+'" data-part="aa" rx="2"/>';
/* 2. a*b (top-right, оранжевый) */
html += '<rect class="area-ab" x="'+(offX+aPx)+'" y="'+offY+'" width="'+bPx+'" height="'+aPx+'" data-part="ab1" rx="2"/>';
/* 3. b*a (bottom-left, оранжевый) */
html += '<rect class="area-ab" x="'+offX+'" y="'+(offY+aPx)+'" width="'+aPx+'" height="'+bPx+'" data-part="ab2" rx="2"/>';
/* 4. b² (bottom-right, зелёный) */
html += '<rect class="area-bb" x="'+(offX+aPx)+'" y="'+(offY+aPx)+'" width="'+bPx+'" height="'+bPx+'" data-part="bb" rx="2"/>';
/* Подписи */
if(aPx > 32) html += '<text class="area-label" x="'+(offX+aPx/2)+'" y="'+(offY+aPx/2+5)+'">a²='+(a*a)+'</text>';
if(bPx > 22 && aPx > 28) html += '<text class="area-label" x="'+(offX+aPx+bPx/2)+'" y="'+(offY+aPx/2+5)+'">ab='+(a*b)+'</text>';
if(bPx > 22 && aPx > 28) html += '<text class="area-label" x="'+(offX+aPx/2)+'" y="'+(offY+aPx+bPx/2+5)+'">ab='+(a*b)+'</text>';
if(bPx > 32) html += '<text class="area-label" x="'+(offX+aPx+bPx/2)+'" y="'+(offY+aPx+bPx/2+5)+'">b²='+(b*b)+'</text>';
/* Стороны (внешняя разметка) */
html += '<text x="'+(offX+aPx/2)+'" y="'+(offY+PX+22)+'" text-anchor="middle" font-size="13" fill="#475569" font-family="JetBrains Mono">a='+a+'</text>';
html += '<text x="'+(offX+aPx+bPx/2)+'" y="'+(offY+PX+22)+'" text-anchor="middle" font-size="13" fill="#475569" font-family="JetBrains Mono">b='+b+'</text>';
html += '<text x="'+(offX+PX/2)+'" y="'+(offY-2)+'" text-anchor="middle" font-size="12" fill="#1e293b" font-weight="700" font-family="Unbounded">сторона = a+b = '+(a+b)+'</text>';
/* Скобка снизу */
html += '<line x1="'+offX+'" y1="'+(offY+PX+4)+'" x2="'+(offX+PX)+'" y2="'+(offY+PX+4)+'" stroke="#94a3b8" stroke-width="1.2"/>';
html += '<line x1="'+offX+'" y1="'+(offY+PX+4)+'" x2="'+offX+'" y2="'+(offY+PX+10)+'" stroke="#94a3b8" stroke-width="1.2"/>';
html += '<line x1="'+(offX+PX)+'" y1="'+(offY+PX+4)+'" x2="'+(offX+PX)+'" y2="'+(offY+PX+10)+'" stroke="#94a3b8" stroke-width="1.2"/>';
/* Формула */
formula.innerHTML =
'$(' + a + ' + ' + b + ')^2 = ' +
'\\underbrace{' + a + '^2}_{\\color{#dc2626}{' + (a*a) + '}} + ' +
'\\underbrace{2 \\cdot ' + a + ' \\cdot ' + b + '}_{\\color{#d97706}{' + (2*a*b) + '}} + ' +
'\\underbrace{' + b + '^2}_{\\color{#047857}{' + (b*b) + '}} = ' +
((a+b)*(a+b)) + '$';
} else {
/* DIFF mode: показываем (a-b)² внутри a×a, выделяя верхний-левый угол */
if(b >= a){
html += '<text x="160" y="160" text-anchor="middle" font-size="14" fill="#dc2626" font-family="Inter,sans-serif">$b \\ge a$ — выбери $b < a$</text>';
formula.innerHTML = '<span style="color:#dc2626">Условие: $b < a$ для $(a-b)^2$</span>';
} else {
const diff = a - b;
const dPx = diff * unit;
/* (a-b)² в углу (красный) */
html += '<rect class="area-aa" x="'+offX+'" y="'+offY+'" width="'+dPx+'" height="'+dPx+'" rx="2"/>';
/* Полосы 2ab внутри */
html += '<rect class="area-ab" x="'+(offX+dPx)+'" y="'+offY+'" width="'+bPx+'" height="'+dPx+'" rx="2"/>';
html += '<rect class="area-ab" x="'+offX+'" y="'+(offY+dPx)+'" width="'+dPx+'" height="'+bPx+'" rx="2"/>';
/* b² в углу */
html += '<rect class="area-bb" x="'+(offX+dPx)+'" y="'+(offY+dPx)+'" width="'+bPx+'" height="'+bPx+'" rx="2"/>';
/* Подписи */
if(dPx > 30) html += '<text class="area-label" x="'+(offX+dPx/2)+'" y="'+(offY+dPx/2+5)+'">(a-b)²='+(diff*diff)+'</text>';
if(bPx > 22 && dPx > 25) html += '<text class="area-label" x="'+(offX+dPx+bPx/2)+'" y="'+(offY+dPx/2+5)+'">'+(diff*b)+'</text>';
if(bPx > 22 && dPx > 25) html += '<text class="area-label" x="'+(offX+dPx/2)+'" y="'+(offY+dPx+bPx/2+5)+'">'+(diff*b)+'</text>';
if(bPx > 30) html += '<text class="area-label" x="'+(offX+dPx+bPx/2)+'" y="'+(offY+dPx+bPx/2+5)+'">b²='+(b*b)+'</text>';
html += '<text x="'+(offX+PX/2)+'" y="'+(offY-2)+'" text-anchor="middle" font-size="12" fill="#1e293b" font-weight="700" font-family="Unbounded">сторона a='+a+'; b='+b+'; ab='+diff+'</text>';
/* Формула */
formula.innerHTML =
'$(' + a + ' - ' + b + ')^2 = a^2 - 2ab + b^2 = ' +
a + '^2 - 2 \\cdot ' + a + ' \\cdot ' + b + ' + ' + b + '^2 = ' +
(a*a) + ' - ' + (2*a*b) + ' + ' + (b*b) + ' = ' + (diff*diff) + '$';
}
}
svg.innerHTML = html;
ALG7.renderMath(formula);
/* Hover-эффект: при клике подсветить термин */
svg.querySelectorAll('rect[data-part]').forEach(r=>{
r.style.cursor = 'pointer';
r.addEventListener('click', ()=>{
const part = r.dataset.part;
svg.querySelectorAll('rect.area-aa, rect.area-ab, rect.area-bb').forEach(x=>x.classList.add('dim'));
const sameClass = part==='aa' ? 'area-aa' : (part==='bb' ? 'area-bb' : 'area-ab');
svg.querySelectorAll('.' + sameClass).forEach(x=>x.classList.remove('dim'));
setTimeout(()=>{ svg.querySelectorAll('rect.area-aa, rect.area-ab, rect.area-bb').forEach(x=>x.classList.remove('dim')); }, 1500);
});
});
}
aSl.addEventListener('input', render);
bSl.addEventListener('input', render);
modeButtons.forEach(btn => btn.addEventListener('click', ()=>{
modeButtons.forEach(b=>b.classList.remove('active'));
btn.classList.add('active');
mode = btn.dataset.mode;
render();
}));
render();
};
/* ============================================================
ВИЗУАЛИЗАТОР РАЗНОСТИ КВАДРАТОВ (Ch2 §13)
a² - b² = (a-b)(a+b) — анимация перестроения
============================================================ */
ALG7.buildDiffSquaresViz = function(container){
if(!container) return;
container.innerHTML = ''
+ '<div class="alg7-dsq-stage">'
+ '<div class="alg7-qsum-controls">'
+ '<label>$a$ = <b id="alg7-dsq-a">5</b><input type="range" id="alg7-dsq-a-sl" min="3" max="8" step="1" value="5"></label>'
+ '<label>$b$ = <b id="alg7-dsq-b">2</b><input type="range" id="alg7-dsq-b-sl" min="1" max="6" step="1" value="2"></label>'
+ '</div>'
+ '<svg class="alg7-dsq-svg" id="alg7-dsq-svg" viewBox="0 0 460 280"></svg>'
+ '<div class="alg7-dsq-formula" id="alg7-dsq-formula"></div>'
+ '<button class="alg7-dsq-step-btn" id="alg7-dsq-step">'
+ '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:18px;height:18px"><polygon points="6 4 20 12 6 20 6 4"/></svg>'
+ '<span id="alg7-dsq-step-text">Шаг 1: вырезать $b^2$</span>'
+ '</button>'
+ '</div>';
const aSl = container.querySelector('#alg7-dsq-a-sl');
const bSl = container.querySelector('#alg7-dsq-b-sl');
const aV = container.querySelector('#alg7-dsq-a');
const bV = container.querySelector('#alg7-dsq-b');
const svg = container.querySelector('#alg7-dsq-svg');
const formula = container.querySelector('#alg7-dsq-formula');
const stepBtn = container.querySelector('#alg7-dsq-step');
const stepText = container.querySelector('#alg7-dsq-step-text');
let stage = 0; /* 0 = большой a² с вырезанной b²; 1 = две части L-формы; 2 = собранный прямоугольник */
function render(){
const a = +aSl.value;
const b = +bSl.value;
aV.textContent = a;
bV.textContent = b;
if(b >= a){
svg.innerHTML = '<text x="230" y="140" text-anchor="middle" font-size="14" fill="#dc2626" font-family="Inter,sans-serif">Выбери $b &lt; a$</text>';
formula.innerHTML = '<span style="color:#dc2626">Условие: $b < a$</span>';
stepBtn.disabled = true;
return;
}
stepBtn.disabled = false;
const diff = a - b;
const sum = a + b;
/* Площади 200x200 для исходного, 200x200 для финального — рядом */
const PX = 200;
const unit = PX / Math.max(a, sum); /* единичная клетка */
const aPx = a * unit;
const bPx = b * unit;
const dPx = diff * unit;
const sumPx = sum * unit;
const offX = 20, offY = 30;
const off2X = 250, off2Y = 30;
let html = '';
/* === ЭТАП 0 — исходное состояние === */
/* Левая сторона: большой квадрат a² */
html += '<text x="'+(offX+aPx/2)+'" y="'+(offY-8)+'" text-anchor="middle" font-size="12" fill="#1e293b" font-weight="700" font-family="Unbounded">a² (сторона a='+a+')</text>';
/* Большой квадрат (рыжий) */
html += '<rect class="big-square" x="'+offX+'" y="'+offY+'" width="'+aPx+'" height="'+aPx+'" rx="2"/>';
/* Голубой квадрат b² в правом верхнем углу */
if(stage >= 0){
html += '<rect class="small-square" x="'+(offX+aPx-bPx)+'" y="'+offY+'" width="'+bPx+'" height="'+bPx+'" rx="2"/>';
html += '<text class="label" x="'+(offX+aPx-bPx/2)+'" y="'+(offY+bPx/2+5)+'">b²='+(b*b)+'</text>';
}
/* Подпись большой стороны */
html += '<text class="label dark" x="'+(offX+aPx/2)+'" y="'+(offY+aPx/2+5)+'" style="font-size:14px;fill:#fff">a²='+(a*a)+'</text>';
/* === ЭТАП 1+ — L-форма (большой квадрат минус b²) разделена на 2 прямоугольника === */
/* L-форма состоит из двух частей:
- Нижний прямоугольник: a × (a-b) = a × diff
- Верхний-левый прямоугольник: (a-b) × b = diff × b
Они собираются в прямоугольник (a-b) × (a+b)
*/
/* Этап 1: показываем линию разреза в исходном квадрате */
if(stage === 1){
const cutY = offY + dPx; /* горизонтальная линия разреза на высоте a-b */
html += '<line x1="'+offX+'" y1="'+cutY+'" x2="'+(offX+aPx)+'" y2="'+cutY+'" stroke="#1e293b" stroke-width="2" stroke-dasharray="6 3"/>';
html += '<text x="'+(offX+aPx+20)+'" y="'+(cutY+4)+'" font-size="11" fill="#1e293b" font-family="JetBrains Mono">разрез</text>';
}
/* Этап 2: показываем «улетающие» части и финальный прямоугольник справа */
if(stage === 2){
/* Призрак исходного квадрата */
html += '<rect class="ghost-rect" x="'+offX+'" y="'+offY+'" width="'+aPx+'" height="'+aPx+'"/>';
/* Финальный прямоугольник (a-b) × (a+b) справа */
html += '<text x="'+(off2X+sumPx/2)+'" y="'+(off2Y-8)+'" text-anchor="middle" font-size="12" fill="#1e293b" font-weight="700" font-family="Unbounded">(a-b)(a+b) = '+(diff*sum)+'</text>';
/* Часть 1: нижний прямоугольник a × (a-b) — поворачиваем в финале на правую часть */
html += '<rect class="rect-piece" x="'+off2X+'" y="'+off2Y+'" width="'+(a*unit)+'" height="'+dPx+'" rx="2"/>';
html += '<text class="label" x="'+(off2X+a*unit/2)+'" y="'+(off2Y+dPx/2+5)+'">a·(a-b)='+(a*diff)+'</text>';
/* Часть 2: верхний-левый прямоугольник (a-b) × b — приклеиваем к правому концу */
html += '<rect class="rect-piece" x="'+(off2X+a*unit)+'" y="'+off2Y+'" width="'+bPx+'" height="'+dPx+'" rx="2"/>';
html += '<text class="label" x="'+(off2X+a*unit+bPx/2)+'" y="'+(off2Y+dPx/2+5)+'">b(a-b)='+(b*diff)+'</text>';
/* Подписи сторон финального прямоугольника */
html += '<text x="'+(off2X+sumPx/2)+'" y="'+(off2Y+dPx+18)+'" text-anchor="middle" font-size="11" fill="#475569" font-family="JetBrains Mono">a+b='+sum+'</text>';
html += '<text x="'+(off2X-8)+'" y="'+(off2Y+dPx/2+3)+'" text-anchor="end" font-size="11" fill="#475569" font-family="JetBrains Mono">a-b='+diff+'</text>';
/* Линия разделения */
html += '<line x1="'+(off2X+a*unit)+'" y1="'+off2Y+'" x2="'+(off2X+a*unit)+'" y2="'+(off2Y+dPx)+'" stroke="#fff" stroke-width="1.5" stroke-dasharray="4 3"/>';
}
svg.innerHTML = html;
/* Формула */
let formulaText;
if(stage === 0){
formulaText = '$a^2 - b^2 = ' + a + '^2 - ' + b + '^2 = ' + (a*a) + ' - ' + (b*b) + ' = ' + (a*a - b*b) + '$';
stepText.textContent = 'Шаг 1: разрезать L-форму';
} else if(stage === 1){
formulaText = 'Разрезаем L-форму горизонтально: $a \\cdot (a-b)$ + $(a-b) \\cdot b$';
stepText.textContent = 'Шаг 2: собрать в прямоугольник';
} else {
formulaText = '$a^2 - b^2 = (a-b)(a+b) = ' + diff + ' \\cdot ' + sum + ' = ' + (diff*sum) + '$ ✓';
stepText.textContent = 'Сбросить';
}
formula.innerHTML = formulaText;
ALG7.renderMath(formula);
}
stepBtn.addEventListener('click', ()=>{
stage = (stage + 1) % 3;
render();
});
aSl.addEventListener('input', ()=>{ stage = 0; render(); });
bSl.addEventListener('input', ()=>{ stage = 0; render(); });
render();
};
})();