// phys9_ch3_widgets.js — виджеты для Физики 9, Глава 3 (§25-§30): статика и гидростатика.
(function(){
'use strict';
const C = () => window.PHYS9_COLORS || {};
const PI = Math.PI;
function dndPool(secId, items, cats){
let p='
';
items.forEach(it=>{ p += '
'+it.html+'
'; });
p += '
';
cats.forEach(c=>{ p += '
'; });
return p + '
';
}
function wireDnd(scopeId, items){
const scope = document.querySelector('#'+scopeId); if(!scope) return;
scope.querySelectorAll('.dnd-chip').forEach(chip=>{
chip.addEventListener('dragstart', e=>{ e.dataTransfer.setData('text/plain', chip.dataset.id); chip.style.opacity='0.5'; });
chip.addEventListener('dragend', e=>{ chip.style.opacity='1'; });
});
scope.querySelectorAll('.drop-box').forEach(box=>{
box.addEventListener('dragover', e=>{ e.preventDefault(); box.style.borderColor=C().force||'#10b981'; });
box.addEventListener('dragleave', e=>{ box.style.borderColor=''; });
box.addEventListener('drop', e=>{
e.preventDefault(); box.style.borderColor='';
const id = e.dataTransfer.getData('text/plain');
const chip = scope.querySelector('.dnd-chip[data-id="'+id+'"]');
if(chip) box.querySelector('.drop-items').appendChild(chip);
});
});
scope.querySelector('.dnd-check').addEventListener('click', ()=>{
let wrong = 0; const total = items.length;
scope.querySelectorAll('.drop-box').forEach(box=>{
const cat = box.dataset.cat;
box.querySelectorAll('.dnd-chip').forEach(chip=>{ if(chip.dataset.cat !== cat) wrong++; });
});
let placed = 0; scope.querySelectorAll('.drop-box .dnd-chip').forEach(()=>placed++);
const fb = scope.querySelector('.dnd-fb');
if(placed < total){ fb.className='feedback fail'; fb.innerHTML='Распредели все — осталось '+(total-placed)+'.'; return; }
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='✓ Идеально!'; }
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'.'; }
});
}
function wgWrapper(secId, badge, title, help, body){
return '';
}
function appendTo(secId, html){
const box = document.getElementById(secId+'-body');
if(!box) return false;
if(box.querySelector('.wg-phys9-extra-'+secId)) return false;
const div = document.createElement('div'); div.className = 'wg-phys9-extra-'+secId; div.innerHTML = html;
box.appendChild(div);
try { if(window.renderMathInElement) window.renderMathInElement(box, { delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}], throwOnError:false }); } catch(e){}
return true;
}
/* ====== §25 — Равновесие рычага F₁ l₁ = F₂ l₂ ====== */
function add_p25(){
const body = ''
+'$m_1$, кг: 2 '
+'$l_1$, м: 1.5 '
+'$m_2$, кг: 3 '
+'$l_2$, м: 1.0 '
+'
'
+' '
+''
+'$M_1 = m_1 g l_1$ = 29.4 Н·м '
+'$M_2 = m_2 g l_2$ = 29.4 Н·м '
+'РАВНОВЕСИЕ ✓ '
+'
';
if(appendTo('p25', wgWrapper('p25-extra', 'CALC+VIS', 'Равновесие рычага', '$F_1 l_1 = F_2 l_2$ — моменты сил равны.', body))){
const upd = ()=>{
const m1 = +document.getElementById('p25w-m1-r').value;
const l1 = +document.getElementById('p25w-l1-r').value;
const m2 = +document.getElementById('p25w-m2-r').value;
const l2 = +document.getElementById('p25w-l2-r').value;
document.getElementById('p25w-m1').textContent = m1;
document.getElementById('p25w-l1').textContent = l1.toFixed(1);
document.getElementById('p25w-m2').textContent = m2;
document.getElementById('p25w-l2').textContent = l2.toFixed(1);
const M1 = m1*9.8*l1, M2 = m2*9.8*l2;
document.getElementById('p25w-M1').textContent = M1.toFixed(1);
document.getElementById('p25w-M2').textContent = M2.toFixed(1);
const eq = document.getElementById('p25w-eq');
if(Math.abs(M1-M2) < 0.5){ eq.innerHTML = 'РАВНОВЕСИЕ ✓'; eq.style.color = 'var(--ok,#10b981)'; }
else if(M1 > M2){ eq.innerHTML = 'ЛЕВАЯ ПЕРЕВЕШИВАЕТ ⤵'; eq.style.color = 'var(--fail,#dc2626)'; }
else { eq.innerHTML = 'ПРАВАЯ ПЕРЕВЕШИВАЕТ ⤵'; eq.style.color = 'var(--fail,#dc2626)'; }
/* SVG */
const col = C();
const tilt = Math.max(-15, Math.min(15, (M2-M1)*0.3));
const cx = 230, cy = 100;
const len = 180;
const rad = tilt*PI/180;
const lx = cx - len*Math.cos(rad), ly = cy + len*Math.sin(rad);
const rx = cx + len*Math.cos(rad), ry = cy - len*Math.sin(rad);
let s = '';
/* опора */
s += ' ';
/* балка */
s += ' ';
/* грузы */
const r1 = Math.min(20, 5 + m1*2);
const r2 = Math.min(20, 5 + m2*2);
s += ' ';
s += ' ';
/* подписи */
s += ''+m1+' кг ';
s += ''+m2+' кг ';
document.getElementById('p25w-svg').innerHTML = s;
};
['p25w-m1-r','p25w-l1-r','p25w-m2-r','p25w-l2-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd));
upd();
}
}
/* ====== §26 — Простые механизмы ====== */
function add_p26(){
const items = [
{id:'i1', cat:'force', html:'рычаг с длинным плечом'},
{id:'i2', cat:'force', html:'наклонная плоскость (длинная)'},
{id:'i3', cat:'force', html:'неподвижный блок + полиспаст'},
{id:'i4', cat:'force', html:'клин'},
{id:'i5', cat:'dist', html:'рычаг с коротким плечом (метла)'},
{id:'i6', cat:'dist', html:'педаль велосипеда'},
{id:'i7', cat:'none', html:'неподвижный блок (один)'},
{id:'i8', cat:'none', html:'жёсткий стержень'}
];
const body = dndPool('p26ex', items, [
{cat:'force', label:'Выигрыш в силе'},
{cat:'dist', label:'Выигрыш в скор./пути'},
{cat:'none', label:'Без выигрыша'}
]) + 'Проверить
';
if(appendTo('p26', wgWrapper('p26-extra', 'DnD', 'Что даёт выигрыш?', 'Золотое правило: выигрываем в силе — проигрываем в расстоянии. И наоборот.', body))){
wireDnd('p26-extra', items);
}
}
/* ====== §27 — КПД наклонной плоскости ====== */
function add_p27(){
const body = ''
+'$m$ груза, кг: 10 '
+'$h$ высота, м: 1 '
+'$\\alpha$ угол, °: 30 '
+'$\\mu$ трение: 0.2 '
+'
'
+''
+'$A_{пол} = m g h$ = 98 Дж '
+'$A_{зат} = F \\cdot l$ = 131 Дж '
+'$\\eta$ = 75 % '
+'
';
if(appendTo('p27', wgWrapper('p27-extra', 'CALC', 'КПД наклонной плоскости', 'Полезная работа = $mgh$. Затраченная = $F \\cdot l$, где $F$ учитывает трение.', body))){
const upd = ()=>{
const m = +document.getElementById('p27w-m-r').value;
const h = +document.getElementById('p27w-h-r').value;
const a = +document.getElementById('p27w-an-r').value;
const mu = +document.getElementById('p27w-mu-r').value;
document.getElementById('p27w-m').textContent = m;
document.getElementById('p27w-h').textContent = h.toFixed(1);
document.getElementById('p27w-an').textContent = a;
document.getElementById('p27w-mu').textContent = mu.toFixed(2);
const g = 9.8;
const l = h / Math.sin(a*PI/180);
const F = m*g*(Math.sin(a*PI/180) + mu*Math.cos(a*PI/180));
const Apol = m*g*h;
const Azat = F*l;
const eta = (Apol/Azat)*100;
document.getElementById('p27w-Apol').textContent = Apol.toFixed(0);
document.getElementById('p27w-Azat').textContent = Azat.toFixed(0);
document.getElementById('p27w-eta').textContent = eta.toFixed(0);
};
['p27w-m-r','p27w-h-r','p27w-an-r','p27w-mu-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd));
upd();
}
}
/* ====== §28 — Виды равновесия ====== */
function add_p28(){
const items = [
{id:'i1', cat:'st', html:'шар в углублении'},
{id:'i2', cat:'st', html:'маятник в нижней точке'},
{id:'i3', cat:'st', html:'столб с широким основанием'},
{id:'i4', cat:'un', html:'шар на вершине горы'},
{id:'i5', cat:'un', html:'карандаш на остром конце'},
{id:'i6', cat:'un', html:'пирамида на вершине'},
{id:'i7', cat:'in', html:'шар на горизонтальном столе'},
{id:'i8', cat:'in', html:'цилиндр на ровной поверхности'}
];
const body = dndPool('p28ex', items, [
{cat:'st', label:'Устойчивое'},
{cat:'un', label:'Неустойчивое'},
{cat:'in', label:'Безразличное'}
]) + 'Проверить
';
if(appendTo('p28', wgWrapper('p28-extra', 'DnD', 'Вид равновесия', 'Устойчивое — возврат, неустойчивое — уход, безразличное — без изменений.', body))){
wireDnd('p28-extra', items);
}
}
/* ====== §29 — Закон Архимеда ====== */
function add_p29(){
const body = ''
+'$V$ тела, см³: 100 '
+'Жидкость: вода (1000) керосин (800) ртуть (13600) спирт (789) '
+'$\\rho_{тела}$, кг/м³: 500 '
+'
'
+''
+'$F_A = \\rho g V$ = 0.98 Н '
+'Вес тела $P$ = 0.49 Н '
+'ПЛАВАЕТ ↑ '
+'
';
if(appendTo('p29', wgWrapper('p29-extra', 'CALC', 'Закон Архимеда', '$F_A = \\rho g V$. Сравни с весом тела.', body))){
const upd = ()=>{
const V_cm3 = +document.getElementById('p29w-V-r').value;
const V = V_cm3 * 1e-6;
const rho_l = +document.getElementById('p29w-liq').value;
const rho_t = +document.getElementById('p29w-rt-r').value;
document.getElementById('p29w-V').textContent = V_cm3;
document.getElementById('p29w-rt').textContent = rho_t;
const g = 9.8;
const Fa = rho_l*g*V;
const P = rho_t*g*V;
document.getElementById('p29w-Fa').textContent = Fa.toFixed(2);
document.getElementById('p29w-P').textContent = P.toFixed(2);
const r = document.getElementById('p29w-result');
if(Math.abs(Fa-P) < 0.01){ r.innerHTML = 'ВИСИТ В ТОЛЩЕ →'; r.style.color = 'var(--muted)'; }
else if(Fa > P){ r.innerHTML = 'ПЛАВАЕТ ↑'; r.style.color = 'var(--ok,#10b981)'; }
else { r.innerHTML = 'ТОНЕТ ↓'; r.style.color = 'var(--fail,#dc2626)'; }
};
['p29w-V-r','p29w-liq','p29w-rt-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd));
document.getElementById('p29w-liq').addEventListener('change', upd);
upd();
}
}
/* ====== §30 — Плотности жидкостей по возрастанию ====== */
function add_p30(){
const items = [
{id:'i1', cat:'r1', html:'спирт ($789$)'},
{id:'i2', cat:'r2', html:'керосин ($800$)'},
{id:'i3', cat:'r3', html:'вода ($1000$)'},
{id:'i4', cat:'r4', html:'морская вода ($1030$)'},
{id:'i5', cat:'r5', html:'ртуть ($13600$)'}
];
const body = dndPool('p30ex', items, [
{cat:'r1', label:'$<800$'},
{cat:'r2', label:'$800–999$'},
{cat:'r3', label:'$1000–1029$'},
{cat:'r4', label:'$1030–1500$'},
{cat:'r5', label:'$>10000$'}
]) + 'Проверить
';
if(appendTo('p30', wgWrapper('p30-extra', 'DnD', 'Плотности жидкостей (кг/м³)', 'Расставь жидкости по группам плотности.', body))){
wireDnd('p30-extra', items);
}
}
window.PHYS9_CH3_WIDGETS = { p25:add_p25, p26:add_p26, p27:add_p27, p28:add_p28, p29:add_p29, p30:add_p30 };
})();