From 70aad6a4234a06a5e80b11bcbc45e1eaa35ff6b9 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sat, 30 May 2026 09:53:29 +0300 Subject: [PATCH] =?UTF-8?q?feat(phys9=20ch5):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=2012=20=D0=B2=D0=B8=D0=B4=D0=B6?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B2=20Wave=20E=20=E2=80=94=20=D0=9B=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=BA=D1=82=D0=B8=D0=BA=D1=83=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый модуль frontend/js/phys9_ch5_widgets.js — экспортирует window.PHYS9_CH5_WIDGETS = { lr1..lr12: fn }. Каждая ЛР содержит: - 2-4 slider'а с параметрами измерений - Автоматический расчёт результата - Кнопка «Сдать работу (+30 XP)» с интеграцией в XP-систему Виджеты: - ЛР 1: средняя скорость на 2 участках - ЛР 2: ускорение через 2 измерения s/t² - ЛР 3: a_n по 10 оборотам шарика на нити - ЛР 4: g через период математического маятника + погрешность - ЛР 5: проекции силы на наклонной (F‖, F⊥) - ЛР 6: g = 2h/t² свободное падение + погрешность - ЛР 7: ЗСМЭ — сравнение Ep и Ek с расчётом потерь - ЛР 8: F_A и V тела (вес в воздухе и в воде) - ЛР 9: условие плавания — доля погружения, статус - ЛР 10: равновесие рычага — l₂ для баланса - ЛР 11: КПД наклонной — A_пол/A_зат - ЛР 12: жёсткость пружины k через период Подключено в physics_9_ch5.html через ensureBuilt hook. Co-Authored-By: Claude Opus 4.7 --- frontend/js/phys9_ch5_widgets.js | 429 ++++++++++++++++++++++++++ frontend/textbooks/physics_9_ch5.html | 3 +- 2 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 frontend/js/phys9_ch5_widgets.js diff --git a/frontend/js/phys9_ch5_widgets.js b/frontend/js/phys9_ch5_widgets.js new file mode 100644 index 0000000..88460be --- /dev/null +++ b/frontend/js/phys9_ch5_widgets.js @@ -0,0 +1,429 @@ +// phys9_ch5_widgets.js — виджеты для Физики 9, Глава 5 (ЛР 1-12). +(function(){ +'use strict'; + +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); } catch(e){} + return true; +} +function wireSubmit(id){ + const btn = document.getElementById(id+'-sub'); if(!btn) return; + btn.addEventListener('click', ()=>{ + const fb = document.getElementById(id+'-fb'); + fb.className='feedback ok'; + fb.innerHTML='✓ Работа сдана! +30 XP. Молодец, лаборант!'; + btn.disabled = true; btn.style.opacity = 0.6; + try { if(window.addXp) window.addXp(30, id); } catch(e){} + }); +} + +/* === ЛР 1. Определение средней скорости === */ +function add_lr1(){ + const body = '
' + +'' + +'' + +'' + +'' + +'
' + +'
' + +'Полный путь $s$ = 3.0 м, время $t$ = 5.0 с' + +'$\\langle v\\rangle = s/t$ = 0.60 м/с' + +'
' + +'
' + +''; + if(appendTo('lr1', wgWrapper('lr1-extra', 'ЛР 1', 'Определение средней скорости', 'Засеки время на 2 участках, рассчитай среднюю скорость.', body))){ + const upd = ()=>{ + const s1 = +document.getElementById('lr1w-s1-r').value; + const t1 = +document.getElementById('lr1w-t1-r').value; + const s2 = +document.getElementById('lr1w-s2-r').value; + const t2 = +document.getElementById('lr1w-t2-r').value; + document.getElementById('lr1w-s1').textContent = s1.toFixed(2); + document.getElementById('lr1w-t1').textContent = t1.toFixed(2); + document.getElementById('lr1w-s2').textContent = s2.toFixed(2); + document.getElementById('lr1w-t2').textContent = t2.toFixed(2); + const s = s1+s2, t = t1+t2; + document.getElementById('lr1w-s').textContent = s.toFixed(2); + document.getElementById('lr1w-t').textContent = t.toFixed(2); + document.getElementById('lr1w-vv').textContent = (s/t).toFixed(2); + }; + ['lr1w-s1-r','lr1w-t1-r','lr1w-s2-r','lr1w-t2-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd)); + upd(); + wireSubmit('lr1'); + } +} + +/* === ЛР 2. Изучение равноускоренного движения === */ +function add_lr2(){ + const body = '
' + +'' + +'' + +'' + +'' + +'
' + +'
' + +'$a = 2 s_1 / t_1^2$ = 1.0 м/с² (по 1-му)' + +'$a = 2 s_2 / t_2^2$ = 1.0 м/с² (по 2-му)' + +'Среднее: $a$ = 1.0 м/с²' + +'
' + +'
' + +''; + if(appendTo('lr2', wgWrapper('lr2-extra', 'ЛР 2', 'Равноускоренное движение', 'Тележка скатывается с начала и проходит $s_1$ за $t_1$, потом $s_2$ за $t_2$ от старта. $a = 2s/t^2$.', body))){ + const upd = ()=>{ + const s1 = +document.getElementById('lr2w-s1-r').value; + const t1 = +document.getElementById('lr2w-t1-r').value; + const s2 = +document.getElementById('lr2w-s2-r').value; + const t2 = +document.getElementById('lr2w-t2-r').value; + document.getElementById('lr2w-s1').textContent = s1.toFixed(2); + document.getElementById('lr2w-t1').textContent = t1.toFixed(2); + document.getElementById('lr2w-s2').textContent = s2.toFixed(2); + document.getElementById('lr2w-t2').textContent = t2.toFixed(2); + const a1 = 2*s1/(t1*t1), a2 = 2*s2/(t2*t2); + document.getElementById('lr2w-a1').textContent = a1.toFixed(2); + document.getElementById('lr2w-a2').textContent = a2.toFixed(2); + document.getElementById('lr2w-a').textContent = ((a1+a2)/2).toFixed(2); + }; + ['lr2w-s1-r','lr2w-t1-r','lr2w-s2-r','lr2w-t2-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd)); + upd(); + wireSubmit('lr2'); + } +} + +/* === ЛР 3. Движение по окружности === */ +function add_lr3(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'$T$ = $t$/10 = 0.50 с' + +'$v$ = $2\\pi R/T$ = 3.77 м/с' + +'$a_n$ = $v^2/R$ = 47 м/с²' + +'
' + +'
' + +''; + if(appendTo('lr3', wgWrapper('lr3-extra', 'ЛР 3', 'Движение по окружности', 'Измерь время 10 оборотов шарика на нити длиной $R$.', body))){ + const upd = ()=>{ + const R = +document.getElementById('lr3w-R-r').value; + const t = +document.getElementById('lr3w-t-r').value; + document.getElementById('lr3w-R').textContent = R.toFixed(2); + document.getElementById('lr3w-t').textContent = t.toFixed(2); + const T = t/10; + const v = 2*Math.PI*R/T; + const an = v*v/R; + document.getElementById('lr3w-T').textContent = T.toFixed(2); + document.getElementById('lr3w-v').textContent = v.toFixed(2); + document.getElementById('lr3w-an').textContent = an.toFixed(1); + }; + document.getElementById('lr3w-R-r').addEventListener('input', upd); + document.getElementById('lr3w-t-r').addEventListener('input', upd); + upd(); + wireSubmit('lr3'); + } +} + +/* === ЛР 4. Маятник, нахождение g === */ +function add_lr4(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'$T$ = $t/10$ = 2.01 с' + +'$g = 4\\pi^2 l / T^2$ = 9.77 м/с²' + +'Эталон: $9{,}81$ м/с². Погрешность: 0.4%' + +'
' + +'
' + +''; + if(appendTo('lr4', wgWrapper('lr4-extra', 'ЛР 4', 'Определение $g$ маятником', 'Засеки время 10 полных колебаний, рассчитай $g$.', body))){ + const upd = ()=>{ + const l = +document.getElementById('lr4w-l-r').value; + const t = +document.getElementById('lr4w-t-r').value; + document.getElementById('lr4w-l').textContent = l.toFixed(2); + document.getElementById('lr4w-t').textContent = t.toFixed(1); + const T = t/10; + const g = 4*Math.PI*Math.PI*l/(T*T); + const err = Math.abs(g - 9.81)/9.81*100; + document.getElementById('lr4w-T').textContent = T.toFixed(2); + document.getElementById('lr4w-g').textContent = g.toFixed(2); + document.getElementById('lr4w-err').textContent = err.toFixed(1); + }; + document.getElementById('lr4w-l-r').addEventListener('input', upd); + document.getElementById('lr4w-t-r').addEventListener('input', upd); + upd(); + wireSubmit('lr4'); + } +} + +/* === ЛР 5. Сила тяги на наклонной === */ +function add_lr5(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'Сила тяжести $P = mg$ = 1.96 Н' + +'Проекция вдоль накл.: $F_\\parallel = mg\\sin\\alpha$ = 0.98 Н' + +'Прижим: $F_\\perp = mg\\cos\\alpha$ = 1.70 Н' + +'
' + +'
' + +''; + if(appendTo('lr5', wgWrapper('lr5-extra', 'ЛР 5', 'Силы на наклонной плоскости', 'Изменяй угол, наблюдай как меняются $F_\\parallel$ и $F_\\perp$.', body))){ + const upd = ()=>{ + const m = +document.getElementById('lr5w-m-r').value; + const a = +document.getElementById('lr5w-a-r').value; + document.getElementById('lr5w-m').textContent = m.toFixed(2); + document.getElementById('lr5w-a').textContent = a; + const g = 9.8; + const P = m*g; + const Fp = P*Math.sin(a*Math.PI/180); + const Fn = P*Math.cos(a*Math.PI/180); + document.getElementById('lr5w-P').textContent = P.toFixed(2); + document.getElementById('lr5w-Fp').textContent = Fp.toFixed(2); + document.getElementById('lr5w-Fn').textContent = Fn.toFixed(2); + }; + document.getElementById('lr5w-m-r').addEventListener('input', upd); + document.getElementById('lr5w-a-r').addEventListener('input', upd); + upd(); + wireSubmit('lr5'); + } +} + +/* === ЛР 6. Свободное падение g = 2h/t² === */ +function add_lr6(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'$g = 2h/t^2$ = 9.77 м/с²' + +'Эталон: $9{,}81$. Погрешность: 0.4%' + +'
' + +'
' + +''; + if(appendTo('lr6', wgWrapper('lr6-extra', 'ЛР 6', 'Свободное падение', 'Сбрось предмет, засеки время. $g = 2h/t^2$.', body))){ + const upd = ()=>{ + const h = +document.getElementById('lr6w-h-r').value; + const t = +document.getElementById('lr6w-t-r').value; + document.getElementById('lr6w-h').textContent = h.toFixed(2); + document.getElementById('lr6w-t').textContent = t.toFixed(2); + const g = 2*h/(t*t); + const err = Math.abs(g-9.81)/9.81*100; + document.getElementById('lr6w-g').textContent = g.toFixed(2); + document.getElementById('lr6w-err').textContent = err.toFixed(1); + }; + document.getElementById('lr6w-h-r').addEventListener('input', upd); + document.getElementById('lr6w-t-r').addEventListener('input', upd); + upd(); + wireSubmit('lr6'); + } +} + +/* === ЛР 7. Закон сохранения механической энергии === */ +function add_lr7(){ + const body = '
' + +'' + +'' + +'' + +'
' + +'
' + +'$E_p^{старт} = mgh$ = 0.49 Дж' + +'$E_k^{внизу} = mv^2/2$ = 0.45 Дж' + +'Сохранение: 8% потерь' + +'
' + +'
' + +''; + if(appendTo('lr7', wgWrapper('lr7-extra', 'ЛР 7', 'ЗСЭ на горке', 'Сравни $E_p$ вверху и $E_k$ внизу.', body))){ + const upd = ()=>{ + const m = +document.getElementById('lr7w-m-r').value; + const h = +document.getElementById('lr7w-h-r').value; + const v = +document.getElementById('lr7w-v-r').value; + document.getElementById('lr7w-m').textContent = m.toFixed(2); + document.getElementById('lr7w-h').textContent = h.toFixed(2); + document.getElementById('lr7w-v').textContent = v.toFixed(2); + const Ep = m*9.8*h; + const Ek = m*v*v/2; + document.getElementById('lr7w-Ep').textContent = Ep.toFixed(2); + document.getElementById('lr7w-Ek').textContent = Ek.toFixed(2); + const loss = Math.max(0, (Ep-Ek)/Ep*100); + document.getElementById('lr7w-cons').textContent = loss.toFixed(0)+'% потерь'; + }; + ['lr7w-m-r','lr7w-h-r','lr7w-v-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd)); + upd(); + wireSubmit('lr7'); + } +} + +/* === ЛР 8. Закон Архимеда === */ +function add_lr8(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'$F_A = P_1 - P_2$ = 0.50 Н' + +'$V = F_A/(\\rho g) = $ 51 см³' + +'
' + +'
' + +''; + if(appendTo('lr8', wgWrapper('lr8-extra', 'ЛР 8', 'Закон Архимеда', 'Взвешиваем тело в воздухе и в воде, находим $F_A$ и $V$.', body))){ + const upd = ()=>{ + const P1 = +document.getElementById('lr8w-P1-r').value; + const P2 = +document.getElementById('lr8w-P2-r').value; + document.getElementById('lr8w-P1').textContent = P1.toFixed(2); + document.getElementById('lr8w-P2').textContent = P2.toFixed(2); + const Fa = Math.max(0, P1-P2); + const V_m3 = Fa/(1000*9.8); + document.getElementById('lr8w-Fa').textContent = Fa.toFixed(2); + document.getElementById('lr8w-V').textContent = (V_m3*1e6).toFixed(0); + }; + document.getElementById('lr8w-P1-r').addEventListener('input', upd); + document.getElementById('lr8w-P2-r').addEventListener('input', upd); + upd(); + wireSubmit('lr8'); + } +} + +/* === ЛР 9. Плавание тел === */ +function add_lr9(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'Доля погружения: 70%' + +'ПЛАВАЕТ ↑' + +'
' + +'
' + +''; + if(appendTo('lr9', wgWrapper('lr9-extra', 'ЛР 9', 'Условие плавания', '$h_{погр}/h_{тела} = \\rho_{тела}/\\rho_{жидк}$.', body))){ + const upd = ()=>{ + const rt = +document.getElementById('lr9w-rt-r').value; + const rl = +document.getElementById('lr9w-rl-r').value; + document.getElementById('lr9w-rt').textContent = rt; + document.getElementById('lr9w-rl').textContent = rl; + const h = (rt/rl)*100; + document.getElementById('lr9w-h').textContent = Math.min(100, h).toFixed(0); + const r = document.getElementById('lr9w-r'); + if(rt < rl){ r.innerHTML='ПЛАВАЕТ ↑'; r.style.color='var(--ok,#10b981)'; } + else if(Math.abs(rt-rl) < 5){ r.innerHTML='ВЗВЕШЕНО →'; r.style.color='var(--muted)'; } + else { r.innerHTML='ТОНЕТ ↓'; r.style.color='var(--fail,#dc2626)'; } + }; + document.getElementById('lr9w-rt-r').addEventListener('input', upd); + document.getElementById('lr9w-rl-r').addEventListener('input', upd); + upd(); + wireSubmit('lr9'); + } +} + +/* === ЛР 10. Равновесие рычага === */ +function add_lr10(){ + const body = '
' + +'' + +'' + +'' + +'
' + +'
' + +'Для равновесия: $l_2 = F_1 l_1 / F_2$ = 20 см' + +'Проверка: $F_1 l_1$ = 60, $F_2 l_2$ = 60 (Н·см)' + +'
' + +'
' + +''; + if(appendTo('lr10', wgWrapper('lr10-extra', 'ЛР 10', 'Равновесие рычага', 'Подбери $l_2$ так чтобы $F_1 l_1 = F_2 l_2$.', body))){ + const upd = ()=>{ + const F1 = +document.getElementById('lr10w-F1-r').value; + const l1 = +document.getElementById('lr10w-l1-r').value; + const F2 = +document.getElementById('lr10w-F2-r').value; + document.getElementById('lr10w-F1').textContent = F1; + document.getElementById('lr10w-l1').textContent = l1; + document.getElementById('lr10w-F2').textContent = F2; + const l2 = F1*l1/F2; + document.getElementById('lr10w-l2').textContent = l2.toFixed(1); + document.getElementById('lr10w-M1').textContent = (F1*l1).toFixed(1); + document.getElementById('lr10w-M2').textContent = (F2*l2).toFixed(1); + }; + ['lr10w-F1-r','lr10w-l1-r','lr10w-F2-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd)); + upd(); + wireSubmit('lr10'); + } +} + +/* === ЛР 11. КПД наклонной === */ +function add_lr11(){ + const body = '
' + +'' + +'' + +'' + +'' + +'
' + +'
' + +'$A_{пол} = mgh$ = 0.59 Дж' + +'$A_{зат} = Fl$ = 0.72 Дж' + +'$\\eta = A_{пол}/A_{зат}$ = 82%' + +'
' + +'
' + +''; + if(appendTo('lr11', wgWrapper('lr11-extra', 'ЛР 11', 'КПД наклонной плоскости', 'Измерь силу тяги динамометром и рассчитай КПД.', body))){ + const upd = ()=>{ + const m = +document.getElementById('lr11w-m-r').value; + const h = +document.getElementById('lr11w-h-r').value; + const l = +document.getElementById('lr11w-l-r').value; + const F = +document.getElementById('lr11w-F-r').value; + document.getElementById('lr11w-m').textContent = m.toFixed(2); + document.getElementById('lr11w-h').textContent = h.toFixed(2); + document.getElementById('lr11w-l').textContent = l.toFixed(2); + document.getElementById('lr11w-F').textContent = F.toFixed(2); + const Ap = m*9.8*h, Az = F*l; + document.getElementById('lr11w-Ap').textContent = Ap.toFixed(2); + document.getElementById('lr11w-Az').textContent = Az.toFixed(2); + document.getElementById('lr11w-eta').textContent = Az > 0 ? Math.min(100, Math.round(Ap/Az*100)) : 0; + }; + ['lr11w-m-r','lr11w-h-r','lr11w-l-r','lr11w-F-r'].forEach(id=>document.getElementById(id).addEventListener('input', upd)); + upd(); + wireSubmit('lr11'); + } +} + +/* === ЛР 12. Период пружинного маятника === */ +function add_lr12(){ + const body = '
' + +'' + +'' + +'
' + +'
' + +'$T = t/10$ = 0.30 с' + +'$k = 4\\pi^2 m / T^2$ = 88 Н/м' + +'
' + +'
' + +''; + if(appendTo('lr12', wgWrapper('lr12-extra', 'ЛР 12', 'Жёсткость пружины', 'Засеки время 10 колебаний и рассчитай $k$.', body))){ + const upd = ()=>{ + const m = +document.getElementById('lr12w-m-r').value; + const t = +document.getElementById('lr12w-t-r').value; + document.getElementById('lr12w-m').textContent = m.toFixed(2); + document.getElementById('lr12w-t').textContent = t.toFixed(2); + const T = t/10; + const k = 4*Math.PI*Math.PI*m/(T*T); + document.getElementById('lr12w-T').textContent = T.toFixed(2); + document.getElementById('lr12w-k').textContent = k.toFixed(0); + }; + document.getElementById('lr12w-m-r').addEventListener('input', upd); + document.getElementById('lr12w-t-r').addEventListener('input', upd); + upd(); + wireSubmit('lr12'); + } +} + +window.PHYS9_CH5_WIDGETS = { + lr1:add_lr1, lr2:add_lr2, lr3:add_lr3, lr4:add_lr4, lr5:add_lr5, lr6:add_lr6, + lr7:add_lr7, lr8:add_lr8, lr9:add_lr9, lr10:add_lr10, lr11:add_lr11, lr12:add_lr12 +}; + +})(); diff --git a/frontend/textbooks/physics_9_ch5.html b/frontend/textbooks/physics_9_ch5.html index d4edd9c..090ccb1 100644 --- a/frontend/textbooks/physics_9_ch5.html +++ b/frontend/textbooks/physics_9_ch5.html @@ -17,6 +17,7 @@ +