// F7. Лифт с динамометром (§24 в ch2) — переживание перегрузки и невесомости. (function(){ 'use strict'; const B = () => window.PHYS9_FLAG_BASE; const C = () => window.PHYS9_COLORS || {}; function init(secId){ if (!B()) return false; const body = '' + '
' + '' + '' + '
' + '' + '
' + '' + '' + '' + '' + '' + '
' + '
' + '
Вес динамометра9.8 Н
' + '
Отношение P/mg1.0 g
' + '
$v$ лифта0 м/с
' + '
$h$ высота0 м
' + '
' + '
'; const card = B().makeCard(secId, 'F7. Лифт с динамометром', 'Управляй лифтом — наблюдай, как меняется показание динамометра. $P = m(g + a)$. При $a = -g$ — невесомость.', body); if (!card) return false; const cv = document.getElementById('F7-cv'); const ctx = cv.getContext('2d'); const W = cv.width, H = cv.height; let st = { y: 0, v: 0, a: 0, t: 0, mode: 'idle' }; function readSliders(){ document.getElementById('F7-mv').textContent = (+document.getElementById('F7-m').value).toFixed(2); document.getElementById('F7-av').textContent = (+document.getElementById('F7-a').value).toFixed(1); } function reset(){ st = { y: 0, v: 0, a: 0, t: 0, mode: 'idle' }; document.getElementById('F7-fb').className = 'flag-feedback'; draw(); } function tick(dt){ const m = +document.getElementById('F7-m').value; const a_in = +document.getElementById('F7-a').value; const g = 9.8; /* Управление режимом */ if (st.mode === 'up') st.a = a_in; else if (st.mode === 'dn') st.a = -a_in; else if (st.mode === 'stop') st.a = 0; else if (st.mode === 'fall') st.a = -g; else st.a = 0; st.v += st.a * dt; st.y += st.v * dt; /* ограничения */ if (st.y < 0){ st.y = 0; if (st.v < 0) st.v = 0; } if (st.y > 50){ st.y = 50; if (st.v > 0) st.v = 0; } st.t += dt; /* Вес */ const P = m * (g + st.a); const ratio = P/(m*g); document.getElementById('F7-P').textContent = P.toFixed(2) + ' Н'; document.getElementById('F7-pmg').textContent = ratio.toFixed(2) + ' g'; document.getElementById('F7-v').textContent = st.v.toFixed(2) + ' м/с'; document.getElementById('F7-h').textContent = st.y.toFixed(1) + ' м'; const fb = document.getElementById('F7-fb'); if (Math.abs(P) < m*g*0.05){ fb.className = 'flag-feedback warn show'; fb.innerHTML = '⚠ НЕВЕСОМОСТЬ! Динамометр показывает почти 0.'; } else if (P < 0){ fb.className = 'flag-feedback fail show'; fb.innerHTML = 'P < 0: предмет «отрывается» от пола (или сильно прижимается к потолку).'; } else if (ratio > 1.5){ fb.className = 'flag-feedback warn show'; fb.innerHTML = 'ПЕРЕГРУЗКА '+ratio.toFixed(1)+'g — космонавты тренируются на таких.'; } else if (Math.abs(ratio - 1) < 0.05){ fb.className = 'flag-feedback ok show'; fb.innerHTML = 'Нормальный вес — $a = 0$ или равномерное движение.'; } else { fb.className = 'flag-feedback'; } draw(); } function draw(){ const col = C(); ctx.fillStyle = col.bg || '#fafafa'; ctx.fillRect(0, 0, W, H); /* Шахта */ const shaftX = 80, shaftY = 30, shaftW = 280, shaftH = H - 60; ctx.fillStyle = col.bgSubtle || '#f8fafc'; ctx.fillRect(shaftX, shaftY, shaftW, shaftH); ctx.strokeStyle = col.axis || '#1e293b'; ctx.lineWidth = 2; ctx.strokeRect(shaftX, shaftY, shaftW, shaftH); /* Этажи */ ctx.strokeStyle = col.grid || '#e5e7eb'; ctx.lineWidth = 1; for (let i = 0; i <= 5; i++){ const py = shaftY + (i/5) * shaftH; ctx.beginPath(); ctx.moveTo(shaftX, py); ctx.lineTo(shaftX + shaftW, py); ctx.stroke(); ctx.fillStyle = col.textMuted || '#64748b'; ctx.font = '11px Inter,sans-serif'; ctx.fillText((5-i)+' эт', shaftX + 6, py + 12); } /* Лифт (кабина) */ const yNorm = st.y / 50; /* 0..1 */ const liftY = shaftY + shaftH - 100 - yNorm * (shaftH - 120); ctx.fillStyle = col.bodyLight || '#cbd5e1'; ctx.fillRect(shaftX + 50, liftY, shaftW - 100, 90); ctx.strokeStyle = col.bodyAccent || '#1e293b'; ctx.lineWidth = 2; ctx.strokeRect(shaftX + 50, liftY, shaftW - 100, 90); /* Трос */ ctx.strokeStyle = st.mode === 'fall' ? col.fail : col.axis || '#1e293b'; ctx.lineWidth = st.mode === 'fall' ? 1 : 2.5; if (st.mode === 'fall'){ ctx.setLineDash([4, 4]); } ctx.beginPath(); ctx.moveTo(shaftX + shaftW/2, shaftY); ctx.lineTo(shaftX + shaftW/2, liftY); ctx.stroke(); ctx.setLineDash([]); /* Динамометр внутри лифта */ const dynaX = shaftX + shaftW/2, dynaY = liftY + 20; ctx.strokeStyle = col.axis || '#1e293b'; ctx.lineWidth = 1.5; ctx.fillStyle = col.bg || '#fafafa'; ctx.fillRect(dynaX - 12, dynaY, 24, 22); ctx.strokeRect(dynaX - 12, dynaY, 24, 22); /* стрелка */ const m = +document.getElementById('F7-m').value; const g = 9.8; const P = m*(g + st.a); const Pmax = m*g*2.5; const norm = Math.max(0, Math.min(1, P/Pmax)); const angle = -Math.PI*0.75 + norm * Math.PI*1.5; ctx.strokeStyle = col.fail || '#dc2626'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(dynaX, dynaY + 11); ctx.lineTo(dynaX + 9*Math.cos(angle), dynaY + 11 + 9*Math.sin(angle)); ctx.stroke(); /* Груз ниже динамометра */ ctx.fillStyle = col.forceGravity || '#2563eb'; const gSize = Math.min(30, 10 + m*3); ctx.fillRect(dynaX - gSize/2, dynaY + 30, gSize, gSize); ctx.strokeStyle = col.bodyAccent || '#1e293b'; ctx.strokeRect(dynaX - gSize/2, dynaY + 30, gSize, gSize); ctx.fillStyle = '#fff'; ctx.font = 'bold 10px Inter,sans-serif'; ctx.textAlign = 'center'; ctx.fillText(m.toFixed(1)+' кг', dynaX, dynaY + 30 + gSize/2 + 3); ctx.textAlign = 'left'; /* подпись режима */ ctx.fillStyle = col.text || '#0f172a'; ctx.font = 'bold 13px Inter,sans-serif'; const modeText = { idle: '— пауза —', up: '↑ РАЗГОН ВВЕРХ', dn: '↓ РАЗГОН ВНИЗ', stop: '— равномерно/стоит', fall: 'СВОБОДНОЕ ПАДЕНИЕ' }[st.mode]; ctx.fillText(modeText, shaftX + shaftW + 20, 60); /* большой циферблат справа */ const dialX = shaftX + shaftW + 130, dialY = 200, dialR = 70; ctx.fillStyle = col.bg || '#fafafa'; ctx.beginPath(); ctx.arc(dialX, dialY, dialR, 0, Math.PI*2); ctx.fill(); ctx.strokeStyle = col.axis || '#1e293b'; ctx.lineWidth = 3; ctx.stroke(); /* шкала 0..2.5 g */ ctx.lineWidth = 2; for (let i = 0; i <= 10; i++){ const a = Math.PI*0.75 + (i/10) * Math.PI*1.5; const r1 = dialR - 12, r2 = dialR; ctx.strokeStyle = col.axis || '#1e293b'; ctx.beginPath(); ctx.moveTo(dialX + r1*Math.cos(a), dialY + r1*Math.sin(a)); ctx.lineTo(dialX + r2*Math.cos(a), dialY + r2*Math.sin(a)); ctx.stroke(); ctx.fillStyle = col.textMuted || '#64748b'; ctx.font = '10px Inter,sans-serif'; const lbl = (i*0.25).toFixed(1); const lblR = dialR - 25; ctx.fillText(lbl, dialX + lblR*Math.cos(a) - 8, dialY + lblR*Math.sin(a) + 4); } /* стрелка на большом */ const normBig = Math.max(0, Math.min(1, P/(m*g*2.5))); const aBig = Math.PI*0.75 + normBig * Math.PI*1.5; ctx.strokeStyle = col.fail || '#dc2626'; ctx.lineWidth = 4; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(dialX, dialY); ctx.lineTo(dialX + (dialR-15)*Math.cos(aBig), dialY + (dialR-15)*Math.sin(aBig)); ctx.stroke(); ctx.fillStyle = col.fail || '#dc2626'; ctx.beginPath(); ctx.arc(dialX, dialY, 5, 0, Math.PI*2); ctx.fill(); /* подпись */ ctx.fillStyle = col.text || '#0f172a'; ctx.textAlign = 'center'; ctx.font = 'bold 14px Inter,sans-serif'; ctx.fillText(P.toFixed(1) + ' Н', dialX, dialY + dialR + 22); ctx.font = '11px Inter,sans-serif'; ctx.fillText('(' + (P/(m*g)).toFixed(2) + ' g)', dialX, dialY + dialR + 38); ctx.textAlign = 'left'; } document.getElementById('F7-up').addEventListener('mousedown', ()=>{ st.mode = 'up'; }); document.getElementById('F7-up').addEventListener('mouseup', ()=>{ st.mode = 'stop'; }); document.getElementById('F7-dn').addEventListener('mousedown', ()=>{ st.mode = 'dn'; }); document.getElementById('F7-dn').addEventListener('mouseup', ()=>{ st.mode = 'stop'; }); document.getElementById('F7-stop').addEventListener('click', ()=>{ st.mode = 'stop'; }); document.getElementById('F7-fall').addEventListener('click', ()=>{ st.mode = 'fall'; }); document.getElementById('F7-reset').addEventListener('click', reset); ['F7-m','F7-a'].forEach(id => document.getElementById(id).addEventListener('input', readSliders)); readSliders(); draw(); B().startLoop('F7', cv, tick); return true; } if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F7', { init: init, cleanup: function(){} }); else document.addEventListener('DOMContentLoaded', ()=>{ if (window.PHYS9_FLAG_BASE) window.PHYS9_FLAG_BASE.register('F7', { init: init, cleanup: function(){} }); }); })();