diff --git a/frontend/js/phys.js b/frontend/js/phys.js
index 822f64b..0316e82 100644
--- a/frontend/js/phys.js
+++ b/frontend/js/phys.js
@@ -672,15 +672,33 @@ class HillSlideSim {
this.scale = opts.scale || 30;
this.reset();
}
- reset() { this.t = 0; this.x = 0; this.v = 0; this.h = this.hStart; }
+ reset() {
+ // Начальный импульс: тележка стартует с лёгким толчком (1% от L) и небольшой скоростью,
+ // иначе при h=hStart, v=0 — она навсегда останется на вершине (бесконечный нуль).
+ const L = this.hStart * 4;
+ this.t = 0;
+ this.x = L * 0.01;
+ const xRel = this.x / L;
+ this.h = this.hStart * Math.pow(1 - xRel, 2);
+ this.v = Math.sqrt(2 * this.g * (this.hStart - this.h));
+ }
step(dt) {
const gEff = this.g * (1 - this.friction);
this.t += dt;
- const dropped = this.hStart - this.h;
- if (this.h <= 0) { this.h = 0; this.v = Math.sqrt(2 * gEff * this.hStart); return; }
- this.v = Math.sqrt(2 * gEff * Math.max(0, dropped));
- this.x += this.v * dt;
const L = this.hStart * 4;
+ if (this.h <= 0.001 || this.x >= L) {
+ this.h = 0;
+ // Финальная скорость с учётом потерь на трение.
+ this.v = Math.sqrt(2 * gEff * this.hStart);
+ this.x = L;
+ return;
+ }
+ // Скорость по закону сохранения энергии (с учётом трения).
+ const dropped = Math.max(0.0001, this.hStart - this.h);
+ this.v = Math.sqrt(2 * gEff * dropped);
+ // Скорость по x — это горизонтальная компонента; для наглядного моделирования
+ // используем её напрямую как темп роста x.
+ this.x += this.v * dt;
const xRel = Math.min(this.x, L) / L;
this.h = this.hStart * Math.pow(1 - xRel, 2);
}
diff --git a/frontend/js/phys7_ch3_widgets.js b/frontend/js/phys7_ch3_widgets.js
index be5ebaf..6814958 100644
--- a/frontend/js/phys7_ch3_widgets.js
+++ b/frontend/js/phys7_ch3_widgets.js
@@ -1223,8 +1223,18 @@ function add_p22(){
+ '$F_т = m g = $ 0.98 Н (на Земле, $g = $ 9.8 Н/кг)'
+ '');
- /* IV-2 КВИЗ */
- h += wgWrap('p22-iv2', 'КВИЗ', 'Сила тяжести', '',
+ /* IV-2 СИМ — Падение тела на 4 планетах */
+ h += wgWrap('p22-iv2', 'СИМ', 'Падение с одной высоты на 4 планетах', 'Нажми «Уронить» — увидь, кто упадёт быстрее. Время и скорость в реальном времени.',
+ '
'
+ quizQuestion('p22-q', 0, 'Куда направлена сила тяжести на поверхности Земли?', ['Вверх','Вертикально вниз (к центру Земли)','В сторону Солнца','В произвольном направлении'], 1)
+ quizQuestion('p22-q', 1, '$g$ на Земле примерно равно…', ['1 Н/кг','9,8 Н/кг','100 Н/кг','1000 Н/кг'], 1)
@@ -1280,6 +1290,80 @@ function add_p22(){
document.getElementById('p22-m-r').addEventListener('input', upd22);
upd22();
+ // §22 IV-2 — Падение тел на 4 планетах
+ const planets22 = [
+ { nm:'Земля', g:9.8, col:'#0284c7' },
+ { nm:'Луна', g:1.6, col:'#94a3b8' },
+ { nm:'Марс', g:3.7, col:'#dc2626' },
+ { nm:'Юпитер', g:24.8, col:'#d97706' }
+ ];
+ let drop22 = { t:0, raf:0, running:false, h0:10 };
+ function drawDrop22(){
+ const svg = document.getElementById('p22-drop-svg');
+ if(!svg) return;
+ const W = 380, H = 200, baseY = 175;
+ const pxPerM = (baseY - 25) / drop22.h0;
+ const colW = W / planets22.length;
+ let s = '';
+ s += '
';
+ planets22.forEach((p, i) => {
+ const cx = colW * i + colW/2;
+ const startY = baseY - drop22.h0 * pxPerM;
+ // Падение: h(t) = h0 - g*t²/2 (свободное падение)
+ const fall = 0.5 * p.g * drop22.t * drop22.t;
+ const hNow = Math.max(0, drop22.h0 - fall);
+ const cy = baseY - hNow * pxPerM - 6;
+ const v = p.g * drop22.t;
+ const fellTime = Math.sqrt(2 * drop22.h0 / p.g);
+ const onGround = drop22.t >= fellTime;
+ // Опорная линия высоты
+ s += '
';
+ // Шарик
+ s += '
';
+ // Подпись планеты
+ s += '
' + p.nm + '';
+ s += '
g=' + p.g + '';
+ if(onGround) s += '
' + fellTime.toFixed(2) + ' с';
+ });
+ svg.innerHTML = s;
+ // Info
+ const status = planets22.map(p => {
+ const fellTime = Math.sqrt(2 * drop22.h0 / p.g);
+ const onGround = drop22.t >= fellTime;
+ const vNow = onGround ? p.g * fellTime : p.g * drop22.t;
+ return '
' + p.nm + ': ' + (onGround ? 'упал за ' + fellTime.toFixed(2) + ' с, $v = ' + vNow.toFixed(1) + '$ м/с' : 'падает, $v = ' + vNow.toFixed(1) + '$ м/с');
+ }).join(' · ');
+ document.getElementById('p22-drop-info').innerHTML = '$t = ' + drop22.t.toFixed(2) + '$ с · ' + status;
+ renderMath(document.getElementById('p22-drop-info'));
+ }
+ function loop22(){
+ if(!drop22.running) return;
+ drop22.t += 0.04;
+ drawDrop22();
+ // Останавливаем когда даже самая медленная (Луна) упала
+ const maxT = Math.sqrt(2 * drop22.h0 / 1.6);
+ if(drop22.t >= maxT + 0.5){ drop22.running = false; return; }
+ drop22.raf = requestAnimationFrame(loop22);
+ }
+ document.getElementById('p22-drop-h-r').addEventListener('input', () => {
+ drop22.h0 = +document.getElementById('p22-drop-h-r').value;
+ document.getElementById('p22-drop-h').textContent = drop22.h0;
+ drop22.t = 0; drop22.running = false;
+ if(drop22.raf) cancelAnimationFrame(drop22.raf);
+ drawDrop22();
+ });
+ document.getElementById('p22-drop').addEventListener('click', () => {
+ drop22.t = 0; drop22.running = true;
+ if(drop22.raf) cancelAnimationFrame(drop22.raf);
+ loop22();
+ });
+ document.getElementById('p22-drop-reset').addEventListener('click', () => {
+ drop22.t = 0; drop22.running = false;
+ if(drop22.raf) cancelAnimationFrame(drop22.raf);
+ drawDrop22();
+ });
+ drawDrop22();
+
wireDnd('p22-dnd', [
{ id:'a1', cat:'1n' },{ id:'a2', cat:'10n' },{ id:'a3', cat:'100n' },
{ id:'a4', cat:'1n' },{ id:'a5', cat:'10n' },{ id:'a6', cat:'100n' }
@@ -1444,14 +1528,18 @@ function add_p24(){
+ 'На
пружинных весах (динамометре) измеряют силу, с которой тело растягивает пружину. '
+ 'Это и есть вес тела в Ньютонах.');
- /* IV-1 СИМ: тело в 3 ситуациях */
- h += wgWrap('p24-iv1', 'СИМ', 'Три ситуации: покой, падение, ускорение', 'Выбери ситуацию — увидь стрелки сил.',
- '
'
- + [['rest','На столе (покой)'],['fall','Свободное падение'],['up','Ускоряется вверх'],['down','Ускоряется вниз']].map((s, i) =>
- '
').join('')
+ /* IV-1 СИМ: динамический лифт с динамометром */
+ h += wgWrap('p24-iv1', 'СИМ', 'Лифт с динамометром: вес меняется при ускорении', 'Подвинь slider ускорения — увидь, как меняется показание динамометра (вес).',
+ '
'
+ + ''
+ + ''
+ '
'
- + '
'
- + '
');
+ + '
'
+ + [['0','Покой'],['2','Едет вверх с ускорением'],['-2','Едет вниз с ускорением'],['-10','Свободное падение']].map(p =>
+ '').join('')
+ + '
'
+ + '
'
+ + '
');
/* IV-2 КВИЗ */
h += wgWrap('p24-iv2', 'КВИЗ', 'Вес vs сила тяжести', '',
@@ -1489,72 +1577,86 @@ function add_p24(){
h += readButton('p24');
body.innerHTML = h;
- function draw24(s){
+ function draw24(){
const svg = document.getElementById('p24-svg');
if(!svg) return;
- const cx = 180, cy = 100;
- let html = '';
- let info = '';
- if(s === 'rest'){
- // Тело на столе
- html += '
';
- for(let i = 0; i < 14; i++) html += '
';
- html += '
';
- // F_т вниз (фиолетовая)
- html += '
';
- html += '
';
- html += '
F_т (на тело)';
- // P вниз от низа тела (индиго)
- html += '
';
- html += '
';
- html += '
P (на стол)';
- info = '
Покой на горизонтальной опоре: $P = F_т = mg$, но приложены к разным телам.';
- } else if(s === 'fall'){
- html += '
';
- // F_т вниз
- html += '
';
- html += '
';
- html += '
F_т ↓';
- // P = 0 — пиктограмма
- html += '
P = 0';
- html += '
невесомость';
- info = '
Свободное падение / орбита: сила тяжести есть ($F_т = mg \\ne 0$), но вес $P = 0$ — тело ни на что не давит.';
- } else if(s === 'up'){
- html += '
';
- html += '
'; // тяга вверх
- html += '
';
- // F_т
- html += '
';
- html += '
';
- html += '
F_т = mg';
- // P больше — длинная стрелка
- html += '
';
- html += '
';
- html += '
P > mg (перегрузка)';
- info = '
Лифт ускоряется вверх: вес
больше $mg$ — это перегрузка. Космонавты на старте испытывают $P \\approx 3 mg$.';
- } else {
- // down
- html += '
';
- html += '
';
- html += '
';
- html += '
';
- html += '
F_т = mg';
- // P меньше — короткая стрелка
- html += '
';
- html += '
';
- html += '
P < mg';
- info = '
Лифт ускоряется вниз: вес
меньше $mg$. Если ускорение $= g$, то $P = 0$ — невесомость.';
+ const m = +document.getElementById('p24-m-r').value;
+ const a = +document.getElementById('p24-a-r').value; // положительное = вверх
+ const g = 10;
+ // Вес на пружине: P = m(g + a). a > 0 (лифт ускоряется вверх) → P > mg.
+ // Если лифт в свободном падении (a = -g), P = 0.
+ const P = Math.max(0, m * (g + a));
+ const W = 380, H = 260;
+ // Кабина лифта 160×220, центрирована
+ const cabX = 110, cabY = 20, cabW = 160, cabH = 220;
+ let s = '';
+ // Шахта (стенки)
+ s += '
';
+ s += '
';
+ // Кабина
+ s += '
';
+ // Стрелка ускорения слева снаружи
+ if(Math.abs(a) > 0.3){
+ const aY = cabY + cabH/2;
+ const arrLen = Math.min(50, Math.abs(a) * 4);
+ const dir = a > 0 ? -1 : 1; // вверх = -y, вниз = +y
+ s += '
';
+ s += '
';
+ s += '
a = ' + (a > 0 ? '+' : '') + a + '';
}
- svg.innerHTML = html;
- document.getElementById('p24-info').innerHTML = info;
+ // Динамометр (вертикальный): корпус, пружина, груз
+ const dynX = cabX + cabW/2, dynTop = cabY + 30;
+ s += '
';
+ // Корпус
+ s += '
';
+ // Пружина — длина пропорц. P
+ const maxStretch = 80;
+ const Pmax = m * (g + 10); // P при a = +10
+ const stretch = Math.min(maxStretch, (P / Pmax) * maxStretch);
+ const sprBot = dynTop + 5 + stretch;
+ const coils = 6;
+ let path = 'M ' + dynX + ' ' + (dynTop + 5);
+ for(let i = 0; i < coils; i++){
+ path += ' L ' + (dynX + (i%2 ? 9 : -9)) + ' ' + (dynTop + 5 + (i + 0.5) * stretch/coils);
+ }
+ path += ' L ' + dynX + ' ' + sprBot;
+ s += '
';
+ // Шкала справа
+ for(let i = 0; i <= 10; i++){
+ const ty = dynTop + 5 + (i / 10) * maxStretch;
+ s += '
';
+ if(i % 2 === 0) s += '
' + ((Pmax * i / 10).toFixed(0)) + '';
+ }
+ // Указатель
+ s += '
';
+ // Груз — кружок снизу пружины
+ s += '
';
+ s += '
' + m + ' кг';
+ // Показание динамометра в нижней части кабины
+ s += '
P = ' + P.toFixed(1) + ' Н';
+ // Подпись режима
+ let mode, modeCol;
+ if(Math.abs(P - m*g) < 0.01){ mode = 'ПОКОЙ или равномерно: P = mg'; modeCol = '#475569'; }
+ else if(P < 0.1){ mode = 'НЕВЕСОМОСТЬ: P = 0'; modeCol = '#10b981'; }
+ else if(P > m*g){ mode = 'ПЕРЕГРУЗКА: P > mg'; modeCol = '#dc2626'; }
+ else { mode = 'ПОНИЖЕННЫЙ ВЕС: P < mg'; modeCol = '#0284c7'; }
+ s += '
' + mode + '';
+ svg.innerHTML = s;
+ document.getElementById('p24-m').textContent = m;
+ document.getElementById('p24-a').textContent = a > 0 ? '+' + a : a;
+ const Ft = m * g;
+ document.getElementById('p24-info').innerHTML =
+ '
$F_т = mg = ' + Ft.toFixed(1) + '$ Н — сила тяжести на тело (не меняется при ускорении).
'
+ + '
$P = m(g + a) = ' + m + ' \\cdot (' + g + (a >= 0 ? ' + ' : ' − ') + Math.abs(a) + ') = ' + P.toFixed(1) + '$ Н — показание динамометра (вес).
'
+ + '
' + mode + '';
renderMath(document.getElementById('p24-info'));
}
- body.querySelectorAll('.p24-sit').forEach(btn => btn.addEventListener('click', () => {
- body.querySelectorAll('.p24-sit').forEach(b => { b.style.background = '#fff'; b.style.color = '#dc2626'; });
- btn.style.background = '#dc2626'; btn.style.color = '#fff';
- draw24(btn.dataset.s);
+ body.querySelectorAll('.p24-preset').forEach(btn => btn.addEventListener('click', () => {
+ document.getElementById('p24-a-r').value = btn.dataset.a;
+ draw24();
}));
- draw24('rest');
+ ['p24-m-r','p24-a-r'].forEach(id => document.getElementById(id).addEventListener('input', draw24));
+ draw24();
wireDnd('p24-dnd', [
{ id:'a1', cat:'ft' },{ id:'a2', cat:'ft' },{ id:'a3', cat:'p' },