feat(phys9 ch5): добавлены 12 виджетов Wave E — Лабораторный практикум

Новый модуль 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 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 09:53:29 +03:00
parent 9d5a2959e1
commit 70aad6a423
2 changed files with 431 additions and 1 deletions
+429
View File
@@ -0,0 +1,429 @@
// phys9_ch5_widgets.js — виджеты для Физики 9, Глава 5 (ЛР 1-12).
(function(){
'use strict';
function wgWrapper(secId, badge, title, help, body){
return '<div class="wg" id="'+secId+'"><div class="wg-header"><span class="wg-badge">'+badge+'</span><div class="wg-title">'+title+'</div></div><div class="wg-help">'+help+'</div>'+body+'</div>';
}
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='&#10003; Работа сдана! +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 = '<div class="sliders">'
+'<label>$s_1$ м: <b id="lr1w-s1">1.0</b><input type="range" id="lr1w-s1-r" min="0.2" max="3" step="0.1" value="1"></label>'
+'<label>$t_1$ с: <b id="lr1w-t1">2.0</b><input type="range" id="lr1w-t1-r" min="0.5" max="5" step="0.1" value="2"></label>'
+'<label>$s_2$ м: <b id="lr1w-s2">2.0</b><input type="range" id="lr1w-s2-r" min="0.2" max="5" step="0.1" value="2"></label>'
+'<label>$t_2$ с: <b id="lr1w-t2">3.0</b><input type="range" id="lr1w-t2-r" min="0.5" max="6" step="0.1" value="3"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Полный путь $s$ = <b id="lr1w-s">3.0</b> м, время $t$ = <b id="lr1w-t">5.0</b> с</span>'
+'<span>$\\langle v\\rangle = s/t$ = <b id="lr1w-vv">0.60</b> м/с</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr1-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr1-fb"></div>';
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 = '<div class="sliders">'
+'<label>Длина 1: $s_1$ м: <b id="lr2w-s1">0.5</b><input type="range" id="lr2w-s1-r" min="0.1" max="2" step="0.05" value="0.5"></label>'
+'<label>$t_1$ с: <b id="lr2w-t1">1.0</b><input type="range" id="lr2w-t1-r" min="0.2" max="3" step="0.05" value="1"></label>'
+'<label>Длина 2: $s_2$ м: <b id="lr2w-s2">2.0</b><input type="range" id="lr2w-s2-r" min="0.5" max="5" step="0.1" value="2"></label>'
+'<label>$t_2$ с: <b id="lr2w-t2">2.0</b><input type="range" id="lr2w-t2-r" min="0.5" max="5" step="0.05" value="2"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$a = 2 s_1 / t_1^2$ = <b id="lr2w-a1">1.0</b> м/с² (по 1-му)</span>'
+'<span>$a = 2 s_2 / t_2^2$ = <b id="lr2w-a2">1.0</b> м/с² (по 2-му)</span>'
+'<span>Среднее: $a$ = <b id="lr2w-a">1.0</b> м/с²</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr2-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr2-fb"></div>';
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 = '<div class="sliders">'
+'<label>$R$ м: <b id="lr3w-R">0.3</b><input type="range" id="lr3w-R-r" min="0.05" max="2" step="0.05" value="0.3"></label>'
+'<label>10 оборотов за $t$ с: <b id="lr3w-t">5</b><input type="range" id="lr3w-t-r" min="1" max="30" step="0.5" value="5"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$T$ = $t$/10 = <b id="lr3w-T">0.50</b> с</span>'
+'<span>$v$ = $2\\pi R/T$ = <b id="lr3w-v">3.77</b> м/с</span>'
+'<span>$a_n$ = $v^2/R$ = <b id="lr3w-an">47</b> м/с²</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr3-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr3-fb"></div>';
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 = '<div class="sliders">'
+'<label>$l$ нить, м: <b id="lr4w-l">1.0</b><input type="range" id="lr4w-l-r" min="0.1" max="3" step="0.05" value="1"></label>'
+'<label>10 колебаний за $t$ с: <b id="lr4w-t">20.1</b><input type="range" id="lr4w-t-r" min="5" max="40" step="0.1" value="20.1"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$T$ = $t/10$ = <b id="lr4w-T">2.01</b> с</span>'
+'<span>$g = 4\\pi^2 l / T^2$ = <b id="lr4w-g">9.77</b> м/с²</span>'
+'<span>Эталон: $9{,}81$ м/с². Погрешность: <b id="lr4w-err">0.4</b>%</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr4-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr4-fb"></div>';
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 = '<div class="sliders">'
+'<label>$m$ груза, кг: <b id="lr5w-m">0.2</b><input type="range" id="lr5w-m-r" min="0.05" max="2" step="0.05" value="0.2"></label>'
+'<label>Угол $\\alpha$, &#176;: <b id="lr5w-a">30</b><input type="range" id="lr5w-a-r" min="10" max="60" step="1" value="30"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Сила тяжести $P = mg$ = <b id="lr5w-P">1.96</b> Н</span>'
+'<span>Проекция вдоль накл.: $F_\\parallel = mg\\sin\\alpha$ = <b id="lr5w-Fp">0.98</b> Н</span>'
+'<span>Прижим: $F_\\perp = mg\\cos\\alpha$ = <b id="lr5w-Fn">1.70</b> Н</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr5-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr5-fb"></div>';
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 = '<div class="sliders">'
+'<label>$h$ высота, м: <b id="lr6w-h">2.0</b><input type="range" id="lr6w-h-r" min="0.5" max="10" step="0.1" value="2"></label>'
+'<label>$t$ время, с: <b id="lr6w-t">0.64</b><input type="range" id="lr6w-t-r" min="0.2" max="2" step="0.01" value="0.64"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$g = 2h/t^2$ = <b id="lr6w-g">9.77</b> м/с²</span>'
+'<span>Эталон: $9{,}81$. Погрешность: <b id="lr6w-err">0.4</b>%</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr6-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr6-fb"></div>';
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 = '<div class="sliders">'
+'<label>$m$ кг: <b id="lr7w-m">0.1</b><input type="range" id="lr7w-m-r" min="0.05" max="2" step="0.05" value="0.1"></label>'
+'<label>$h$ м: <b id="lr7w-h">0.5</b><input type="range" id="lr7w-h-r" min="0.1" max="3" step="0.05" value="0.5"></label>'
+'<label>Измер. $v_{внизу}$: <b id="lr7w-v">3.0</b><input type="range" id="lr7w-v-r" min="0.5" max="10" step="0.05" value="3"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$E_p^{старт} = mgh$ = <b id="lr7w-Ep">0.49</b> Дж</span>'
+'<span>$E_k^{внизу} = mv^2/2$ = <b id="lr7w-Ek">0.45</b> Дж</span>'
+'<span>Сохранение: <b id="lr7w-cons">8%</b> потерь</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr7-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr7-fb"></div>';
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 = '<div class="sliders">'
+'<label>Вес в воздухе $P_1$, Н: <b id="lr8w-P1">2.0</b><input type="range" id="lr8w-P1-r" min="0.5" max="10" step="0.1" value="2"></label>'
+'<label>Вес в воде $P_2$, Н: <b id="lr8w-P2">1.5</b><input type="range" id="lr8w-P2-r" min="0.1" max="10" step="0.1" value="1.5"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$F_A = P_1 - P_2$ = <b id="lr8w-Fa">0.50</b> Н</span>'
+'<span>$V = F_A/(\\rho g) = $ <b id="lr8w-V">51</b> см³</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr8-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr8-fb"></div>';
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 = '<div class="sliders">'
+'<label>$\\rho_{тела}$, кг/м³: <b id="lr9w-rt">700</b><input type="range" id="lr9w-rt-r" min="100" max="15000" step="50" value="700"></label>'
+'<label>$\\rho_{жидк}$, кг/м³: <b id="lr9w-rl">1000</b><input type="range" id="lr9w-rl-r" min="500" max="14000" step="50" value="1000"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Доля погружения: <b id="lr9w-h">70</b>%</span>'
+'<span><b id="lr9w-r" style="color:var(--ok)">ПЛАВАЕТ ↑</b></span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr9-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr9-fb"></div>';
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 = '<div class="sliders">'
+'<label>$F_1$ Н: <b id="lr10w-F1">2</b><input type="range" id="lr10w-F1-r" min="0.5" max="20" step="0.5" value="2"></label>'
+'<label>$l_1$ см: <b id="lr10w-l1">30</b><input type="range" id="lr10w-l1-r" min="5" max="100" step="1" value="30"></label>'
+'<label>$F_2$ Н: <b id="lr10w-F2">3</b><input type="range" id="lr10w-F2-r" min="0.5" max="20" step="0.5" value="3"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>Для равновесия: $l_2 = F_1 l_1 / F_2$ = <b id="lr10w-l2">20</b> см</span>'
+'<span>Проверка: $F_1 l_1$ = <b id="lr10w-M1">60</b>, $F_2 l_2$ = <b id="lr10w-M2">60</b> (Н·см)</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr10-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr10-fb"></div>';
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 = '<div class="sliders">'
+'<label>$m$ кг: <b id="lr11w-m">0.2</b><input type="range" id="lr11w-m-r" min="0.05" max="2" step="0.05" value="0.2"></label>'
+'<label>$h$ м: <b id="lr11w-h">0.3</b><input type="range" id="lr11w-h-r" min="0.05" max="1.5" step="0.05" value="0.3"></label>'
+'<label>$l$ м (длина накл.): <b id="lr11w-l">0.6</b><input type="range" id="lr11w-l-r" min="0.1" max="3" step="0.05" value="0.6"></label>'
+'<label>Сила тяги $F$, Н: <b id="lr11w-F">1.2</b><input type="range" id="lr11w-F-r" min="0.05" max="5" step="0.05" value="1.2"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$A_{пол} = mgh$ = <b id="lr11w-Ap">0.59</b> Дж</span>'
+'<span>$A_{зат} = Fl$ = <b id="lr11w-Az">0.72</b> Дж</span>'
+'<span>$\\eta = A_{пол}/A_{зат}$ = <b id="lr11w-eta">82</b>%</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr11-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr11-fb"></div>';
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 = '<div class="sliders">'
+'<label>$m$ груза, кг: <b id="lr12w-m">0.2</b><input type="range" id="lr12w-m-r" min="0.05" max="2" step="0.05" value="0.2"></label>'
+'<label>10 колебаний за $t$ с: <b id="lr12w-t">3.0</b><input type="range" id="lr12w-t-r" min="0.5" max="15" step="0.1" value="3"></label>'
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:4px">'
+'<span>$T = t/10$ = <b id="lr12w-T">0.30</b> с</span>'
+'<span>$k = 4\\pi^2 m / T^2$ = <b id="lr12w-k">88</b> Н/м</span>'
+'</div>'
+'<div class="actions"><button class="btn primary" id="lr12-sub">Сдать работу (+30 XP)</button></div>'
+'<div class="feedback" id="lr12-fb"></div>';
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
};
})();
+2 -1
View File
@@ -17,6 +17,7 @@
<script src="/js/phys.js" defer></script>
<script src="/js/phys9_palette.js" defer></script>
<script src="/js/phys9_legacy.js" defer></script>
<script src="/js/phys9_ch5_widgets.js" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
@@ -795,7 +796,7 @@ function _injectTasks(id){
var body = document.getElementById(id + '-body');
if(!body || body.querySelector('.legacy-tasks')) return;
body.insertAdjacentHTML('beforeend', _makeTaskBlock(id));
setTimeout(function(){ try { if(window.renderTask) window.renderTask(id); if(window.renderNav) window.renderNav(id); } catch(e){} }, 60);
setTimeout(function(){ try { if(window.renderTask) window.renderTask(id); if(window.renderNav) window.renderNav(id); } catch(e){} try { if(window.PHYS9_CH5_WIDGETS && window.PHYS9_CH5_WIDGETS[id]) window.PHYS9_CH5_WIDGETS[id](); } catch(e){ console.warn('phys9 widget init:', e.message); } }, 60);
}
var _origEnsureBuilt = ensureBuilt;
ensureBuilt = function(id){ _origEnsureBuilt(id); _injectTasks(id); };