// 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 += '
'+c.label+'
'; }); 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 '
'+badge+'
'+title+'
'+help+'
'+body+'
'; } 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 = 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 = '
' +'' +'' +'' +'' +'
' +'
' +'$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 = '
' +'' +'' +'' +'
' +'
' +'$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 }; })();