3044 lines
215 KiB
HTML
3044 lines
215 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<title>Физика 10 · Глава 5 · «Магнитное поле и ЭМИ»</title>
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
|
||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<script src="/js/g3d.js" defer></script>
|
||
<script src="/js/phys.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{
|
||
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
|
||
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
|
||
--pri:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
|
||
--acc:#22d3ee; --acc2:#0891b2; --acc-soft:#cffafe;
|
||
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
|
||
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
|
||
}
|
||
.dark{--bg:#0a0a0e; --card:#13120a; --card-soft:#18160a; --text:#fef9e7; --ink:#fef9e7; --muted:#a39070; --border:#2a2512}
|
||
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
||
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
|
||
button,input,select,textarea{font-family:inherit;font-size:inherit}
|
||
button{cursor:pointer;border:0;background:transparent;color:inherit}
|
||
a{color:inherit;text-decoration:none}
|
||
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
|
||
|
||
.hdr{position:relative;background:linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(255,255,255,.2);min-height:130px}
|
||
.hdr::before{content:'ГЛАВА 5';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,255,255,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
|
||
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
|
||
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
|
||
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
||
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
|
||
.hdr-btn:hover{background:rgba(255,255,255,.24)}
|
||
|
||
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
|
||
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
|
||
.col-main{min-width:0}
|
||
|
||
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
|
||
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
|
||
.hero::before{content:'B';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
|
||
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
|
||
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
|
||
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
|
||
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
|
||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(0,0,0,.18)}
|
||
.hero-progress{flex:1;min-width:200px;max-width:280px}
|
||
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
|
||
.hp-bar{height:8px;background:rgba(0,0,0,.12);border-radius:5px;overflow:hidden}
|
||
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
|
||
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
|
||
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(0,0,0,.18);font-family:'Unbounded',sans-serif}
|
||
|
||
.psel{margin-bottom:24px}
|
||
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
|
||
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
|
||
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
|
||
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
|
||
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
|
||
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
|
||
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
|
||
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
|
||
.psel-prog{height:4px;background:rgba(0,0,0,.10);border-radius:3px;overflow:hidden}
|
||
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
|
||
.psel-card.final{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft))}
|
||
.psel-card.final .psel-num{color:var(--warn)}
|
||
|
||
.sec[id="sec-p27"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p28"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p29"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p30"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p31"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p32"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p33"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-final5"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
|
||
.sec{display:none;position:relative;animation:fadeIn .35s ease}
|
||
.sec.active{display:block}
|
||
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
|
||
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
|
||
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
|
||
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.6rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
|
||
|
||
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(0,0,0,.04);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
|
||
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(0,0,0,.08)}
|
||
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
|
||
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
|
||
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}
|
||
.card-icon .ic{width:18px;height:18px}
|
||
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
|
||
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
|
||
.card-body{font-size:.94rem;line-height:1.65}
|
||
.card-body p{margin-bottom:8px}
|
||
.card-body p:last-child{margin-bottom:0}
|
||
|
||
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
|
||
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
|
||
.btn:active{transform:scale(.96)}
|
||
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
|
||
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
|
||
|
||
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
|
||
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
|
||
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
|
||
|
||
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
|
||
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
|
||
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
|
||
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
|
||
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
|
||
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
|
||
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
|
||
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
|
||
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
|
||
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
|
||
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
|
||
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
|
||
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
|
||
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
|
||
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
|
||
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
|
||
.spoiler summary::-webkit-details-marker{display:none}
|
||
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
|
||
.spoiler[open] summary::before{content:'\2212'}
|
||
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
|
||
|
||
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
|
||
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
|
||
.dnd-pool.col{flex-direction:column;align-items:stretch}
|
||
.dnd-pool.col .dnd-chip{width:auto}
|
||
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
|
||
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
|
||
.dnd-chip:active{cursor:grabbing}
|
||
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(0,0,0,.10);transform:translateY(-1px)}
|
||
.dnd-chip.dragging{opacity:.28}
|
||
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
|
||
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
|
||
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
|
||
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
|
||
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
|
||
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
|
||
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
|
||
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
|
||
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
|
||
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
|
||
|
||
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
|
||
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
|
||
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
|
||
.sidecard-row b{color:var(--pri);font-weight:700}
|
||
.sidecard-row:last-child{margin-bottom:0}
|
||
@media(max-width:980px){.col-side{position:static;max-height:none}}
|
||
|
||
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
|
||
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
|
||
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
|
||
.xp-bar{height:9px;background:rgba(0,0,0,.10);border-radius:6px;overflow:hidden;margin:7px 0}
|
||
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
|
||
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
|
||
|
||
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
|
||
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
|
||
|
||
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
|
||
.ach-popup.show{display:flex}
|
||
|
||
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
|
||
.col-side-backdrop.show{display:block}
|
||
@media(max-width:980px){
|
||
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
|
||
.col-side.open{transform:none}
|
||
}
|
||
|
||
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
|
||
.search-modal.show{display:flex}
|
||
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
|
||
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
|
||
.search-results{flex:1;overflow-y:auto;padding:6px 0}
|
||
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border:0;width:100%;color:var(--text)}
|
||
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
|
||
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
|
||
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
|
||
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
|
||
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
|
||
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px}
|
||
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
|
||
|
||
/* === PHYS10 POLISH (visual + micro-interactions) === */
|
||
@keyframes wgFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
.sec.active .wg{animation:wgFadeIn .35s cubic-bezier(.16,1,.3,1) backwards}
|
||
.sec.active .wg:nth-of-type(1){animation-delay:.02s}
|
||
.sec.active .wg:nth-of-type(2){animation-delay:.08s}
|
||
.sec.active .wg:nth-of-type(3){animation-delay:.14s}
|
||
.sec.active .wg:nth-of-type(4){animation-delay:.20s}
|
||
.sec.active .wg:nth-of-type(5){animation-delay:.26s}
|
||
.sec.active .wg:nth-of-type(6){animation-delay:.32s}
|
||
.wg svg{transition:filter .25s ease}
|
||
.wg:hover svg{filter:drop-shadow(0 4px 16px rgba(0,0,0,.10))}
|
||
input[type=range]:active{box-shadow:0 0 0 4px var(--pri-soft);border-radius:8px}
|
||
.wg input[type=range]{cursor:ew-resize}
|
||
.score-display b{transition:transform .22s cubic-bezier(.16,1,.3,1),color .22s;display:inline-block;transform-origin:center}
|
||
.score-display b.bump{transform:scale(1.28);color:var(--pri)}
|
||
.katex{transition:color .2s}
|
||
.wg-help .katex:hover,.card-body .katex:hover{color:var(--pri2,var(--pri));cursor:help}
|
||
.hp-fill,.psel-prog-fill,.xp-fill,[id$="-overall-fill"]{transition:width .6s cubic-bezier(.16,1,.3,1)!important}
|
||
.boss-card,.btn.primary,.btn-primary{position:relative;overflow:hidden}
|
||
.btn.primary::after,.btn-primary::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at center,rgba(255,255,255,.42) 0%,transparent 60%);opacity:0;transition:opacity .25s;pointer-events:none}
|
||
.btn.primary:hover::after,.btn-primary:hover::after{opacity:1}
|
||
.psel-card{position:relative}
|
||
.psel-card .psel-done{position:absolute;top:6px;right:6px;width:18px;height:18px;border-radius:50%;background:#10b981;display:none;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(16,185,129,.45);z-index:2}
|
||
.psel-card .psel-done svg{width:11px;height:11px;stroke:#fff;fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
|
||
.psel-card.done .psel-done{display:flex}
|
||
.boss-card{transition:border-color .35s,box-shadow .6s,background .3s,transform .2s}
|
||
.boss-card.glow{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important}
|
||
@keyframes bossPulse{0%{box-shadow:0 0 0 0 rgba(16,185,129,.55)}70%{box-shadow:0 0 0 14px rgba(16,185,129,0)}100%{box-shadow:0 0 0 0 rgba(16,185,129,0)}}
|
||
.boss-card.pulse{animation:bossPulse .8s ease-out}
|
||
.sec{transition:opacity .25s}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="hdr">
|
||
<div class="hdr-row">
|
||
<div>
|
||
<h1>Физика 10 · Глава 5</h1>
|
||
<div class="hdr-sub">Магнитное поле · Ампер · Лоренц · поток · индукция · Ленц · Фарадей · самоиндукция</div>
|
||
</div>
|
||
<div class="hdr-side">
|
||
<a href="/textbook/physics-10" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К физике 10</a>
|
||
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
|
||
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
|
||
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="main">
|
||
<div class="col-main">
|
||
|
||
<section class="hero">
|
||
<h2>Магнитное поле и электромагнитная индукция</h2>
|
||
<p>Магнитное поле тока, сила Ампера, сила Лоренца, явление электромагнитной индукции и закон Фарадея.</p>
|
||
<div class="hero-row">
|
||
<button class="btn-primary" onclick="goTo('p27')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 27</button>
|
||
<div class="hero-progress">
|
||
<span class="hp-label">Прогресс по главе</span>
|
||
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
|
||
<span id="hero-hp-text" class="hp-text">0%</span>
|
||
</div>
|
||
<div id="hero-xp-badge" class="hero-xp-badge"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="psel">
|
||
<div class="psel-title">Параграфы главы</div>
|
||
<div id="psel-grid" class="psel-grid"></div>
|
||
</section>
|
||
|
||
<section id="sec-p27" class="sec" data-watermark="Эрстед"><div class="sec-header"><span class="sec-num">§ 27</span><h2 class="sec-h">Магнитное поле тока</h2></div><div id="p27-body"></div></section>
|
||
<section id="sec-p28" class="sec" data-watermark="B"><div class="sec-header"><span class="sec-num">§ 28</span><h2 class="sec-h">Индукция магнитного поля</h2></div><div id="p28-body"></div></section>
|
||
<section id="sec-p29" class="sec" data-watermark="Ампер"><div class="sec-header"><span class="sec-num">§ 29</span><h2 class="sec-h">Сила Ампера</h2></div><div id="p29-body"></div></section>
|
||
<section id="sec-p30" class="sec" data-watermark="Лоренц"><div class="sec-header"><span class="sec-num">§ 30</span><h2 class="sec-h">Сила Лоренца</h2></div><div id="p30-body"></div></section>
|
||
<section id="sec-p31" class="sec" data-watermark="Φ"><div class="sec-header"><span class="sec-num">§ 31</span><h2 class="sec-h">Магнитный поток. Электромагнитная индукция</h2></div><div id="p31-body"></div></section>
|
||
<section id="sec-p32" class="sec" data-watermark="Фарадей"><div class="sec-header"><span class="sec-num">§ 32</span><h2 class="sec-h">Правило Ленца. Закон Фарадея</h2></div><div id="p32-body"></div></section>
|
||
<section id="sec-p33" class="sec" data-watermark="L"><div class="sec-header"><span class="sec-num">§ 33</span><h2 class="sec-h">Самоиндукция</h2></div><div id="p33-body"></div></section>
|
||
<section id="sec-final5" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0891b2,#22d3ee)">★</span><h2 class="sec-h">Финал главы</h2></div><div id="final5-body"></div></section>
|
||
|
||
</div>
|
||
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
|
||
<div class="col-side-backdrop" id="col-side-backdrop"></div>
|
||
</main>
|
||
|
||
<footer class="foot">Интерактивный учебник «Физика 10» · Глава 5 · «Магнитное поле и ЭМИ» · LearnSpace</footer>
|
||
|
||
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
|
||
<div id="search-modal" class="search-modal" role="dialog">
|
||
<div class="search-box">
|
||
<input type="text" id="search-input" class="search-input" placeholder="Поиск…" autocomplete="off">
|
||
<div id="search-results" class="search-results"></div>
|
||
<div class="search-foot"><span><kbd>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
'use strict';
|
||
|
||
const STATE = { current:'p27', progress:{}, achievements:new Map(), xp:0, level:1 };
|
||
const TOTAL_PARAS = 8;
|
||
const _TB_SLUG = 'physics-10-ch5';
|
||
|
||
const PARAS = [
|
||
{ id:'p27', num:'\u00a7 27', name:"Магнитное поле тока", sub:'Опыт Эрстеда' },
|
||
{ id:'p28', num:'\u00a7 28', name:"Индукция магнитного поля", sub:'$\\vec{B}$' },
|
||
{ id:'p29', num:'\u00a7 29', name:"Сила Ампера", sub:'$F = BIL\\sin\\alpha$' },
|
||
{ id:'p30', num:'\u00a7 30', name:"Сила Лоренца", sub:'$F = qvB$' },
|
||
{ id:'p31', num:'\u00a7 31', name:"Магнитный поток. Электромагнитная индукция", sub:'$\\Phi = BS\\cos\\alpha$' },
|
||
{ id:'p32', num:'\u00a7 32', name:"Правило Ленца. Закон Фарадея", sub:'$\\mathcal{E}_i = -d\\Phi/dt$' },
|
||
{ id:'p33', num:'\u00a7 33', name:"Самоиндукция", sub:'$L$, $W_L = LI^2/2$' },
|
||
{ id:'final5', num:'\u2605', name:'Финал главы', sub:'Итоги \u00b7 боссы главы 5', final:true }
|
||
];
|
||
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
|
||
|
||
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
|
||
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
|
||
|
||
const ACH_LABELS = {
|
||
start:"Начало главы 5!",
|
||
p27_done:"Магнитное поле тока освоен!",
|
||
p28_done:"Индукция магнитного поля освоен!",
|
||
p29_done:"Сила Ампера освоен!",
|
||
p30_done:"Сила Лоренца освоен!",
|
||
p31_done:"Магнитный поток. Электромагнитная индукция освоен!",
|
||
p32_done:"Правило Ленца. Закон Фарадея освоен!",
|
||
p33_done:"Самоиндукция освоен!",
|
||
ch5_done:"Глава 5 пройдена!"
|
||
};
|
||
|
||
function loadProgress(){
|
||
try{
|
||
const s=localStorage.getItem('physics10_ch5_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
|
||
const a=localStorage.getItem('physics10_ch5_achievements');
|
||
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
|
||
STATE.xp=+(localStorage.getItem('physics10_xp')||0); STATE.level=calcLevel(STATE.xp);
|
||
}catch(e){}
|
||
}
|
||
function saveProgress(){
|
||
try{
|
||
localStorage.setItem('physics10_ch5_progress', JSON.stringify(STATE.progress));
|
||
localStorage.setItem('physics10_ch5_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
|
||
localStorage.setItem('physics10_xp', String(STATE.xp));
|
||
}catch(e){}
|
||
}
|
||
function bumpProgress(key, delta){
|
||
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
|
||
saveProgress(); refreshProgressUI();
|
||
if(STATE.progress[key]>=50) markParaRead(key);
|
||
}
|
||
|
||
const _markedRead=new Set();
|
||
let _pendingProgressBody=null, _progressTimer=null;
|
||
function _flushProgress(){
|
||
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
|
||
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
|
||
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
|
||
}
|
||
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
|
||
function markLastPara(id){ _queueProgress({last_para:id}); }
|
||
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
|
||
window.addEventListener('beforeunload', _flushProgress);
|
||
function loadServerReadState(){
|
||
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
|
||
fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{
|
||
if(!d||!d.progress) return;
|
||
(d.progress.read||[]).forEach(k=>{_markedRead.add(k); if((STATE.progress[k]||0)<50) STATE.progress[k]=100;});
|
||
saveProgress(); refreshProgressUI();
|
||
}).catch(()=>{});
|
||
}
|
||
|
||
function addXp(n,src){
|
||
if(!n) return;
|
||
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
|
||
saveProgress(); refreshProgressUI();
|
||
if(window.LS&&window.LS.xp) window.LS.xp.add(n,'physics10-ch5-'+(src||'misc'));
|
||
if(STATE.level>prev){
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
|
||
}
|
||
}
|
||
|
||
function refreshProgressUI(){
|
||
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
|
||
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
|
||
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
|
||
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
|
||
const xpBadge=document.getElementById('hero-xp-badge');
|
||
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
|
||
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
|
||
}
|
||
|
||
function achievement(id,text){
|
||
if(STATE.achievements.has(id)) return;
|
||
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
|
||
addXp(20,'ach-'+id);
|
||
}
|
||
|
||
function buildParaSelector(){
|
||
const g=document.getElementById('psel-grid'); g.innerHTML='';
|
||
PARAS.forEach(p=>{
|
||
const card=document.createElement('div');
|
||
card.className='psel-card'+(p.final?' final':'');
|
||
card.dataset.id=p.id; card.dataset.progCard=p.id;
|
||
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
|
||
card.addEventListener('click', ()=>goTo(p.id));
|
||
g.appendChild(card);
|
||
});
|
||
}
|
||
|
||
const BUILT=new Set();
|
||
const BUILDERS = { p27:()=>build_p27(), p28:()=>build_p28(), p29:()=>build_p29(), p30:()=>build_p30(), p31:()=>build_p31(), p32:()=>build_p32(), p33:()=>build_p33(), final5:()=>build_final5() };
|
||
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
|
||
function goTo(id){
|
||
STATE.current=id; ensureBuilt(id);
|
||
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
|
||
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
|
||
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
|
||
buildSidebar(id);
|
||
window.scrollTo({top:0,behavior:'smooth'});
|
||
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
|
||
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
|
||
markLastPara(id);
|
||
}
|
||
|
||
const SIDEBARS = {
|
||
p27:{title:"Шпаргалка §27",rows:[["Эрстед","ток отклоняет стрелку"],["Поле тока","вихревое, $\\vec{B}$"],["Правая рука","для проводника"]]},
|
||
p28:{title:"Шпаргалка §28",rows:[["$\\vec{B}$","индукция магн. поля — Тл"],["Линии","замкнутые, без начала/конца"],["Опр.","$B = F_{max}/(IL)$"]]},
|
||
p29:{title:"Шпаргалка §29",rows:[["$F_A = BIL\\sin\\alpha$","сила Ампера"],["Левая рука","для напр-я силы"],["$F_A \\bot \\vec{B}, \\vec{I}$",""]]},
|
||
p30:{title:"Шпаргалка §30",rows:[["$F_л = qvB\\sin\\alpha$","сила Лоренца"],["Радиус","$r = mv/(qB)$"],["$F_л \\bot \\vec{v}$","траектория — окружность/спираль"]]},
|
||
p31:{title:"Шпаргалка §31",rows:[["$\\Phi = BS\\cos\\alpha$","магн. поток — Вб"],["ЭМИ","при $\\Delta\\Phi \\ne 0$ возникает $\\mathcal{E}_i$"],["Опыт Фарадея",""]]},
|
||
p32:{title:"Шпаргалка §32",rows:[["Ленц","$I_{инд}$ противодействует причине"],["$\\mathcal{E}_i = -d\\Phi/dt$","закон Фарадея"],["Знак","определяет Ленц"]]},
|
||
p33:{title:"Шпаргалка §33",rows:[["$\\mathcal{E}_{si} = -L\\dfrac{dI}{dt}$","самоиндукция"],["$L$","индуктивность — Гн"],["$W_L = LI^2/2$","энергия магн. поля"]]},
|
||
final5:{title:"Финал главы 5",rows:[["§§27–33","теория главы 5"],["Награда","+50 XP"]]}
|
||
};
|
||
|
||
const TIPS=[
|
||
{sec:'p27',html:"Опыт Эрстеда показал: ток создаёт магн. поле. Линии $\\vec{B}$ вокруг тока — концентр. окружности."},
|
||
{sec:'p28',html:"$\\vec{B}$ — индукция магн. поля, измер. в Тл. Линии замкнуты (магн. поле — вихревое)."},
|
||
{sec:'p29',html:"Сила Ампера: $F_A = BIL\\sin\\alpha$. Направление — по правилу левой руки."},
|
||
{sec:'p30',html:"Сила Лоренца: $F_л = qvB\\sin\\alpha$. Заряд движется по окружности с $r = mv/(qB)$."},
|
||
{sec:'p31',html:"Магн. поток: $\\Phi = BS\\cos\\alpha$. Измеряется в Вб. ЭМИ возникает при $\\Delta\\Phi \\ne 0$."},
|
||
{sec:'p32',html:"Закон Фарадея: $\\mathcal{E}_i = -\\dfrac{d\\Phi}{dt}$. Правило Ленца: $I_{инд}$ противодействует $\\Delta\\Phi$."},
|
||
{sec:'p33',html:"Самоиндукция: $\\mathcal{E}_{si} = -L\\dfrac{dI}{dt}$. Энергия магн. поля катушки: $W_L = LI^2/2$."},
|
||
{sec:'final5',html:"Финал главы 5 — интегрированные задачи по §§27–33. В разработке (Phase 5+)."}
|
||
];
|
||
|
||
function buildSidebar(id){
|
||
const box=document.getElementById('sidebar-content');
|
||
const sb=SIDEBARS[id]||SIDEBARS[PARAS[0].id];
|
||
let html='';
|
||
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
|
||
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
|
||
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
|
||
html+='<div class="xp-card"><div class="xp-card-title"><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
|
||
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
|
||
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' \u2014 '+v:'')+'</div>'; });
|
||
html+='</div>';
|
||
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
|
||
if(tip){
|
||
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
|
||
}
|
||
if(STATE.achievements.size>0){
|
||
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
|
||
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ '+text+'</div>'; });
|
||
html+='</div>';
|
||
}
|
||
box.innerHTML=html;
|
||
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
|
||
}
|
||
|
||
function initTheme(){
|
||
const t=localStorage.getItem('physics10_ch5_theme')||'light';
|
||
if(t==='dark') document.documentElement.classList.add('dark');
|
||
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
|
||
document.getElementById('theme-btn').addEventListener('click', ()=>{
|
||
document.documentElement.classList.toggle('dark');
|
||
const dark=document.documentElement.classList.contains('dark');
|
||
localStorage.setItem('physics10_ch5_theme', dark?'dark':'light');
|
||
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
|
||
});
|
||
}
|
||
|
||
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
|
||
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'✓ Верно!':'✗ Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
|
||
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
|
||
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
|
||
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
|
||
function makeCard(kind, title, num, body){
|
||
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
|
||
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
|
||
}
|
||
function setupSorter(cfg){
|
||
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
|
||
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
|
||
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
|
||
let armed = null;
|
||
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
|
||
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
|
||
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
|
||
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
|
||
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
|
||
attachBoxTaps(); render();
|
||
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
|
||
}
|
||
|
||
/* === SVG-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */
|
||
|
||
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
|
||
const ux = (W - 2*pad) / (xmax - xmin);
|
||
const uy = (H - 2*pad) / (ymax - ymin);
|
||
const toX = v => pad + (v - xmin) * ux;
|
||
const toY = v => H - pad - (v - ymin) * uy;
|
||
let g = '';
|
||
g += '<g stroke="#e5e7eb" stroke-width="1">';
|
||
for (let x = Math.ceil(xmin); x <= xmax; x++){
|
||
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
|
||
}
|
||
for (let y = Math.ceil(ymin); y <= ymax; y++){
|
||
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
|
||
}
|
||
g += '</g>';
|
||
const y0 = toY(0), x0 = toX(0);
|
||
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
|
||
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
|
||
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
|
||
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
|
||
g += '<g font-size="10" fill="#64748b">';
|
||
for (let x = Math.ceil(xmin); x <= xmax; x++){
|
||
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
|
||
}
|
||
for (let y = Math.ceil(ymin); y <= ymax; y++){
|
||
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
|
||
}
|
||
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
|
||
g += '</g>';
|
||
return { content: g, toX, toY, ux, uy };
|
||
}
|
||
|
||
function plotFunc(f, xmin, xmax, toX, toY, color, N){
|
||
N = N || 200;
|
||
let d = '';
|
||
let prevValid = false;
|
||
for (let i = 0; i <= N; i++){
|
||
const x = xmin + (xmax - xmin) * i / N;
|
||
let y;
|
||
try { y = f(x); } catch(e){ y = NaN; }
|
||
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
|
||
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
|
||
prevValid = true;
|
||
}
|
||
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
|
||
}
|
||
|
||
function pointWithDrop(x, fx, toX, toY, color, label){
|
||
const px = toX(x), py = toY(fx);
|
||
let s = '';
|
||
s += '<line x1="'+px+'" y1="'+py+'" x2="'+px+'" y2="'+toY(0)+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
|
||
s += '<line x1="'+px+'" y1="'+py+'" x2="'+toX(0)+'" y2="'+py+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
|
||
s += '<circle cx="'+px+'" cy="'+py+'" r="4.5" fill="'+color+'" stroke="#fff" stroke-width="2"/>';
|
||
if (label){
|
||
s += '<text x="'+(px+8)+'" y="'+(py-8)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+color+'">'+label+'</text>';
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function asymptote(orientation, value, toX, toY, xmin, xmax, ymin, ymax, color){
|
||
color = color || '#94a3b8';
|
||
if (orientation === 'h'){
|
||
const y = toY(value);
|
||
return '<line x1="'+toX(xmin)+'" y1="'+y+'" x2="'+toX(xmax)+'" y2="'+y+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
|
||
} else {
|
||
const x = toX(value);
|
||
return '<line x1="'+x+'" y1="'+toY(ymin)+'" x2="'+x+'" y2="'+toY(ymax)+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
|
||
}
|
||
}
|
||
|
||
function snapToValue(value, snapPoints, tolerance){
|
||
tolerance = tolerance || 0.1;
|
||
for (const sp of snapPoints){
|
||
if (Math.abs(value - sp) < tolerance) return sp;
|
||
}
|
||
return value;
|
||
}
|
||
|
||
function rightAngleMark(V, uIn, wIn, s){
|
||
s = s || 9;
|
||
const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
|
||
const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
|
||
const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
|
||
return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
|
||
}
|
||
function angleArcAuto(V, uA, uB, R){
|
||
const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
|
||
const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
|
||
const cross = uA.x*uB.y - uA.y*uB.x;
|
||
const sweep = cross > 0 ? 1 : 0;
|
||
return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
|
||
}
|
||
function unitVec(p1, p2){
|
||
const dx = p2.x - p1.x, dy = p2.y - p1.y;
|
||
const len = Math.sqrt(dx*dx + dy*dy) || 1;
|
||
return {x: dx/len, y: dy/len};
|
||
}
|
||
function deg2rad(d){ return d * Math.PI / 180; }
|
||
|
||
const ICONS = {
|
||
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
|
||
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
|
||
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
|
||
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
|
||
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
|
||
oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
|
||
};
|
||
|
||
function secNavFor(curId){
|
||
const idx = PARAS.findIndex(p => p.id === curId);
|
||
const prev = idx > 0 ? PARAS[idx-1].id : null;
|
||
const next = idx < PARAS.length - 1 ? PARAS[idx+1].id : null;
|
||
return secNav(prev, next);
|
||
}
|
||
function secNav(prev, next){
|
||
const NAMES = {p27:'\xA727',p28:'\xA728',p29:'\xA729',p30:'\xA730',p31:'\xA731',p32:'\xA732',p33:'\xA733',final5:'Финал'};
|
||
let h='<div class="sec-nav">';
|
||
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
|
||
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
|
||
h+='</div>'; return h;
|
||
}
|
||
|
||
function readButton(paraId){
|
||
const p = PARAS.find(x => x.id === paraId);
|
||
const labelTail = p && p.final ? 'финал' : (p ? p.num : '\xA7?');
|
||
return '<div style="margin-top:18px;display:flex;justify-content:center">'
|
||
+'<button class="btn primary" id="'+paraId+'-read-btn">'
|
||
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
|
||
+' Я прочитал — '+labelTail+' (+10 XP)'
|
||
+'</button></div>';
|
||
}
|
||
function wireReadBtn(paraId){
|
||
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
|
||
btn.addEventListener('click', ()=>{
|
||
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
|
||
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
|
||
const aId = paraId+'_done';
|
||
if(ACH_LABELS[aId]) achievement(aId);
|
||
});
|
||
}
|
||
|
||
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
|
||
|
||
function build_p27(){
|
||
const box = document.getElementById('p27-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Магнитное поле и опыт Эрстеда */
|
||
html += makeCard('theory', "Магнитное поле и опыт Эрстеда", "§27", `
|
||
<p><b>Магнитное поле</b> — особый вид материи, возникающий вокруг движущихся электрических зарядов и магнитов. Действует на другие движущиеся заряды и магниты.</p>
|
||
<p style="margin-top:10px"><b>Открытие связи электричества и магнетизма.</b> В 1820 году датский физик обнаружил, что магнитная стрелка возле проводника с током <b>отклоняется</b>. Это доказало, что электрический ток создаёт магнитное поле.</p>
|
||
<p style="margin-top:10px">До этого опыта электричество и магнетизм считались разными явлениями. После — стало ясно, что это <b>разные проявления одного электромагнитного взаимодействия</b>.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Главная мысль.</b> Магнитное поле <b>создаётся движущимися зарядами</b> (током, движущимися частицами, постоянными магнитами) и <b>действует на движущиеся заряды</b> и магниты.</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Источники и свойства поля */
|
||
html += makeCard('rule', "Источники и свойства поля", "§27", `
|
||
<p><b>Источники магнитного поля:</b></p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li><b>Постоянные магниты</b> (магнетит, ферриты, неодимовые магниты).</li>
|
||
<li><b>Проводники с током</b> (любые).</li>
|
||
<li><b>Движущиеся заряженные частицы</b>.</li>
|
||
<li><b>Земля</b> — гигантский магнит, создающий геомагнитное поле.</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Главные свойства:</b></p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Поле — <b>векторная</b> характеристика, обозначается $\\vec{B}$ — <b>магнитная индукция</b>.</li>
|
||
<li>Линии поля <b>замкнуты</b> (нет «магнитных зарядов» — северного или южного по отдельности).</li>
|
||
<li>Поле действует <b>только</b> на движущиеся заряды и магниты.</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Сравни.</b> Электрическое поле создают <b>любые</b> заряды (даже неподвижные). Магнитное — только <b>движущиеся</b>.</p>
|
||
`);
|
||
|
||
/* THEORY 3 — Взаимодействие проводников с током */
|
||
html += makeCard('example', "Взаимодействие двух проводников с током", "§27", `
|
||
<p><b>Опыт Ампера</b> (1820): два параллельных проводника с током <b>взаимодействуют</b>.</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li><b>Параллельные токи</b> (в одном направлении) — <b>притягиваются</b>.</li>
|
||
<li><b>Антипараллельные токи</b> (в разных направлениях) — <b>отталкиваются</b>.</li>
|
||
</ul>
|
||
<p style="margin-top:10px">Это <b>противоположно</b> зарядам! (Одноимённые заряды отталкиваются, одноимённо направленные токи — притягиваются.)</p>
|
||
<p style="margin-top:10px"><b>Объяснение.</b> Первый проводник создаёт магнитное поле в области второго; это поле действует с силой на ток во втором проводнике. Эта сила называется <b>силой Ампера</b> (§29).</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Определение Ампера через силу</b> (СИ): $1$ А — сила тока, при которой два параллельных проводника длиной $1$ м, расположенные на расстоянии $1$ м, взаимодействуют с силой $F = 2 \\cdot 10^{-7}$ Н.</p>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — Опыт Эрстеда */
|
||
html += `<div class="wg" id="p27-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Опыт Эрстеда: ток отклоняет стрелку</div></div>
|
||
<div class="wg-help">Переключай ток в проводнике и смотри, как ведёт себя магнитная стрелка под ним. Без тока — стрелка показывает на север.</div>
|
||
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px;flex-wrap:wrap">
|
||
<button class="btn" id="p27-iv1-off">Ток выкл.</button>
|
||
<button class="btn primary" id="p27-iv1-right">Ток вправо</button>
|
||
<button class="btn" id="p27-iv1-left">Ток влево</button>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p27-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p27-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.65;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Взаимодействие проводников */
|
||
html += `<div class="wg" id="p27-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Два проводника: притяжение или отталкивание?</div></div>
|
||
<div class="wg-help">Переключай направление тока в каждом проводнике. Параллельные токи притягиваются, антипараллельные — отталкиваются.</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px">
|
||
<div style="text-align:center"><div style="font-size:.88rem;color:var(--muted);margin-bottom:4px">$I_1$ (слева)</div>
|
||
<button class="btn primary" id="p27-iv2-1up" style="margin:2px">Вверх ↑</button>
|
||
<button class="btn" id="p27-iv2-1dn" style="margin:2px">Вниз ↓</button>
|
||
</div>
|
||
<div style="text-align:center"><div style="font-size:.88rem;color:var(--muted);margin-bottom:4px">$I_2$ (справа)</div>
|
||
<button class="btn primary" id="p27-iv2-2up" style="margin:2px">Вверх ↑</button>
|
||
<button class="btn" id="p27-iv2-2dn" style="margin:2px">Вниз ↓</button>
|
||
</div>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p27-iv2-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p27-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.65;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Откуда берётся поле? */
|
||
html += `<div class="wg" id="p27-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Откуда берётся магнитное поле?</div></div>
|
||
<div class="wg-help">Определи источник магнитного поля в каждой ситуации.</div>
|
||
<div class="score-display"><span>Задача <b id="p27-iv3-i">1</b> / 6</span><span>Очки: <b id="p27-iv3-s">0</b> / 6</span></div>
|
||
<div id="p27-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p27-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p27-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p27-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр */
|
||
html += `<div class="wg" id="p27-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: магнитное поле тока</div></div>
|
||
<div class="wg-help">5 вопросов на ключевые понятия §27.</div>
|
||
<div class="score-display"><span>Задача <b id="p27-iv4-i">1</b> / 5</span><span>Очки: <b id="p27-iv4-s">0</b> / 5</span></div>
|
||
<div id="p27-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p27-iv4-opts" style="display:grid;grid-template-columns:1fr;gap:8px"></div>
|
||
<div class="feedback" id="p27-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p27-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav(null, 'p28');
|
||
html += readButton('p27');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Опыт Эрстеда */
|
||
(function(){
|
||
const svg = document.getElementById('p27-iv1-svg');
|
||
const out = document.getElementById('p27-iv1-out');
|
||
const bOff = document.getElementById('p27-iv1-off');
|
||
const bR = document.getElementById('p27-iv1-right');
|
||
const bL = document.getElementById('p27-iv1-left');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
let state = 'right'; // 'off' | 'right' | 'left'
|
||
|
||
function setState(s){
|
||
state = s;
|
||
[bOff,bR,bL].forEach(b => b.classList.remove('primary'));
|
||
({off:bOff, right:bR, left:bL}[s]).classList.add('primary');
|
||
render();
|
||
seen.add(s);
|
||
if(!_done && seen.size >= 3){ _done = true; addXp(10, 'p27-iv1'); bumpProgress('p27', 15); }
|
||
}
|
||
|
||
function render(){
|
||
const W = 480, H = 280;
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Опыт Эрстеда</text>';
|
||
|
||
// Провод горизонтальный
|
||
const wireY = 110;
|
||
g += '<line x1="40" y1="'+wireY+'" x2="440" y2="'+wireY+'" stroke="#0f172a" stroke-width="4" stroke-linecap="round"/>';
|
||
g += '<text x="50" y="'+(wireY-12)+'" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">Проводник</text>';
|
||
|
||
// Стрелка направления тока на проводе
|
||
if(state === 'right'){
|
||
g += PHYS.drawArrow(180, wireY, 300, wireY, '#dc2626', 3, 14);
|
||
g += '<text x="240" y="'+(wireY-6)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">I →</text>';
|
||
} else if(state === 'left'){
|
||
g += PHYS.drawArrow(300, wireY, 180, wireY, '#dc2626', 3, 14);
|
||
g += '<text x="240" y="'+(wireY-6)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">← I</text>';
|
||
} else {
|
||
g += '<text x="240" y="'+(wireY-6)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#64748b">I = 0</text>';
|
||
}
|
||
|
||
// Магнитная стрелка под проводом — крутится
|
||
const compassX = 240, compassY = 210;
|
||
const compassR = 42;
|
||
// По умолчанию (off) — север (вверх). При токе вправо — отклоняется влево (на нас, по правилу буравчика снизу). При токе влево — вправо.
|
||
let angle = 0; // 0 = север (вверх), измеряется по часовой стрелке
|
||
if(state === 'right') angle = -75; // отклонение влево
|
||
else if(state === 'left') angle = 75; // отклонение вправо
|
||
|
||
// Корпус компаса
|
||
g += '<circle cx="'+compassX+'" cy="'+compassY+'" r="'+compassR+'" fill="#fff" stroke="#0f172a" stroke-width="2"/>';
|
||
g += '<text x="'+compassX+'" y="'+(compassY-compassR-4)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#64748b">С</text>';
|
||
g += '<text x="'+compassX+'" y="'+(compassY+compassR+12)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#64748b">Ю</text>';
|
||
g += '<text x="'+(compassX-compassR-6)+'" y="'+(compassY+3)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#64748b">З</text>';
|
||
g += '<text x="'+(compassX+compassR+6)+'" y="'+(compassY+3)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="10" fill="#64748b">В</text>';
|
||
|
||
// Стрелка компаса (вращается)
|
||
const rad = angle * Math.PI / 180;
|
||
const nx = compassX + Math.sin(rad) * (compassR - 6);
|
||
const ny = compassY - Math.cos(rad) * (compassR - 6);
|
||
const sx = compassX - Math.sin(rad) * (compassR - 6);
|
||
const sy = compassY + Math.cos(rad) * (compassR - 6);
|
||
g += '<line x1="'+sx.toFixed(1)+'" y1="'+sy.toFixed(1)+'" x2="'+nx.toFixed(1)+'" y2="'+ny.toFixed(1)+'" stroke="#0f172a" stroke-width="2"/>';
|
||
// Северный конец — красный
|
||
g += '<polygon points="'+nx.toFixed(1)+','+ny.toFixed(1)+' '+(compassX + Math.sin(rad)*(compassR-22) - Math.cos(rad)*5).toFixed(1)+','+(compassY - Math.cos(rad)*(compassR-22) - Math.sin(rad)*5).toFixed(1)+' '+(compassX + Math.sin(rad)*(compassR-22) + Math.cos(rad)*5).toFixed(1)+','+(compassY - Math.cos(rad)*(compassR-22) + Math.sin(rad)*5).toFixed(1)+'" fill="#dc2626"/>';
|
||
// центр
|
||
g += '<circle cx="'+compassX+'" cy="'+compassY+'" r="3" fill="#0f172a"/>';
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let txt = '';
|
||
if(state === 'off'){
|
||
txt = '<b>Ток выключен.</b> Стрелка показывает на север (магнитное поле Земли).';
|
||
} else if(state === 'right'){
|
||
txt = '<b>Ток вправо</b> → стрелка отклонилась влево. Проводник создал магнитное поле, повернувшее стрелку.';
|
||
} else {
|
||
txt = '<b>Ток влево</b> → стрелка отклонилась вправо. Смена направления тока — смена направления поля.';
|
||
}
|
||
out.innerHTML = txt;
|
||
}
|
||
|
||
bOff.addEventListener('click', () => setState('off'));
|
||
bR.addEventListener('click', () => setState('right'));
|
||
bL.addEventListener('click', () => setState('left'));
|
||
setState('right');
|
||
})();
|
||
|
||
/* IV2 — Взаимодействие проводников */
|
||
(function(){
|
||
const svg = document.getElementById('p27-iv2-svg');
|
||
const out = document.getElementById('p27-iv2-out');
|
||
const b1u = document.getElementById('p27-iv2-1up');
|
||
const b1d = document.getElementById('p27-iv2-1dn');
|
||
const b2u = document.getElementById('p27-iv2-2up');
|
||
const b2d = document.getElementById('p27-iv2-2dn');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
let I1 = 'up', I2 = 'up';
|
||
|
||
function setI1(v){ I1 = v; b1u.classList.toggle('primary', v==='up'); b1d.classList.toggle('primary', v==='dn'); render(); }
|
||
function setI2(v){ I2 = v; b2u.classList.toggle('primary', v==='up'); b2d.classList.toggle('primary', v==='dn'); render(); }
|
||
|
||
function render(){
|
||
const W = 480, H = 280;
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Два параллельных проводника</text>';
|
||
|
||
// Два вертикальных провода
|
||
const x1 = 170, x2 = 310;
|
||
const yTop = 50, yBot = 230;
|
||
g += '<line x1="'+x1+'" y1="'+yTop+'" x2="'+x1+'" y2="'+yBot+'" stroke="#0f172a" stroke-width="4" stroke-linecap="round"/>';
|
||
g += '<line x1="'+x2+'" y1="'+yTop+'" x2="'+x2+'" y2="'+yBot+'" stroke="#0f172a" stroke-width="4" stroke-linecap="round"/>';
|
||
|
||
// Стрелки токов
|
||
if(I1 === 'up'){
|
||
g += PHYS.drawArrow(x1, yBot-20, x1, yTop+20, '#dc2626', 3, 12);
|
||
g += '<text x="'+(x1-22)+'" y="'+(yTop+12)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">I₁ ↑</text>';
|
||
} else {
|
||
g += PHYS.drawArrow(x1, yTop+20, x1, yBot-20, '#dc2626', 3, 12);
|
||
g += '<text x="'+(x1-22)+'" y="'+(yBot)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">I₁ ↓</text>';
|
||
}
|
||
if(I2 === 'up'){
|
||
g += PHYS.drawArrow(x2, yBot-20, x2, yTop+20, '#dc2626', 3, 12);
|
||
g += '<text x="'+(x2+22)+'" y="'+(yTop+12)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">I₂ ↑</text>';
|
||
} else {
|
||
g += PHYS.drawArrow(x2, yTop+20, x2, yBot-20, '#dc2626', 3, 12);
|
||
g += '<text x="'+(x2+22)+'" y="'+(yBot)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">I₂ ↓</text>';
|
||
}
|
||
|
||
// Силы взаимодействия
|
||
const sameDir = (I1 === I2);
|
||
const fY = 140;
|
||
if(sameDir){
|
||
// Притяжение: стрелки направлены друг к другу
|
||
g += PHYS.drawArrow(x1+8, fY, x1+58, fY, '#7c3aed', 3, 12);
|
||
g += PHYS.drawArrow(x2-8, fY, x2-58, fY, '#7c3aed', 3, 12);
|
||
g += '<text x="240" y="'+(fY-12)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="#7c3aed">Притяжение</text>';
|
||
g += '<text x="'+(x1+33)+'" y="'+(fY+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#7c3aed">F</text>';
|
||
g += '<text x="'+(x2-33)+'" y="'+(fY+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#7c3aed">F</text>';
|
||
} else {
|
||
// Отталкивание: стрелки направлены в стороны
|
||
g += PHYS.drawArrow(x1+8, fY, x1-42, fY, '#0ea5e9', 3, 12);
|
||
g += PHYS.drawArrow(x2-8, fY, x2+42, fY, '#0ea5e9', 3, 12);
|
||
g += '<text x="240" y="'+(fY-12)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="#0ea5e9">Отталкивание</text>';
|
||
g += '<text x="'+(x1-17)+'" y="'+(fY+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0ea5e9">F</text>';
|
||
g += '<text x="'+(x2+17)+'" y="'+(fY+18)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0ea5e9">F</text>';
|
||
}
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let txt = '';
|
||
if(sameDir){
|
||
txt = '<b>Токи параллельны (в одну сторону)</b> → проводники <b>притягиваются</b>.';
|
||
} else {
|
||
txt = '<b>Токи антипараллельны (в разные стороны)</b> → проводники <b>отталкиваются</b>.';
|
||
}
|
||
out.innerHTML = txt;
|
||
|
||
seen.add(I1+I2);
|
||
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p27-iv2'); bumpProgress('p27', 15); }
|
||
}
|
||
|
||
b1u.addEventListener('click', () => setI1('up'));
|
||
b1d.addEventListener('click', () => setI1('dn'));
|
||
b2u.addEventListener('click', () => setI2('up'));
|
||
b2d.addEventListener('click', () => setI2('dn'));
|
||
render();
|
||
})();
|
||
|
||
/* IV3 — Откуда берётся поле? */
|
||
(function(){
|
||
const OPTS = ['От постоянных магнитов','От движущихся зарядов','Не существует'];
|
||
const Q = [
|
||
{ q:'Возле железного магнита (на холодильнике).', ans:0, why:'Постоянный магнит — источник магнитного поля.' },
|
||
{ q:'Вокруг проводника, по которому течёт ток.', ans:1, why:'Ток — это движущиеся заряды, создающие поле.' },
|
||
{ q:'Возле неподвижного заряженного шарика в вакууме.', ans:2, why:'Неподвижный заряд создаёт только электрическое поле, магнитного нет.' },
|
||
{ q:'Вокруг Земли (геомагнитное поле).', ans:1, why:'Поле создаётся токами в жидком металлическом ядре Земли.' },
|
||
{ q:'Возле сверхпроводящего кольца без тока.', ans:2, why:'Без тока — нет движущихся зарядов, значит, нет и магнитного поля.' },
|
||
{ q:'Возле летящего на большой скорости протона.', ans:1, why:'Движущийся заряд — то же, что ток, и тоже создаёт магнитное поле.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p27-iv3-q');
|
||
const oEl = document.getElementById('p27-iv3-opts');
|
||
const fb = document.getElementById('p27-iv3-fb');
|
||
const iEl = document.getElementById('p27-iv3-i');
|
||
const sEl = document.getElementById('p27-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p27-iv3'); bumpProgress('p27', 25); }
|
||
else if(score >= 4){ addXp(8, 'p27-iv3'); bumpProgress('p27', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p27-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'Магнитное поле создают:', opts:['любые заряды','движущиеся заряды','масса тел'], ans:1, why:'Только движущиеся заряды создают магнитное поле.' },
|
||
{ q:'Кто в 1820 году открыл связь электричества и магнетизма?', opts:['Ньютон','Эрстед','Фарадей'], ans:1, why:'Эрстед заметил отклонение магнитной стрелки возле проводника с током.' },
|
||
{ q:'Два параллельных проводника, токи в одну сторону. Что происходит?', opts:['притягиваются','отталкиваются','ничего'], ans:0, why:'Параллельные токи — притягиваются (опыт Ампера).' },
|
||
{ q:'Два проводника, токи в противоположных направлениях:', opts:['притягиваются','отталкиваются','ничего'], ans:1, why:'Антипараллельные токи — отталкиваются.' },
|
||
{ q:'Вектор магнитной индукции обозначается:', opts:['$\\vec{E}$','$\\vec{B}$','$\\vec{F}$'], ans:1, why:'$\\vec{E}$ — электрическое поле, $\\vec{F}$ — сила, $\\vec{B}$ — магнитная индукция.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p27-iv4-q');
|
||
const oEl = document.getElementById('p27-iv4-opts');
|
||
const fb = document.getElementById('p27-iv4-fb');
|
||
const iEl = document.getElementById('p27-iv4-i');
|
||
const sEl = document.getElementById('p27-iv4-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p27-iv4'); bumpProgress('p27', 25); }
|
||
else if(score >= 3){ addXp(8, 'p27-iv4'); bumpProgress('p27', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = Q[i].opts.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
renderMath(oEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].opts[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p27-iv4-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p27');
|
||
}
|
||
|
||
function build_p28(){
|
||
const box = document.getElementById('p28-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Магнитная индукция */
|
||
html += makeCard('theory', "Магнитная индукция $\\vec{B}$", "§28", `
|
||
<p><b>Магнитная индукция</b> $\\vec{B}$ — векторная характеристика магнитного поля. Определяет силу, действующую на движущуюся заряженную частицу или на проводник с током.</p>
|
||
<p><b>Единица</b> в СИ — <b>Тесла</b> (Тл):</p>
|
||
<p style="text-align:center;margin:8px 0">$$[B] = 1\\text{ Тл} = \\dfrac{1\\text{ Н}}{1\\text{ А}\\cdot 1\\text{ м}} = \\dfrac{1\\text{ кг}}{1\\text{ А}\\cdot 1\\text{ с}^2}$$</p>
|
||
<p style="margin-top:10px"><b>Порядки величин:</b></p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Магнитное поле Земли: $\\sim 5\\cdot 10^{-5}$ Тл.</li>
|
||
<li>Магнит холодильника: $\\sim 5\\cdot 10^{-3}$ Тл.</li>
|
||
<li>Сильный электромагнит: $\\sim 1-2$ Тл.</li>
|
||
<li>Сверхпроводящий магнит МРТ: $\\sim 3$ Тл и выше.</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Направление $\\vec{B}$</b>:
|
||
в постоянном магните <b>снаружи</b> — от северного (N) к южному (S); <b>внутри</b> магнита — от S к N. Линии замкнуты!</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Правило буравчика */
|
||
html += makeCard('rule', "Правило буравчика (правой руки)", "§28", `
|
||
<p><b>Правило буравчика</b> определяет направление $\\vec{B}$ возле проводника с током.</p>
|
||
<p style="margin-top:8px">Возьми проводник правой рукой так, чтобы <b>большой палец</b> указывал по направлению тока. Остальные <b>пальцы</b> покажут направление $\\vec{B}$ (закручивается вокруг проводника).</p>
|
||
<p style="margin-top:10px"><b>Альтернатива</b>: представь штопор (буравчик), который вкручивается в направлении тока — ручка вращается в направлении $\\vec{B}$.</p>
|
||
<p style="margin-top:10px"><b>Линии магнитной индукции вокруг прямого проводника</b>: <b>концентрические окружности</b> в плоскости, перпендикулярной проводнику.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Свойства линий индукции:</b></p>
|
||
<ol style="margin:6px 0 6px 22px">
|
||
<li><b>Замкнуты</b> — главное отличие от электрических линий (магнитных монополей не существует).</li>
|
||
<li>Не пересекаются.</li>
|
||
<li>Густота линий $\\propto B$ (чем гуще, тем сильнее поле).</li>
|
||
<li>Направлены от N к S снаружи магнита.</li>
|
||
</ol>
|
||
`);
|
||
|
||
/* THEORY 3 — Поле кругового тока и соленоида */
|
||
html += makeCard('example', "Поле кругового тока и соленоида", "§28", `
|
||
<p><b>Поле кругового тока</b> (витка): линии индукции образуют картину, похожую на поле магнитного диполя. Виток ведёт себя как <b>маленький магнит</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Одна сторона — N (магнитные линии выходят).</li>
|
||
<li>Другая сторона — S (входят).</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Соленоид</b> — катушка из многих витков. Поле <b>внутри</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Почти <b>однородное</b> (одинаковое по величине и направлению).</li>
|
||
<li>Очень похоже на поле в постоянном магните.</li>
|
||
<li>Направление — правило правой руки для тока в витках.</li>
|
||
</ul>
|
||
<p style="margin-top:10px">Модуль индукции внутри длинного соленоида:</p>
|
||
<p style="text-align:center;margin:8px 0">$$B = \\dfrac{\\mu_0 N I}{L}$$</p>
|
||
<p>где $N$ — число витков, $L$ — длина соленоида, $\\mu_0 = 4\\pi \\cdot 10^{-7}$ Тл·м/А — <b>магнитная постоянная</b>.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Электромагнит</b> — соленоид + железный сердечник. Сердечник усиливает поле в сотни и тысячи раз. Применение: моторы, реле, динамики, МРТ-сканеры, ускорители частиц, левитирующие поезда.</p>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — Правило буравчика */
|
||
html += `<div class="wg" id="p28-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Правило буравчика: $\\vec{B}$ возле проводника</div></div>
|
||
<div class="wg-help">Переключай направление тока. Смотри, как меняется направление линий $\\vec{B}$ (вид сверху, ток идёт сквозь экран).</div>
|
||
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px;flex-wrap:wrap">
|
||
<button class="btn primary" id="p28-iv1-out">Ток ИЗ экрана (•)</button>
|
||
<button class="btn" id="p28-iv1-in">Ток В экран (×)</button>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p28-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p28-iv1-out-txt" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.65;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Соленоид */
|
||
html += `<div class="wg" id="p28-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Магнитное поле соленоида</div></div>
|
||
<div class="wg-help">Меняй ток $I$ и число витков $N$. Длина соленоида $L = 0{,}1$ м. Формула: $B = \\mu_0 N I / L$.</div>
|
||
<div class="sliders">
|
||
<label>$I$: <b id="p28-iv2-IL">2.0</b> А <input type="range" id="p28-iv2-I" min="0" max="5" value="2" step="0.1"></label>
|
||
<label>$N$: <b id="p28-iv2-NL">50</b> витков <input type="range" id="p28-iv2-N" min="10" max="100" value="50" step="1"></label>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p28-iv2-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p28-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.75;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Куда направлено B? */
|
||
html += `<div class="wg" id="p28-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Куда направлено $\\vec{B}$?</div></div>
|
||
<div class="wg-help">Выбери, как определяется (или существует ли вообще) магнитное поле в каждой ситуации.</div>
|
||
<div class="score-display"><span>Задача <b id="p28-iv3-i">1</b> / 6</span><span>Очки: <b id="p28-iv3-s">0</b> / 6</span></div>
|
||
<div id="p28-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p28-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p28-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p28-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр */
|
||
html += `<div class="wg" id="p28-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: индукция магнитного поля</div></div>
|
||
<div class="wg-help">5 вопросов на ключевые понятия §28.</div>
|
||
<div class="score-display"><span>Задача <b id="p28-iv4-i">1</b> / 5</span><span>Очки: <b id="p28-iv4-s">0</b> / 5</span></div>
|
||
<div id="p28-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p28-iv4-opts" style="display:grid;grid-template-columns:1fr;gap:8px"></div>
|
||
<div class="feedback" id="p28-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p28-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav('p27', 'p29');
|
||
html += readButton('p28');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Правило буравчика */
|
||
(function(){
|
||
const svg = document.getElementById('p28-iv1-svg');
|
||
const out = document.getElementById('p28-iv1-out-txt');
|
||
const bOut = document.getElementById('p28-iv1-out');
|
||
const bIn = document.getElementById('p28-iv1-in');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
let dir = 'out'; // 'out' or 'in'
|
||
|
||
function setDir(d){
|
||
dir = d;
|
||
bOut.classList.toggle('primary', d==='out');
|
||
bIn.classList.toggle('primary', d==='in');
|
||
render();
|
||
seen.add(d);
|
||
if(!_done && seen.size >= 2){ _done = true; addXp(10, 'p28-iv1'); bumpProgress('p28', 15); }
|
||
}
|
||
|
||
function render(){
|
||
const W = 480, H = 280;
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Поле прямого тока (вид вдоль проводника)</text>';
|
||
|
||
// Центральный проводник
|
||
const cx = 240, cy = 150;
|
||
const wireColor = '#0f172a';
|
||
|
||
// Концентрические окружности — линии B
|
||
const rings = [40, 70, 100, 130];
|
||
const Bcolor = '#7c3aed';
|
||
rings.forEach(r => {
|
||
g += '<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="'+Bcolor+'" stroke-width="1.5" opacity=".55"/>';
|
||
});
|
||
|
||
// Стрелки касательные к окружностям — направление B по правилу буравчика
|
||
// При токе ИЗ экрана (•) — B по часовой стрелке снизу? Нет: правая рука, большой палец на нас (out), пальцы — против часовой стрелки.
|
||
// Считаю: ток OUT (на наблюдателя) → B против часовой стрелки.
|
||
// ток IN (от наблюдателя) → B по часовой стрелке.
|
||
const ccw = (dir === 'out');
|
||
const arrowAngles = [0, Math.PI/2, Math.PI, 3*Math.PI/2]; // 4 стрелки на каждой окружности
|
||
rings.forEach((r, idx) => {
|
||
if(idx % 2 !== 0) return; // только на двух окружностях
|
||
arrowAngles.forEach((a, k) => {
|
||
const ax = cx + r * Math.cos(a);
|
||
const ay = cy + r * Math.sin(a);
|
||
// Касательный вектор
|
||
const tx = -Math.sin(a) * (ccw ? -1 : 1);
|
||
const ty = Math.cos(a) * (ccw ? -1 : 1);
|
||
const len = 16;
|
||
const x2 = ax + tx * len;
|
||
const y2 = ay + ty * len;
|
||
g += PHYS.drawArrow(ax, ay, x2, y2, Bcolor, 2, 8);
|
||
});
|
||
});
|
||
|
||
// Проводник в центре — крест или точка
|
||
g += '<circle cx="'+cx+'" cy="'+cy+'" r="14" fill="white" stroke="'+wireColor+'" stroke-width="2.4"/>';
|
||
if(dir === 'in'){
|
||
// Крест ×
|
||
g += '<line x1="'+(cx-8)+'" y1="'+(cy-8)+'" x2="'+(cx+8)+'" y2="'+(cy+8)+'" stroke="'+wireColor+'" stroke-width="3"/>';
|
||
g += '<line x1="'+(cx-8)+'" y1="'+(cy+8)+'" x2="'+(cx+8)+'" y2="'+(cy-8)+'" stroke="'+wireColor+'" stroke-width="3"/>';
|
||
} else {
|
||
// Точка •
|
||
g += '<circle cx="'+cx+'" cy="'+cy+'" r="4" fill="'+wireColor+'"/>';
|
||
}
|
||
|
||
// Подпись
|
||
const lbl = dir === 'out' ? 'Ток I из экрана (•)' : 'Ток I в экран (×)';
|
||
g += '<text x="'+cx+'" y="'+(cy+45)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+wireColor+'">'+lbl+'</text>';
|
||
// Подпись B
|
||
g += '<text x="'+(cx + 150)+'" y="'+(cy)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="'+Bcolor+'">B</text>';
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let txt = '';
|
||
if(dir === 'out'){
|
||
txt = '<b>Ток направлен ИЗ экрана</b> (на тебя). Правило буравчика: большой палец на тебя → линии $\\vec{B}$ <b>против часовой стрелки</b>.';
|
||
} else {
|
||
txt = '<b>Ток направлен В экран</b> (от тебя). Правило буравчика: большой палец от тебя → линии $\\vec{B}$ <b>по часовой стрелке</b>.';
|
||
}
|
||
out.innerHTML = txt;
|
||
renderMath(out);
|
||
}
|
||
|
||
bOut.addEventListener('click', () => setDir('out'));
|
||
bIn.addEventListener('click', () => setDir('in'));
|
||
setDir('out');
|
||
})();
|
||
|
||
/* IV2 — Соленоид */
|
||
(function(){
|
||
const svg = document.getElementById('p28-iv2-svg');
|
||
const out = document.getElementById('p28-iv2-out');
|
||
const IS = document.getElementById('p28-iv2-I');
|
||
const NS = document.getElementById('p28-iv2-N');
|
||
const IL = document.getElementById('p28-iv2-IL');
|
||
const NL = document.getElementById('p28-iv2-NL');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
|
||
function render(){
|
||
const I = +IS.value, N = +NS.value;
|
||
IL.textContent = I.toFixed(1);
|
||
NL.textContent = N.toFixed(0);
|
||
const L = 0.1; // м
|
||
const mu0 = 4 * Math.PI * 1e-7;
|
||
const B = mu0 * N * I / L;
|
||
|
||
const W = 480, H = 280;
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Соленоид: однородное поле внутри</text>';
|
||
|
||
// Соленоид — нарисуем как ряд овалов (витков) горизонтально
|
||
const cy = 145;
|
||
const xL = 90, xR = 390;
|
||
const numVisible = Math.min(20, Math.max(5, Math.round(N/5)));
|
||
const stepX = (xR - xL) / (numVisible - 1);
|
||
const Bcolor = '#7c3aed';
|
||
|
||
// Корпус соленоида
|
||
g += '<rect x="'+xL+'" y="'+(cy-40)+'" width="'+(xR-xL)+'" height="80" fill="rgba(124,58,237,0.05)" stroke="#94a3b8" stroke-width="1.4" stroke-dasharray="4 3"/>';
|
||
|
||
// Витки как петли
|
||
for(let i = 0; i < numVisible; i++){
|
||
const x = xL + i * stepX;
|
||
g += '<ellipse cx="'+x+'" cy="'+cy+'" rx="6" ry="35" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
|
||
// Точка на верхушке (ток на нас) или крест внизу (от нас) — упрощённо
|
||
g += '<circle cx="'+x+'" cy="'+(cy-35)+'" r="2.5" fill="#dc2626"/>';
|
||
// Крест внизу
|
||
g += '<line x1="'+(x-3)+'" y1="'+(cy+33)+'" x2="'+(x+3)+'" y2="'+(cy+37)+'" stroke="#dc2626" stroke-width="1.2"/>';
|
||
g += '<line x1="'+(x-3)+'" y1="'+(cy+37)+'" x2="'+(x+3)+'" y2="'+(cy+33)+'" stroke="#dc2626" stroke-width="1.2"/>';
|
||
}
|
||
|
||
// Внутреннее поле — стрелки слева направо (зависит от направления намотки, считаем что N справа)
|
||
if(I > 0.01){
|
||
const nArrows = 5;
|
||
for(let k = 0; k < nArrows; k++){
|
||
const ax = xL + 30 + k * ((xR-xL-60)/(nArrows-1));
|
||
const arrowLen = Math.min(50, 20 + (B * 1e3) * 8);
|
||
g += PHYS.drawArrow(ax - arrowLen/2, cy, ax + arrowLen/2, cy, Bcolor, 2.5, 10);
|
||
}
|
||
// Подписи полюсов
|
||
g += '<text x="'+(xL-18)+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#0ea5e9">S</text>';
|
||
g += '<text x="'+(xR+18)+'" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#dc2626">N</text>';
|
||
|
||
// Линии поля снаружи (упрощённо — две дуги)
|
||
g += '<path d="M '+xR+' '+(cy-35)+' Q '+(xR+60)+' '+(cy-90)+' '+(xL+ (xR-xL)/2)+' '+(cy-100)+' Q '+(xL-60)+' '+(cy-90)+' '+xL+' '+(cy-35)+'" fill="none" stroke="'+Bcolor+'" stroke-width="1.4" opacity=".55"/>';
|
||
g += '<path d="M '+xR+' '+(cy+35)+' Q '+(xR+60)+' '+(cy+90)+' '+(xL+ (xR-xL)/2)+' '+(cy+100)+' Q '+(xL-60)+' '+(cy+90)+' '+xL+' '+(cy+35)+'" fill="none" stroke="'+Bcolor+'" stroke-width="1.4" opacity=".55"/>';
|
||
} else {
|
||
g += '<text x="240" y="'+(cy+5)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" fill="#64748b">I = 0 → поля нет</text>';
|
||
}
|
||
|
||
// Метка B
|
||
if(I > 0.01) g += '<text x="240" y="'+(cy-50)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="'+Bcolor+'">B</text>';
|
||
|
||
svg.innerHTML = g;
|
||
|
||
out.innerHTML = '<b>$B = \\dfrac{\\mu_0 N I}{L} = \\dfrac{4\\pi\\cdot 10^{-7}\\cdot '+N.toFixed(0)+'\\cdot '+I.toFixed(2)+'}{0{,}1} = '+B.toExponential(2).replace('e','\\cdot 10^{').replace('+','')+'}$ Тл</b>';
|
||
renderMath(out);
|
||
|
||
seen.add(I.toFixed(1)+':'+N.toFixed(0));
|
||
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p28-iv2'); bumpProgress('p28', 15); }
|
||
}
|
||
|
||
IS.addEventListener('input', render);
|
||
NS.addEventListener('input', render);
|
||
render();
|
||
})();
|
||
|
||
/* IV3 — Куда направлено B? */
|
||
(function(){
|
||
const OPTS = ['От N к S','По правилу буравчика','От заряда','$\\vec{B} = 0$'];
|
||
const Q = [
|
||
{ q:'Снаружи постоянного магнита (вне корпуса).', ans:0, why:'Снаружи магнита линии $\\vec{B}$ идут от северного полюса к южному.' },
|
||
{ q:'Возле прямого проводника с током.', ans:1, why:'Используем правило правой руки (буравчика).' },
|
||
{ q:'Внутри сверхпроводящего кольца без тока.', ans:3, why:'Нет тока — нет источника поля, $\\vec{B} = 0$.' },
|
||
{ q:'В вакууме вдали от любых источников.', ans:3, why:'Без источников $\\vec{B} = 0$.' },
|
||
{ q:'Внутри соленоида с током.', ans:1, why:'Направление определяется правилом правой руки для тока в витках.' },
|
||
{ q:'В точке посередине между двумя одинаковыми параллельными токами одного направления.', ans:3, why:'Поля от двух проводников в этой точке направлены противоположно и взаимно компенсируются.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p28-iv3-q');
|
||
const oEl = document.getElementById('p28-iv3-opts');
|
||
const fb = document.getElementById('p28-iv3-fb');
|
||
const iEl = document.getElementById('p28-iv3-i');
|
||
const sEl = document.getElementById('p28-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p28-iv3'); bumpProgress('p28', 25); }
|
||
else if(score >= 4){ addXp(8, 'p28-iv3'); bumpProgress('p28', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
renderMath(oEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p28-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'Единица измерения магнитной индукции в СИ:', opts:['Тесла (Тл)','Вольт (В)','Ампер (А)'], ans:0, why:'$1$ Тл $= 1$ Н/(А·м).' },
|
||
{ q:'Магнитное поле Земли по порядку величины (в Тл):', opts:['$5\\cdot 10^{-5}$','$5\\cdot 10^{-2}$','$5\\cdot 10^{1}$'], ans:0, why:'Геомагнитное поле слабое: $\\sim 5\\cdot 10^{-5}$ Тл.' },
|
||
{ q:'Направление $\\vec{B}$ возле прямого проводника находят:', opts:['по закону Ома','по правилу буравчика','по второму закону Ньютона'], ans:1, why:'Правило правой руки (буравчика): большой палец — по току, пальцы — по $\\vec{B}$.' },
|
||
{ q:'Коэффициент $\\mu_0$ в формуле $B = \\mu_0 N I / L$ равен:', opts:['$4\\pi\\cdot 10^{-7}$ Тл·м/А','$8{,}85\\cdot 10^{-12}$ Ф/м','$9\\cdot 10^{9}$ Н·м²/Кл²'], ans:0, why:'$\\mu_0 = 4\\pi\\cdot 10^{-7}$ Тл·м/А — магнитная постоянная.' },
|
||
{ q:'Линии магнитной индукции замкнуты, потому что:', opts:['поле слабое','магнитных монополей не существует','так удобно рисовать'], ans:1, why:'В природе нет отдельных «северных» или «южных» зарядов, поэтому линии $\\vec{B}$ всегда замкнуты.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p28-iv4-q');
|
||
const oEl = document.getElementById('p28-iv4-opts');
|
||
const fb = document.getElementById('p28-iv4-fb');
|
||
const iEl = document.getElementById('p28-iv4-i');
|
||
const sEl = document.getElementById('p28-iv4-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p28-iv4'); bumpProgress('p28', 25); }
|
||
else if(score >= 3){ addXp(8, 'p28-iv4'); bumpProgress('p28', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = Q[i].opts.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
renderMath(oEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].opts[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p28-iv4-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p28');
|
||
}
|
||
|
||
function build_p29(){
|
||
const box = document.getElementById('p29-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Сила Ампера */
|
||
html += makeCard('theory', "Сила Ампера", "§29", `
|
||
<p><b>Сила Ампера</b> — сила, действующая на проводник с током, помещённый в магнитное поле.</p>
|
||
<p style="margin-top:8px"><b>Модуль</b>:</p>
|
||
<p style="text-align:center;margin:8px 0">$$F_A = B I L \\sin\\alpha$$</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>$B$ — индукция магнитного поля (Тл),</li>
|
||
<li>$I$ — сила тока в проводнике (А),</li>
|
||
<li>$L$ — длина участка проводника в поле (м),</li>
|
||
<li>$\\alpha$ — угол между направлением тока и вектором $\\vec{B}$.</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Особые случаи:</b></p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>$\\alpha = 90°$ (ток $\\perp \\vec{B}$): $F_{max} = B I L$ — максимум.</li>
|
||
<li>$\\alpha = 0°$ или $180°$ (ток $\\parallel \\vec{B}$): $F_A = 0$ — сила отсутствует.</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Главная мысль.</b> Магнитное поле действует на ток. Чем сильнее поле, длиннее провод, больше ток и удачнее ориентация (ближе к $90°$), тем больше сила.</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Правило левой руки */
|
||
html += makeCard('rule', "Правило левой руки", "§29", `
|
||
<p><b>Правило левой руки</b> — приём для определения направления силы Ампера.</p>
|
||
<p style="margin-top:8px">Расположи <b>левую руку</b> так, чтобы:</p>
|
||
<ol style="margin:6px 0 6px 22px">
|
||
<li>Линии магнитной индукции $\\vec{B}$ <b>входили в ладонь</b> (поле «прокалывало» ладонь).</li>
|
||
<li>Четыре <b>выпрямленных пальца</b> показывали направление <b>тока</b> $I$.</li>
|
||
<li>Тогда отогнутый под $90°$ <b>большой палец</b> покажет направление <b>силы Ампера</b> $\\vec{F_A}$.</li>
|
||
</ol>
|
||
<p style="margin-top:10px"><b>Свойство.</b> Сила Ампера $\\vec{F_A}$ всегда <b>перпендикулярна</b> и проводнику с током, и вектору $\\vec{B}$.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Важно.</b> Это же правило (с заменой «тока» на «скорость положительного заряда») работает для силы Лоренца (§30). Для отрицательного заряда направление противоположное.</p>
|
||
`);
|
||
|
||
/* THEORY 3 — Применение */
|
||
html += makeCard('example', "Где работает сила Ампера", "§29", `
|
||
<p><b>Электродвигатель.</b> В магнитном поле находится рамка с током. Силы Ампера, действующие на её стороны, создают <b>вращающий момент</b>. Рамка вращается — получается механическое движение из электрического тока. От детских игрушек до электровозов и промышленных приводов — везде это устройство.</p>
|
||
<p style="margin-top:10px"><b>Электроизмерительные приборы.</b> В <b>гальванометре</b> рамка с током отклоняется в поле постоянного магнита; угол отклонения пропорционален силе тока, и стрелка показывает его на шкале.</p>
|
||
<p style="margin-top:10px"><b>Громкоговоритель.</b> Переменный ток в катушке, помещённой в поле постоянного магнита, заставляет мембрану колебаться в такт со звуковым сигналом — рождаются звуковые волны.</p>
|
||
<p style="margin-top:10px"><b>Магнитная левитация.</b> Токи в катушках поездов на магнитной подвеске (маглев) создают поля, которые отталкивают сверхпроводящие или ферромагнитные элементы пути — поезд буквально парит над рельсами.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Принцип суперпозиции для $\\vec{B}$.</b> Магнитное поле от нескольких источников равно <b>векторной сумме</b> полей от каждого. Поэтому сложные конфигурации (катушки, рамки, провода) разбиваются на простые и складываются.</p>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — Правило левой руки (визуализатор) */
|
||
html += `<div class="wg" id="p29-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Правило левой руки: куда направлена $\\vec{F_A}$?</div></div>
|
||
<div class="wg-help">Магнитное поле $\\vec{B}$ направлено <b>в экран</b> (×). Выбери направление тока и параметры — увидишь силу Ампера.</div>
|
||
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px;flex-wrap:wrap">
|
||
<button class="btn primary" id="p29-iv1-up" data-d="up">Ток вверх ↑</button>
|
||
<button class="btn" id="p29-iv1-dn" data-d="dn">Ток вниз ↓</button>
|
||
<button class="btn" id="p29-iv1-rt" data-d="rt">Ток вправо →</button>
|
||
<button class="btn" id="p29-iv1-lt" data-d="lt">Ток влево ←</button>
|
||
</div>
|
||
<div class="sliders">
|
||
<label>$I$: <b id="p29-iv1-IL">5.0</b> А <input type="range" id="p29-iv1-I" min="0" max="10" value="5" step="0.1"></label>
|
||
<label>$B$: <b id="p29-iv1-BL">1.0</b> Тл <input type="range" id="p29-iv1-B" min="0" max="2" value="1" step="0.05"></label>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p29-iv1-svg" viewBox="0 0 480 320" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p29-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.65;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Калькулятор */
|
||
html += `<div class="wg" id="p29-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор силы Ампера</div></div>
|
||
<div class="wg-help">Введи параметры — получи $F_A = B I L \\sin\\alpha$ в Ньютонах.</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px">
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$B$ (Тл):<input type="number" id="p29-iv2-B" value="0.5" step="0.01" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$I$ (А):<input type="number" id="p29-iv2-I" value="2" step="0.1" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$L$ (м):<input type="number" id="p29-iv2-L" value="0.1" step="0.01" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$\\alpha$ (°):<input type="number" id="p29-iv2-A" value="90" step="1" min="0" max="180" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
</div>
|
||
<div class="actions"><button class="btn primary" id="p29-iv2-calc">Вычислить $F_A$</button></div>
|
||
<div id="p29-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.96rem;line-height:1.7;text-align:center;min-height:40px"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Куда направлена сила Ампера? */
|
||
html += `<div class="wg" id="p29-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Куда направлена сила Ампера?</div></div>
|
||
<div class="wg-help">Используй правило левой руки. 6 ситуаций.</div>
|
||
<div class="score-display"><span>Задача <b id="p29-iv3-i">1</b> / 6</span><span>Очки: <b id="p29-iv3-s">0</b> / 6</span></div>
|
||
<div id="p29-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p29-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p29-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p29-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр (числовые ответы) */
|
||
html += `<div class="wg" id="p29-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: сила Ампера</div></div>
|
||
<div class="wg-help">5 числовых задач. Допуск ±5%. Введи число и нажми Enter.</div>
|
||
<div class="score-display"><span>Задача <b id="p29-iv4-i">1</b> / 5</span><span>Очки: <b id="p29-iv4-s">0</b> / 5</span></div>
|
||
<div id="p29-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p29-iv4-form" style="display:flex;gap:8px;justify-content:center;margin-bottom:8px;flex-wrap:wrap">
|
||
<input type="number" id="p29-iv4-inp" step="any" style="padding:8px 10px;border:1px solid var(--border);border-radius:6px;width:140px;font-size:1rem">
|
||
<button class="btn primary" id="p29-iv4-go">Проверить</button>
|
||
</div>
|
||
<div class="feedback" id="p29-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p29-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav('p28', 'p30');
|
||
html += readButton('p29');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Визуализатор правила левой руки */
|
||
(function(){
|
||
const svg = document.getElementById('p29-iv1-svg');
|
||
const out = document.getElementById('p29-iv1-out');
|
||
const bUp = document.getElementById('p29-iv1-up');
|
||
const bDn = document.getElementById('p29-iv1-dn');
|
||
const bRt = document.getElementById('p29-iv1-rt');
|
||
const bLt = document.getElementById('p29-iv1-lt');
|
||
const IS = document.getElementById('p29-iv1-I');
|
||
const BS = document.getElementById('p29-iv1-B');
|
||
const IL = document.getElementById('p29-iv1-IL');
|
||
const BL = document.getElementById('p29-iv1-BL');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
let dir = 'up';
|
||
|
||
function setDir(d){
|
||
dir = d;
|
||
[bUp,bDn,bRt,bLt].forEach(b => b.classList.remove('primary'));
|
||
({up:bUp,dn:bDn,rt:bRt,lt:bLt}[d]).classList.add('primary');
|
||
render();
|
||
}
|
||
|
||
function render(){
|
||
const I = +IS.value, B = +BS.value;
|
||
IL.textContent = I.toFixed(1);
|
||
BL.textContent = B.toFixed(2);
|
||
const W = 480, H = 320;
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">$\\vec{B}$ направлено в экран (×). Правило левой руки</text>'.replace('$\\vec{B}$','B');
|
||
// Сетка магнитного поля (×)
|
||
g += PHYS.magneticFieldGrid(50, 50, 380, 220, 8, 5, 'in');
|
||
|
||
const cx = 240, cy = 160;
|
||
// Проводник с током
|
||
const L = 90; // длина рисунка
|
||
let x1,y1,x2,y2; // конечные точки тока (стрелка от начала к концу)
|
||
if(dir === 'up'){ x1=cx; y1=cy+L; x2=cx; y2=cy-L; }
|
||
else if(dir === 'dn'){ x1=cx; y1=cy-L; x2=cx; y2=cy+L; }
|
||
else if(dir === 'rt'){ x1=cx-L; y1=cy; x2=cx+L; y2=cy; }
|
||
else { x1=cx+L; y1=cy; x2=cx-L; y2=cy; }
|
||
// Сам провод (в нейтральном цвете)
|
||
g += '<line x1="'+(x1)+'" y1="'+(y1)+'" x2="'+(x2)+'" y2="'+(y2)+'" stroke="#0f172a" stroke-width="5" stroke-linecap="round" opacity=".25"/>';
|
||
// Стрелка тока
|
||
g += PHYS.drawArrow(x1, y1, x2, y2, '#dc2626', 3.5, 16);
|
||
// Метка I
|
||
const Imx = x2 + (dir==='rt'?14:dir==='lt'?-14:0);
|
||
const Imy = y2 + (dir==='up'?-12:dir==='dn'?16:0);
|
||
g += '<text x="'+Imx+'" y="'+Imy+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="14" font-weight="800" fill="#dc2626">I</text>';
|
||
|
||
// Направление силы по правилу левой руки (B в экран):
|
||
// F = I × B. B = -z (в экран). I вверх (+y) → F = y × (-z) = -(y×z) = -x → влево
|
||
// I вниз (-y) → F вправо. I вправо (+x) → F = x×(-z) = -(x×z) = +y? В экранных координатах y растёт вниз; но физический y вверх. В физической системе F=qv×B; в экранных: вверх — это -y_экр. Будем выводить так:
|
||
// Take I dir (ix,iy экранные) and B = into page (z-out screen is "к нам", into page = -z_screen).
|
||
// Use formula: F_screen = I_screen × B_into_screen. Cross with -ẑ: (ix, iy, 0) × (0,0,-1) = (iy*(-1) - 0, 0 - ix*(-1), 0) = (-iy, ix, 0)
|
||
// For 'up': I=(0,-1) → F=(-(-1), 0)=(1,0) → вправо. But правило левой руки: B в экран, I вверх → F влево.
|
||
// The discrepancy is sign of B. "В экран" we treated as B = -ẑ_screen. Let's check by physics: B is into page → in standard right-handed coord (x right, y up, z out of page) B = -ẑ. I up = +ŷ. F = IL × B = ŷ × (-ẑ) = -(ŷ×ẑ) = -x̂ → влево. Yes that matches.
|
||
// In SCREEN coordinates: x_screen=x, y_screen=-y. So I_up means y_screen=-1, y_phys=+1. Convert back: ix_screen=0,iy_screen=-1 → ix_phys=0, iy_phys=+1.
|
||
// Compute F in phys: ix_phys × B(=-ẑ) per above formula F_phys=(-iy_phys, ix_phys). For ix_phys=0,iy_phys=+1: F_phys=(-1,0) → x_phys=-1 → влево. ✓
|
||
// To get F_screen we negate y: F_screen=(F_x_phys, -F_y_phys)=(-1,0) → влево. ✓
|
||
// Generalize: given i_screen=(ix,iy), iy_phys=-iy. F_phys=(-iy_phys, ix_phys)=(iy, ix?). Wait ix_phys=ix. F_phys=(-iy_phys, ix_phys)=(iy, ix). Then F_screen=(F_x_phys, -F_y_phys)=(iy, -ix).
|
||
// Test: up i_screen=(0,-1)→F_screen=(-1, 0)=влево ✓. down (0,1)→(1,0) вправо ✓. right (1,0)→(0,-1)=вверх ✓. left(-1,0)→(0,1)=вниз ✓.
|
||
const map = { up:{ix:0,iy:-1, label:'вверх'}, dn:{ix:0,iy:1, label:'вниз'}, rt:{ix:1,iy:0, label:'вправо'}, lt:{ix:-1,iy:0, label:'влево'} };
|
||
const cur = map[dir];
|
||
const fx = cur.iy, fy = -cur.ix;
|
||
const fLabel = (fx===1)?'вправо':(fx===-1)?'влево':(fy===-1)?'вверх':'вниз';
|
||
// Рисуем силу: начинается из центра провода
|
||
const fLen = 70;
|
||
const fEndX = cx + fx * fLen;
|
||
const fEndY = cy + fy * fLen;
|
||
g += PHYS.drawArrow(cx, cy, fEndX, fEndY, '#0891b2', 4, 18);
|
||
g += '<text x="'+(fEndX + fx*16)+'" y="'+(fEndY + fy*16 + (fy===0?4:0))+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="15" font-weight="800" fill="#0891b2">F</text>';
|
||
|
||
// Подпись внизу
|
||
const Fmod = B * I * 1.0; // L=1 м
|
||
g += '<text x="240" y="305" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#64748b">L = 1 м, $\\alpha = 90°$</text>'.replace('$\\alpha = 90°$','α=90°');
|
||
|
||
svg.innerHTML = g;
|
||
out.innerHTML = '<b>Ток '+cur.label+'</b>, $\\vec{B}$ в экран → сила Ампера <b>направлена '+fLabel+'</b>.<br>$F_A = B \\cdot I \\cdot L = '+B.toFixed(2)+' \\cdot '+I.toFixed(2)+' \\cdot 1 = '+Fmod.toFixed(2)+'$ Н.';
|
||
renderMath(out);
|
||
|
||
seen.add(dir);
|
||
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p29-iv1'); bumpProgress('p29', 15); }
|
||
}
|
||
|
||
[bUp,bDn,bRt,bLt].forEach(b => b.addEventListener('click', () => setDir(b.dataset.d)));
|
||
IS.addEventListener('input', render);
|
||
BS.addEventListener('input', render);
|
||
setDir('up');
|
||
})();
|
||
|
||
/* IV2 — Калькулятор */
|
||
(function(){
|
||
const out = document.getElementById('p29-iv2-out');
|
||
const bGo = document.getElementById('p29-iv2-calc');
|
||
const inB = document.getElementById('p29-iv2-B');
|
||
const inI = document.getElementById('p29-iv2-I');
|
||
const inL = document.getElementById('p29-iv2-L');
|
||
const inA = document.getElementById('p29-iv2-A');
|
||
let count = 0, _done = false;
|
||
|
||
function calc(){
|
||
const B = +inB.value, I = +inI.value, L = +inL.value, A = +inA.value;
|
||
if(!isFinite(B)||!isFinite(I)||!isFinite(L)||!isFinite(A)){
|
||
out.innerHTML = '<b style="color:#dc2626">Введи все значения.</b>';
|
||
return;
|
||
}
|
||
const rad = A * Math.PI / 180;
|
||
const F = B * I * L * Math.sin(rad);
|
||
out.innerHTML = '$F_A = B \\cdot I \\cdot L \\cdot \\sin\\alpha = '+B+' \\cdot '+I+' \\cdot '+L+' \\cdot \\sin '+A+'° = \\mathbf{'+F.toFixed(4)+'}$ <b>Н</b>';
|
||
renderMath(out);
|
||
count++;
|
||
if(!_done && count >= 3){ _done = true; addXp(10, 'p29-iv2'); bumpProgress('p29', 15); }
|
||
}
|
||
bGo.addEventListener('click', calc);
|
||
[inB,inI,inL,inA].forEach(x => x.addEventListener('keydown', e => { if(e.key==='Enter') calc(); }));
|
||
})();
|
||
|
||
/* IV3 — Куда направлена F_A? */
|
||
(function(){
|
||
const OPTS = ['Вправо','Влево','Вверх','Вниз'];
|
||
const Q = [
|
||
{ q:'$\\vec{B}$ в экран (×), ток вверх. Куда направлена $\\vec{F_A}$?', ans:1, why:'По правилу левой руки: B входит в ладонь, 4 пальца — вверх (ток), большой — влево.' },
|
||
{ q:'$\\vec{B}$ из экрана (•), ток вверх. Куда направлена $\\vec{F_A}$?', ans:0, why:'B теперь из ладони, четыре пальца — вверх; большой палец — вправо.' },
|
||
{ q:'$\\vec{B}$ в экран (×), ток вправо. Куда направлена $\\vec{F_A}$?', ans:2, why:'B входит в ладонь, пальцы — вправо; большой палец — вверх.' },
|
||
{ q:'$\\vec{B}$ в экран (×), ток влево. Куда направлена $\\vec{F_A}$?', ans:3, why:'B входит в ладонь, пальцы — влево; большой палец — вниз.' },
|
||
{ q:'$\\vec{B}$ из экрана (•), ток вниз. Куда направлена $\\vec{F_A}$?', ans:1, why:'B из ладони, пальцы — вниз; большой палец — влево.' },
|
||
{ q:'$\\vec{B}$ из экрана (•), ток вправо. Куда направлена $\\vec{F_A}$?', ans:3, why:'B из ладони, пальцы — вправо; большой палец — вниз.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p29-iv3-q');
|
||
const oEl = document.getElementById('p29-iv3-opts');
|
||
const fb = document.getElementById('p29-iv3-fb');
|
||
const iEl = document.getElementById('p29-iv3-i');
|
||
const sEl = document.getElementById('p29-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p29-iv3'); bumpProgress('p29', 25); }
|
||
else if(score >= 4){ addXp(8, 'p29-iv3'); bumpProgress('p29', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p29-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр числовой */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'$B = 0{,}5$ Тл, $I = 2$ А, $L = 0{,}1$ м, $\\alpha = 90°$. Найди $F_A$ (Н).', ans:0.1, tol:0.005, why:'$F_A = 0{,}5 \\cdot 2 \\cdot 0{,}1 \\cdot 1 = 0{,}1$ Н.' },
|
||
{ q:'Те же данные, но $\\alpha = 30°$. Найди $F_A$ (Н).', ans:0.05, tol:0.005, why:'$F_A = 0{,}1 \\cdot \\sin 30° = 0{,}1 \\cdot 0{,}5 = 0{,}05$ Н.' },
|
||
{ q:'При каком угле $\\alpha$ (в °, $0\\le\\alpha\\le 90$) сила Ампера равна нулю?', ans:0, tol:1, why:'$F_A = 0$ при $\\alpha = 0°$ (или $180°$): ток параллелен $\\vec{B}$.' },
|
||
{ q:'$F_A = 0{,}4$ Н, $I = 2$ А, $L = 0{,}2$ м, $\\alpha = 90°$. Найди $B$ (Тл).', ans:1.0, tol:0.05, why:'$B = F/(IL) = 0{,}4/(2\\cdot 0{,}2) = 1$ Тл.' },
|
||
{ q:'Проводник с током «вверх» в поле $\\vec{B}$, направленном в экран. Куда отклонится? Введи: 1 — вправо, 2 — влево, 3 — в экран, 4 — из экрана.', ans:2, tol:0, why:'По правилу левой руки сила направлена влево (ответ 2).' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p29-iv4-q');
|
||
const fb = document.getElementById('p29-iv4-fb');
|
||
const iEl = document.getElementById('p29-iv4-i');
|
||
const sEl = document.getElementById('p29-iv4-s');
|
||
const inp = document.getElementById('p29-iv4-inp');
|
||
const bGo = document.getElementById('p29-iv4-go');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
if(score === Q.length){ addXp(15, 'p29-iv4'); bumpProgress('p29', 25); }
|
||
else if(score >= 3){ addXp(8, 'p29-iv4'); bumpProgress('p29', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
fb.style.display = 'none';
|
||
inp.value = ''; inp.disabled = false; bGo.disabled = false; inp.focus();
|
||
renderMath(qEl);
|
||
}
|
||
function check(){
|
||
if(inp.disabled) return;
|
||
const v = parseFloat(inp.value.replace(',','.'));
|
||
if(!isFinite(v)){ feedback(fb, false, 'Введи число.'); return; }
|
||
const ok = Math.abs(v - Q[i].ans) <= Math.max(Q[i].tol, Math.abs(Q[i].ans)*0.05);
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].ans + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
i++;
|
||
setTimeout(show, 1900);
|
||
}
|
||
bGo.addEventListener('click', check);
|
||
inp.addEventListener('keydown', e => { if(e.key==='Enter') check(); });
|
||
document.getElementById('p29-iv4-restart').addEventListener('click', () => { i = 0; score = 0; inp.disabled=false; bGo.disabled=false; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p29');
|
||
}
|
||
|
||
function build_p30(){
|
||
const box = document.getElementById('p30-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Сила Лоренца */
|
||
html += makeCard('theory', "Сила Лоренца", "§30", `
|
||
<p><b>Сила Лоренца</b> — сила, действующая на электрический заряд $q$, движущийся со скоростью $\\vec{v}$ в магнитном поле $\\vec{B}$.</p>
|
||
<p style="margin-top:8px"><b>Модуль</b>:</p>
|
||
<p style="text-align:center;margin:8px 0">$$F_L = |q|\\, v\\, B\\, \\sin\\alpha$$</p>
|
||
<p>где $\\alpha$ — угол между $\\vec{v}$ и $\\vec{B}$.</p>
|
||
<p style="margin-top:10px"><b>Направление</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Для <b>положительного</b> заряда — по <b>правилу левой руки</b> (ладонь — $\\vec{B}$ входит, 4 пальца — направление $\\vec{v}$, большой палец — $\\vec{F_L}$).</li>
|
||
<li>Для <b>отрицательного</b> заряда — <b>противоположно</b>.</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Важнейшее свойство.</b> $\\vec{F_L} \\perp \\vec{v}$ всегда, значит <b>работа силы Лоренца равна нулю</b>. Магнитное поле <b>не меняет</b> модуль скорости (и кинетическую энергию) — только <b>направление</b> движения.</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Движение по окружности */
|
||
html += makeCard('rule', "Движение заряда по окружности", "§30", `
|
||
<p>Если заряд $q$ влетает в однородное магнитное поле <b>перпендикулярно</b> $\\vec{B}$ ($\\alpha = 90°$), он движется по <b>окружности</b>.</p>
|
||
<p style="margin-top:8px">Сила Лоренца играет роль <b>центростремительной</b>:</p>
|
||
<p style="text-align:center;margin:8px 0">$$|q|\\,v\\,B = \\dfrac{m v^2}{R}$$</p>
|
||
<p>Отсюда <b>радиус окружности</b>:</p>
|
||
<p style="text-align:center;margin:8px 0">$$R = \\dfrac{m v}{|q|\\,B}$$</p>
|
||
<p style="margin-top:10px"><b>Период обращения</b> (циклотронный период):</p>
|
||
<p style="text-align:center;margin:8px 0">$$T = \\dfrac{2\\pi R}{v} = \\dfrac{2\\pi m}{|q|\\,B}$$</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Удивительный факт.</b> Период $T$ <b>не зависит</b> от скорости $v$! Это используется в <b>циклотронах</b>: частицы ускоряются, но за каждый оборот всё равно проходят за одно и то же время.</p>
|
||
`);
|
||
|
||
/* THEORY 3 — Спираль и применение */
|
||
html += makeCard('example', "Спираль и применение", "§30", `
|
||
<p>Если $\\vec{v}$ направлена под произвольным углом к $\\vec{B}$ (не $0°$ и не $90°$), движение — по <b>винтовой линии</b> (спирали):</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Компонента $v_{\\parallel}$ (вдоль $\\vec{B}$) даёт <b>прямолинейное</b> движение.</li>
|
||
<li>Компонента $v_{\\perp}$ (поперёк $\\vec{B}$) — <b>круговое</b>.</li>
|
||
<li>Сумма — <b>спираль</b>.</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Применение силы Лоренца:</b></p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li><b>Циклотрон, синхротрон</b> — ускорители заряженных частиц (Большой адронный коллайдер).</li>
|
||
<li><b>Масс-спектрометр</b>: ионы одинаковой энергии, но разной массы движутся по окружностям разного радиуса $R \\propto m$ — это позволяет их разделить и измерить массы.</li>
|
||
<li><b>МРТ</b> (магнитно-резонансная томография) использует магнитное поле для управления спинами протонов в тканях.</li>
|
||
<li><b>Полярное сияние</b>: частицы солнечного ветра захватываются геомагнитным полем и движутся по спиралям вдоль линий поля Земли, возбуждая свечение атмосферы у полюсов.</li>
|
||
<li><b>Кинескоп</b> (старые телевизоры): электронный луч отклоняется магнитным полем — строится изображение.</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Связь с §29.</b> Сила Ампера — это сумма сил Лоренца, действующих на все заряды-носители тока внутри проводника.</p>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — 3D-движение заряда */
|
||
html += `<div class="wg" id="p30-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Заряд в магнитном поле: окружность или прямая?</div></div>
|
||
<div class="wg-help">$\\vec{B}$ направлено в экран (×). Выбери знак заряда, скорость и угол вхождения $\\alpha$. Смотри траекторию.</div>
|
||
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:10px;flex-wrap:wrap">
|
||
<button class="btn primary" id="p30-iv1-pos">Заряд $q > 0$</button>
|
||
<button class="btn" id="p30-iv1-neg">Заряд $q < 0$</button>
|
||
</div>
|
||
<div class="sliders">
|
||
<label>$v$: <b id="p30-iv1-vL">5.0</b> усл. <input type="range" id="p30-iv1-v" min="1" max="10" value="5" step="0.1"></label>
|
||
<label>$\\alpha$ (°): <b id="p30-iv1-aL">90</b> <input type="range" id="p30-iv1-a" min="0" max="90" value="90" step="5"></label>
|
||
</div>
|
||
<div style="display:flex;gap:6px;justify-content:center;margin-bottom:8px;flex-wrap:wrap">
|
||
<button class="btn" id="p30-iv1-pause">Пауза</button>
|
||
<button class="btn" id="p30-iv1-reset">Сброс</button>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p30-iv1-svg" viewBox="0 0 480 320" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p30-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.94rem;line-height:1.7;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Калькулятор */
|
||
html += `<div class="wg" id="p30-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор силы Лоренца, $R$ и $T$</div></div>
|
||
<div class="wg-help">Введи параметры. Заряд $q$ — в единицах $e = 1{,}6\\cdot 10^{-19}$ Кл.</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px">
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$q$ (в $e$):<input type="number" id="p30-iv2-q" value="1" step="0.1" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$v$ (м/с):<input type="number" id="p30-iv2-v" value="1e6" step="any" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$B$ (Тл):<input type="number" id="p30-iv2-B" value="0.1" step="0.01" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$\\alpha$ (°):<input type="number" id="p30-iv2-A" value="90" step="1" min="0" max="180" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$m$ (кг):<input type="number" id="p30-iv2-m" value="9.1e-31" step="any" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
</div>
|
||
<div class="actions"><button class="btn primary" id="p30-iv2-calc">Вычислить</button></div>
|
||
<div id="p30-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.96rem;line-height:1.8;text-align:center;min-height:40px"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Влияет ли поле на скорость? */
|
||
html += `<div class="wg" id="p30-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Влияет ли поле на модуль скорости?</div></div>
|
||
<div class="wg-help">6 ситуаций. Магнитное поле не меняет $|\\vec{v}|$, электрическое — меняет.</div>
|
||
<div class="score-display"><span>Задача <b id="p30-iv3-i">1</b> / 6</span><span>Очки: <b id="p30-iv3-s">0</b> / 6</span></div>
|
||
<div id="p30-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p30-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p30-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p30-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр числовой */
|
||
html += `<div class="wg" id="p30-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: сила Лоренца</div></div>
|
||
<div class="wg-help">5 числовых задач. Допуск ±5%. Используй $e = 1{,}6\\cdot 10^{-19}$ Кл.</div>
|
||
<div class="score-display"><span>Задача <b id="p30-iv4-i">1</b> / 5</span><span>Очки: <b id="p30-iv4-s">0</b> / 5</span></div>
|
||
<div id="p30-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p30-iv4-form" style="display:flex;gap:8px;justify-content:center;margin-bottom:8px;flex-wrap:wrap">
|
||
<input type="number" id="p30-iv4-inp" step="any" style="padding:8px 10px;border:1px solid var(--border);border-radius:6px;width:140px;font-size:1rem">
|
||
<button class="btn primary" id="p30-iv4-go">Проверить</button>
|
||
</div>
|
||
<div class="feedback" id="p30-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p30-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav('p29', 'p31');
|
||
html += readButton('p30');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Симуляция движения заряда */
|
||
(function(){
|
||
const svg = document.getElementById('p30-iv1-svg');
|
||
const out = document.getElementById('p30-iv1-out');
|
||
const bPos = document.getElementById('p30-iv1-pos');
|
||
const bNeg = document.getElementById('p30-iv1-neg');
|
||
const bPause = document.getElementById('p30-iv1-pause');
|
||
const bReset = document.getElementById('p30-iv1-reset');
|
||
const vS = document.getElementById('p30-iv1-v');
|
||
const aS = document.getElementById('p30-iv1-a');
|
||
const vL = document.getElementById('p30-iv1-vL');
|
||
const aL = document.getElementById('p30-iv1-aL');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
let sign = +1;
|
||
let paused = false;
|
||
let t0 = performance.now();
|
||
let trail = [];
|
||
|
||
function setSign(s){ sign = s; bPos.classList.toggle('primary', s>0); bNeg.classList.toggle('primary', s<0); reset(); }
|
||
function reset(){ t0 = performance.now(); trail = []; }
|
||
|
||
function draw(){
|
||
const W = 480, H = 320;
|
||
const v = +vS.value, aDeg = +aS.value;
|
||
vL.textContent = v.toFixed(1);
|
||
aL.textContent = aDeg.toFixed(0);
|
||
const alpha = aDeg * Math.PI / 180;
|
||
|
||
const now = performance.now();
|
||
if(!paused){
|
||
// Эта функция всё равно вызывается через rAF, accumulator подразумевается через t0
|
||
}
|
||
const tSec = (now - t0) / 1000;
|
||
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">B в экран (×). q = '+(sign>0?'+':'−')+', α='+aDeg+'°</text>';
|
||
g += PHYS.magneticFieldGrid(50, 50, 380, 230, 8, 5, 'in');
|
||
|
||
// Параметры
|
||
const cx = 240, cy = 165;
|
||
const vPerp = Math.sin(alpha) * v;
|
||
const vPar = Math.cos(alpha) * v;
|
||
// Радиус и угловая скорость (упрощённо, безразмерно)
|
||
const Rsim = Math.max(8, vPerp * 12); // px
|
||
const omega = (vPerp > 0.001) ? (sign * 0.9) : 0; // рад/с (направление: + → против часовой? Для q>0, B в экран — по часовой)
|
||
// Для q>0, B в экран: F=qv×B. Скорость вправо (+x), B=-z (в экран). F=q(x×-z)=q(+y)=вверх. Значит, частица идущая вправо испытывает силу вверх — она поворачивает вверх. Это против часовой стрелки в экранных координатах (y растёт вниз). Проверим: для CCW (против часовой в экране) с угла θ=0 → x=R, y=0 (вправо); скорость в этот момент должна быть вверх (-y_экр). dθ/dt > 0 → x=Rcos(θ),y=-Rsin(θ)? Нет, упрощу — выберу знак.
|
||
// В экранных координатах: для q>0 (B в экран) движение по часовой стрелке. Возьмём знак omega = -sign (по часовой = в экране отрицательный угол).
|
||
const dir_sign = -sign; // для anim
|
||
const ang0 = Math.PI; // начинать слева
|
||
const ang = ang0 + dir_sign * 1.2 * tSec * (vPerp/5);
|
||
// Спираль: смещение по линии B → в нашем 2D представлении сместим центр окружности вдоль некоторого направления (вверх — представим как «вдоль B», условно)
|
||
// Поскольку B в экран — vPar выходит из экрана; в 2D не виден. Покажем как точку, мигающую с прогрессом.
|
||
const px = cx + Rsim * Math.cos(ang);
|
||
const py = cy + Rsim * Math.sin(ang);
|
||
|
||
if(!paused){
|
||
trail.push([px,py]);
|
||
if(trail.length > 200) trail.shift();
|
||
}
|
||
|
||
if(aDeg === 0){
|
||
// Прямолинейное движение (v параллельно B → точка просто стоит/уходит "в экран")
|
||
g += '<text x="240" y="160" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" fill="#64748b">$\\vec{v} \\parallel \\vec{B}$ → сила не действует, движение прямолинейное.</text>'.replace('$\\vec{v} \\parallel \\vec{B}$','v ∥ B');
|
||
g += PHYS.chargeMark(cx, cy, sign, 14, sign>0?'+':'−');
|
||
} else if(aDeg === 90){
|
||
// Окружность
|
||
g += '<circle cx="'+cx+'" cy="'+cy+'" r="'+Rsim.toFixed(1)+'" fill="none" stroke="#7c3aed" stroke-width="1.4" stroke-dasharray="4 3" opacity=".55"/>';
|
||
// Трейл
|
||
if(trail.length >= 2){
|
||
let path = 'M '+trail[0][0].toFixed(1)+' '+trail[0][1].toFixed(1);
|
||
for(let k=1;k<trail.length;k++) path += ' L '+trail[k][0].toFixed(1)+' '+trail[k][1].toFixed(1);
|
||
g += '<path d="'+path+'" stroke="#0891b2" stroke-width="2" fill="none" opacity=".85"/>';
|
||
}
|
||
// Текущая позиция — заряд
|
||
g += PHYS.chargeMark(px, py, sign, 11, sign>0?'+':'−');
|
||
// Вектор скорости (касательная)
|
||
const vAng = ang + dir_sign * Math.PI/2;
|
||
const vEx = px + 28 * Math.cos(vAng);
|
||
const vEy = py + 28 * Math.sin(vAng);
|
||
g += PHYS.drawArrow(px, py, vEx, vEy, '#dc2626', 2, 10);
|
||
g += '<text x="'+(vEx+8)+'" y="'+(vEy-4)+'" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#dc2626">v</text>';
|
||
// Вектор силы (центростремительная — к центру)
|
||
const fEx = px + 28 * Math.cos(ang + Math.PI);
|
||
const fEy = py + 28 * Math.sin(ang + Math.PI);
|
||
g += PHYS.drawArrow(px, py, fEx, fEy, '#0891b2', 2, 10);
|
||
g += '<text x="'+(fEx-8)+'" y="'+(fEy+12)+'" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="#0891b2">F</text>';
|
||
} else {
|
||
// Спираль — рисуем как смещённую окружность
|
||
g += '<circle cx="'+cx+'" cy="'+cy+'" r="'+Rsim.toFixed(1)+'" fill="none" stroke="#7c3aed" stroke-width="1.2" stroke-dasharray="3 3" opacity=".4"/>';
|
||
if(trail.length >= 2){
|
||
let path = 'M '+trail[0][0].toFixed(1)+' '+trail[0][1].toFixed(1);
|
||
for(let k=1;k<trail.length;k++) path += ' L '+trail[k][0].toFixed(1)+' '+trail[k][1].toFixed(1);
|
||
g += '<path d="'+path+'" stroke="#0891b2" stroke-width="2" fill="none" opacity=".8"/>';
|
||
}
|
||
g += PHYS.chargeMark(px, py, sign, 11, sign>0?'+':'−');
|
||
g += '<text x="240" y="305" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#64748b">Спираль (винтовая линия): $v_{\\perp}$ → круг, $v_{\\parallel}$ → вдоль B</text>'.replace('$v_{\\perp}$','v⊥').replace('$v_{\\parallel}$','v∥');
|
||
}
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let txt = '';
|
||
if(aDeg === 0){
|
||
txt = '<b>$\\alpha = 0°$:</b> $\\vec{v} \\parallel \\vec{B}$, $F_L = 0$. Прямолинейное движение, поле не действует.';
|
||
} else if(aDeg === 90){
|
||
txt = '<b>$\\alpha = 90°$:</b> чистая окружность. $R = mv/(|q|B)$, $T = 2\\pi m/(|q|B)$ (не зависит от $v$).';
|
||
} else {
|
||
txt = '<b>$\\alpha = '+aDeg+'°$:</b> винтовая линия. $v_{\\parallel}$ вдоль $\\vec{B}$ + $v_{\\perp}$ по окружности.';
|
||
}
|
||
out.innerHTML = txt;
|
||
renderMath(out);
|
||
|
||
seen.add(sign+':'+aDeg);
|
||
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p30-iv1'); bumpProgress('p30', 15); }
|
||
}
|
||
|
||
function loop(){
|
||
if(!paused) draw();
|
||
requestAnimationFrame(loop);
|
||
}
|
||
|
||
bPos.addEventListener('click', () => setSign(+1));
|
||
bNeg.addEventListener('click', () => setSign(-1));
|
||
bPause.addEventListener('click', () => { paused = !paused; bPause.textContent = paused ? 'Продолжить' : 'Пауза'; });
|
||
bReset.addEventListener('click', reset);
|
||
vS.addEventListener('input', () => { reset(); draw(); });
|
||
aS.addEventListener('input', () => { reset(); draw(); });
|
||
setSign(+1);
|
||
draw();
|
||
requestAnimationFrame(loop);
|
||
})();
|
||
|
||
/* IV2 — Калькулятор */
|
||
(function(){
|
||
const out = document.getElementById('p30-iv2-out');
|
||
const bGo = document.getElementById('p30-iv2-calc');
|
||
const inQ = document.getElementById('p30-iv2-q');
|
||
const inV = document.getElementById('p30-iv2-v');
|
||
const inB = document.getElementById('p30-iv2-B');
|
||
const inA = document.getElementById('p30-iv2-A');
|
||
const inM = document.getElementById('p30-iv2-m');
|
||
let count = 0, _done = false;
|
||
|
||
function calc(){
|
||
const qe = +inQ.value, v = +inV.value, B = +inB.value, aDeg = +inA.value, m = +inM.value;
|
||
if(![qe,v,B,aDeg,m].every(isFinite)){ out.innerHTML = '<b style="color:#dc2626">Введи все значения.</b>'; return; }
|
||
const e = PHYS.CONST.e;
|
||
const q = qe * e;
|
||
const rad = aDeg * Math.PI / 180;
|
||
const F = Math.abs(q) * v * B * Math.sin(rad);
|
||
let html = '$F_L = |q|\\,v\\,B\\,\\sin\\alpha = '+F.toExponential(3)+'$ <b>Н</b>';
|
||
if(Math.abs(aDeg - 90) < 1e-6 && Math.abs(q) > 0 && B > 0){
|
||
const R = m * v / (Math.abs(q) * B);
|
||
const T = 2 * Math.PI * m / (Math.abs(q) * B);
|
||
html += '<br>При $\\alpha = 90°$: $R = mv/(|q|B) = '+R.toExponential(3)+'$ м, $T = 2\\pi m/(|q|B) = '+T.toExponential(3)+'$ с.';
|
||
}
|
||
out.innerHTML = html;
|
||
renderMath(out);
|
||
count++;
|
||
if(!_done && count >= 3){ _done = true; addXp(10, 'p30-iv2'); bumpProgress('p30', 15); }
|
||
}
|
||
bGo.addEventListener('click', calc);
|
||
[inQ,inV,inB,inA,inM].forEach(x => x.addEventListener('keydown', e => { if(e.key==='Enter') calc(); }));
|
||
})();
|
||
|
||
/* IV3 — Влияет ли поле на скорость? */
|
||
(function(){
|
||
const OPTS = ['Меняет модуль скорости','Не меняет (только направление, или сила = 0)'];
|
||
const Q = [
|
||
{ q:'Электрон, влетевший в однородное магнитное поле перпендикулярно $\\vec{B}$.', ans:1, why:'Сила Лоренца $\\perp \\vec{v}$, работа = 0, $|\\vec{v}|$ постоянен.' },
|
||
{ q:'Электрон в однородном электрическом поле $\\vec{E}$.', ans:0, why:'Электрическое поле совершает работу, $|\\vec{v}|$ меняется.' },
|
||
{ q:'Протон движется параллельно $\\vec{B}$.', ans:1, why:'$\\alpha = 0$ → $F_L = 0$. Прямолинейное равномерное движение.' },
|
||
{ q:'Нейтрон (заряд = 0) в магнитном поле.', ans:1, why:'$q = 0$ → $F_L = 0$. Поле никак не действует.' },
|
||
{ q:'Положительный заряд в однородном поле $\\vec{B}$, $\\vec{v} \\perp \\vec{B}$.', ans:1, why:'Движение по окружности, $|\\vec{v}|$ постоянен — только направление меняется.' },
|
||
{ q:'Электрон в комбинированном поле $\\vec{E} + \\vec{B}$.', ans:0, why:'Из-за $\\vec{E}$ работа $\\ne 0$, поэтому $|\\vec{v}|$ меняется.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p30-iv3-q');
|
||
const oEl = document.getElementById('p30-iv3-opts');
|
||
const fb = document.getElementById('p30-iv3-fb');
|
||
const iEl = document.getElementById('p30-iv3-i');
|
||
const sEl = document.getElementById('p30-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p30-iv3'); bumpProgress('p30', 25); }
|
||
else if(score >= 4){ addXp(8, 'p30-iv3'); bumpProgress('p30', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p30-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр числовой */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'$q = e$, $v = 10^6$ м/с, $B = 0{,}1$ Тл, $\\alpha = 90°$. Найди $F_L$. Введи мантиссу при $10^{-14}$ Н.', ans:1.6, tol:0.1, why:'$F_L = 1{,}6\\cdot 10^{-19}\\cdot 10^6\\cdot 0{,}1 = 1{,}6\\cdot 10^{-14}$ Н.' },
|
||
{ q:'Электрон ($m = 9{,}1\\cdot 10^{-31}$ кг) в $B = 10^{-3}$ Тл, $v = 10^7$ м/с, $\\alpha = 90°$. Найди $R$ (см).', ans:5.7, tol:0.5, why:'$R = mv/(|q|B) = 9{,}1\\cdot 10^{-31}\\cdot 10^7 / (1{,}6\\cdot 10^{-19}\\cdot 10^{-3}) \\approx 5{,}7\\cdot 10^{-2}$ м = 5,7 см.' },
|
||
{ q:'При $\\alpha = 0°$ сила Лоренца равна (в Н)?', ans:0, tol:1e-30, why:'$F_L = qvB\\sin 0° = 0$. Сила не действует.' },
|
||
{ q:'Работа силы Лоренца за период обращения частицы по окружности (в Дж)?', ans:0, tol:1e-30, why:'$\\vec{F_L} \\perp \\vec{v}$, поэтому $A = 0$ всегда.' },
|
||
{ q:'Если скорость заряда удвоить (при $\\alpha=90°$), во сколько раз изменится радиус $R$?', ans:2, tol:0.1, why:'$R = mv/(|q|B) \\propto v$ → удваивается. Период $T$ не меняется.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p30-iv4-q');
|
||
const fb = document.getElementById('p30-iv4-fb');
|
||
const iEl = document.getElementById('p30-iv4-i');
|
||
const sEl = document.getElementById('p30-iv4-s');
|
||
const inp = document.getElementById('p30-iv4-inp');
|
||
const bGo = document.getElementById('p30-iv4-go');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
if(score === Q.length){ addXp(15, 'p30-iv4'); bumpProgress('p30', 25); }
|
||
else if(score >= 3){ addXp(8, 'p30-iv4'); bumpProgress('p30', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
fb.style.display = 'none';
|
||
inp.value = ''; inp.disabled = false; bGo.disabled = false; inp.focus();
|
||
renderMath(qEl);
|
||
}
|
||
function check(){
|
||
if(inp.disabled) return;
|
||
const v = parseFloat(inp.value.replace(',','.'));
|
||
if(!isFinite(v)){ feedback(fb, false, 'Введи число.'); return; }
|
||
const tol = Math.max(Q[i].tol, Math.abs(Q[i].ans)*0.05);
|
||
const ok = Math.abs(v - Q[i].ans) <= tol;
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].ans + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
i++;
|
||
setTimeout(show, 1900);
|
||
}
|
||
bGo.addEventListener('click', check);
|
||
inp.addEventListener('keydown', e => { if(e.key==='Enter') check(); });
|
||
document.getElementById('p30-iv4-restart').addEventListener('click', () => { i = 0; score = 0; inp.disabled=false; bGo.disabled=false; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p30');
|
||
}
|
||
|
||
function build_p31(){
|
||
const box = document.getElementById('p31-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Магнитный поток */
|
||
html += makeCard('theory', "Магнитный поток", "§31", `
|
||
<p><b>Магнитный поток</b> $\\Phi$ через плоскую площадку площадью $S$ в <b>однородном</b> магнитном поле $\\vec{B}$:</p>
|
||
<p style="text-align:center;margin:8px 0">$$\\Phi = B\\,S\\,\\cos\\alpha$$</p>
|
||
<p>где $\\alpha$ — угол между $\\vec{B}$ и <b>нормалью</b> $\\vec{n}$ к площадке.</p>
|
||
<p style="margin-top:10px"><b>Единица</b>: <b>Вебер</b> (Вб) = Тл·м².</p>
|
||
<p style="margin-top:10px"><b>Особые случаи</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>$\\alpha = 0°$ ($\\vec{B} \\parallel \\vec{n}$): $\\Phi_{max} = BS$.</li>
|
||
<li>$\\alpha = 90°$ ($\\vec{B} \\perp \\vec{n}$, поле параллельно площадке): $\\Phi = 0$.</li>
|
||
<li>$\\alpha = 180°$: $\\Phi = -BS$ (отрицательный поток).</li>
|
||
</ul>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Наглядно.</b> Можно представлять $\\Phi$ как «количество силовых линий, пронизывающих площадку».</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Явление электромагнитной индукции */
|
||
html += makeCard('rule', "Явление электромагнитной индукции", "§31", `
|
||
<p><b>Электромагнитная индукция</b> — возникновение электрического тока в замкнутом проводящем контуре при <b>изменении</b> магнитного потока через него.</p>
|
||
<p style="margin-top:8px">Открыта <b>Майклом Фарадеем</b> в 1831 году.</p>
|
||
<p style="margin-top:10px"><b>Главный принцип</b>: ток в контуре возникает <b>только</b> при изменении $\\Phi$, а не от самого $\\Phi$. Если магнит неподвижен в катушке — тока нет.</p>
|
||
<p style="margin-top:10px">Возникший ток называется <b>индукционным</b>. ЭДС, которая его вызывает — <b>ЭДС индукции</b> $\\mathcal{E}_i$.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Ключ.</b> Чем <b>быстрее</b> меняется $\\Phi$ — тем <b>больше</b> индукционный ток.</p>
|
||
`);
|
||
|
||
/* THEORY 3 — Опыты Фарадея и способы изменения Ф */
|
||
html += makeCard('example', "Опыты Фарадея и способы изменения Ф", "§31", `
|
||
<p><b>Классические опыты Фарадея</b>:</p>
|
||
<ol style="margin:6px 0 6px 22px">
|
||
<li><b>Магнит вдвигают в катушку</b> → возникает индукционный ток (одного знака).</li>
|
||
<li><b>Магнит выдвигают</b> → ток противоположного знака.</li>
|
||
<li><b>Магнит ближе к катушке = больше скорость изменения $\\Phi$</b> → больше ток.</li>
|
||
<li><b>Без движения</b> — тока нет.</li>
|
||
</ol>
|
||
<p style="margin-top:10px"><b>Способы изменения $\\Phi$</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li><b>Изменение $B$</b>: движение магнита, изменение тока в соседней катушке.</li>
|
||
<li><b>Изменение $S$</b>: деформация контура, перемещение проводника-перемычки.</li>
|
||
<li><b>Изменение $\\alpha$</b>: поворот контура в поле (основа работы генератора).</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Применение</b>:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li><b>Генераторы электрического тока</b> (электростанции).</li>
|
||
<li><b>Трансформаторы</b>.</li>
|
||
<li><b>Индукционные плиты</b>.</li>
|
||
<li><b>Беспроводная зарядка</b> смартфонов.</li>
|
||
<li><b>Микрофоны</b> и звукосниматели.</li>
|
||
</ul>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — Магнитный поток через рамку */
|
||
html += `<div class="wg" id="p31-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Магнитный поток через рамку</div></div>
|
||
<div class="wg-help">Меняй $B$, $S$ и угол $\\alpha$ между $\\vec{B}$ и нормалью $\\vec{n}$ к рамке. Смотри, как меняется поток $\\Phi = BS\\cos\\alpha$.</div>
|
||
<div class="sliders">
|
||
<label>$B$ (Тл): <b id="p31-iv1-BL">1.0</b> <input type="range" id="p31-iv1-B" min="0" max="2" value="1" step="0.05"></label>
|
||
<label>$S$ (м²): <b id="p31-iv1-SL">0.20</b> <input type="range" id="p31-iv1-S" min="0.01" max="1" value="0.2" step="0.01"></label>
|
||
<label>$\\alpha$ (°): <b id="p31-iv1-aL">30</b> <input type="range" id="p31-iv1-a" min="0" max="180" value="30" step="5"></label>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p31-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p31-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.96rem;line-height:1.7;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Опыт Фарадея */
|
||
html += `<div class="wg" id="p31-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Симуляция опыта Фарадея</div></div>
|
||
<div class="wg-help">Двигай магнит к катушке и от неё. Когда поток меняется — в катушке возникает индукционный ток. Когда магнит неподвижен — тока нет.</div>
|
||
<div class="sliders">
|
||
<label>Положение магнита: <b id="p31-iv2-xL">−0.20</b> <input type="range" id="p31-iv2-x" min="-0.50" max="0.30" value="-0.20" step="0.01"></label>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p31-iv2-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p31-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.96rem;line-height:1.7;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Возникает ли индукционный ток? */
|
||
html += `<div class="wg" id="p31-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возникает ли индукционный ток?</div></div>
|
||
<div class="wg-help">6 ситуаций. Ток возникает только если меняется магнитный поток $\\Phi$ через контур.</div>
|
||
<div class="score-display"><span>Задача <b id="p31-iv3-i">1</b> / 6</span><span>Очки: <b id="p31-iv3-s">0</b> / 6</span></div>
|
||
<div id="p31-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p31-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p31-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p31-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр */
|
||
html += `<div class="wg" id="p31-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр магнитного потока</div></div>
|
||
<div class="wg-help">5 числовых задач. Допуск ±5%. Используй $\\Phi = BS\\cos\\alpha$.</div>
|
||
<div class="score-display"><span>Задача <b id="p31-iv4-i">1</b> / 5</span><span>Очки: <b id="p31-iv4-s">0</b> / 5</span></div>
|
||
<div id="p31-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p31-iv4-form" style="display:flex;gap:8px;justify-content:center;margin-bottom:8px;flex-wrap:wrap">
|
||
<input type="number" id="p31-iv4-inp" step="any" style="padding:8px 10px;border:1px solid var(--border);border-radius:6px;width:140px;font-size:1rem">
|
||
<button class="btn primary" id="p31-iv4-go">Проверить</button>
|
||
</div>
|
||
<div class="feedback" id="p31-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p31-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav('p30', 'p32');
|
||
html += readButton('p31');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Магнитный поток через рамку */
|
||
(function(){
|
||
const svg = document.getElementById('p31-iv1-svg');
|
||
const out = document.getElementById('p31-iv1-out');
|
||
const bS = document.getElementById('p31-iv1-B');
|
||
const sS = document.getElementById('p31-iv1-S');
|
||
const aS = document.getElementById('p31-iv1-a');
|
||
const bL = document.getElementById('p31-iv1-BL');
|
||
const sL = document.getElementById('p31-iv1-SL');
|
||
const aL = document.getElementById('p31-iv1-aL');
|
||
const seen = new Set();
|
||
let _done = false;
|
||
|
||
function draw(){
|
||
const W = 480, H = 280;
|
||
const B = +bS.value, S = +sS.value, aDeg = +aS.value;
|
||
bL.textContent = B.toFixed(2);
|
||
sL.textContent = S.toFixed(2);
|
||
aL.textContent = aDeg.toFixed(0);
|
||
const alpha = aDeg * Math.PI / 180;
|
||
const Phi = B * S * Math.cos(alpha);
|
||
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">B вправо →. Угол между B и нормалью n = '+aDeg+'°</text>';
|
||
|
||
// Силовые линии поля B (горизонтальные стрелки слева → направо)
|
||
for(let i=0;i<5;i++){
|
||
const yL = 60 + i*40;
|
||
g += PHYS.drawArrow(40, yL, 440, yL, '#7c3aed', 1.3, 8);
|
||
}
|
||
// подпись B
|
||
g += '<text x="450" y="148" font-family="JetBrains Mono,monospace" font-size="14" font-weight="700" fill="#7c3aed">B</text>';
|
||
|
||
// Рамка (вид сверху, поворот вокруг вертикальной оси на угол alpha)
|
||
// Ширина проекции рамки = w0 * |sin(alpha)| — это «как мы видим её сбоку». Но удобнее
|
||
// показывать рамку с нормалью под углом alpha к B. Изобразим рамку как ромб (3D-проекция).
|
||
const cx = 240, cy = 150;
|
||
const halfW = 60; // полуширина рамки в плоскости поля
|
||
const halfH = 50; // высота рамки (не меняется)
|
||
// При alpha=0 рамка видна как горизонтальная линия (поток max); при alpha=90 рамка перпенд. → видна как прямоугольник
|
||
const projW = Math.max(2, halfW * Math.abs(Math.sin(alpha)));
|
||
// верхний и нижний край рамки — горизонтальные линии длиной 2*projW
|
||
// боковые стороны — наклонные (для эффекта 3D)
|
||
const xL = cx - projW, xR = cx + projW;
|
||
// Заполнение рамки
|
||
g += '<polygon points="'+xL.toFixed(1)+','+(cy-halfH).toFixed(1)+' '+xR.toFixed(1)+','+(cy-halfH).toFixed(1)+' '+xR.toFixed(1)+','+(cy+halfH).toFixed(1)+' '+xL.toFixed(1)+','+(cy+halfH).toFixed(1)+'" fill="rgba(8,145,178,0.13)" stroke="#0891b2" stroke-width="2.2"/>';
|
||
// Нормаль n
|
||
const nLen = 60;
|
||
// Направление нормали — повёрнуто на alpha от B (B вдоль +x). Тогда n = (cos(alpha), sin(alpha)).
|
||
const nx = cx + nLen * Math.cos(alpha);
|
||
const ny = cy - nLen * Math.sin(alpha);
|
||
g += PHYS.drawArrow(cx, cy, nx, ny, '#dc2626', 2.4, 11);
|
||
g += '<text x="'+(nx+8)+'" y="'+(ny-4)+'" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#dc2626">n</text>';
|
||
// Дуга угла между n и B (B = +x)
|
||
const arcR = 28;
|
||
const a1 = 0, a2 = -alpha; // угол в SVG-координатах (y вниз)
|
||
const aStart = Math.min(a1, a2), aEnd = Math.max(a1, a2);
|
||
g += '<path d="M '+(cx+arcR)+' '+cy+' A '+arcR+' '+arcR+' 0 0 0 '+(cx+arcR*Math.cos(a2)).toFixed(1)+' '+(cy+arcR*Math.sin(a2)).toFixed(1)+'" fill="none" stroke="#0f172a" stroke-width="1.4"/>';
|
||
g += '<text x="'+(cx+arcR+8)+'" y="'+(cy - 4).toFixed(1)+'" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">α</text>';
|
||
|
||
// Подпись параметров рамки
|
||
g += '<text x="240" y="248" text-anchor="middle" font-family="Inter,sans-serif" font-size="12" fill="#0f172a">B = '+B.toFixed(2)+' Тл, S = '+S.toFixed(2)+' м², α = '+aDeg+'°</text>';
|
||
g += '<text x="240" y="266" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="14" font-weight="700" fill="#0891b2">Φ = '+(Phi*1000).toFixed(2)+' мВб</text>';
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let note = '';
|
||
if(aDeg === 0) note = '<b>α = 0°:</b> $\\vec{B} \\parallel \\vec{n}$ — поток максимален: $\\Phi_{max} = BS = '+(B*S*1000).toFixed(2)+'$ мВб.';
|
||
else if(aDeg === 90) note = '<b>α = 90°:</b> $\\vec{B} \\perp \\vec{n}$ — линии поля скользят вдоль рамки. $\\Phi = 0$.';
|
||
else if(aDeg === 180) note = '<b>α = 180°:</b> $\\vec{B}$ и $\\vec{n}$ направлены противоположно. $\\Phi = -BS = '+(-B*S*1000).toFixed(2)+'$ мВб.';
|
||
else note = '<b>α = '+aDeg+'°:</b> $\\Phi = BS\\cos\\alpha = '+B.toFixed(2)+'\\cdot '+S.toFixed(2)+'\\cdot \\cos '+aDeg+'° = '+(Phi*1000).toFixed(2)+'$ мВб.';
|
||
out.innerHTML = note;
|
||
renderMath(out);
|
||
|
||
const key = (B>0.5?'B+':'B-') + ':' + (S>0.3?'S+':'S-') + ':' + Math.round(aDeg/30);
|
||
seen.add(key);
|
||
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p31-iv1'); bumpProgress('p31', 15); }
|
||
}
|
||
bS.addEventListener('input', draw);
|
||
sS.addEventListener('input', draw);
|
||
aS.addEventListener('input', draw);
|
||
draw();
|
||
})();
|
||
|
||
/* IV2 — Опыт Фарадея */
|
||
(function(){
|
||
const svg = document.getElementById('p31-iv2-svg');
|
||
const out = document.getElementById('p31-iv2-out');
|
||
const xS = document.getElementById('p31-iv2-x');
|
||
const xL = document.getElementById('p31-iv2-xL');
|
||
let lastX = +xS.value, lastT = performance.now();
|
||
let movesPos = 0, movesNeg = 0, _done = false;
|
||
|
||
function draw(){
|
||
const W = 480, H = 280;
|
||
const x = +xS.value;
|
||
xL.textContent = x.toFixed(2);
|
||
const now = performance.now();
|
||
const dt = Math.max(0.01, (now - lastT) / 1000);
|
||
const dx = x - lastX;
|
||
const vel = dx / dt; // м/с (условно)
|
||
lastX = x; lastT = now;
|
||
|
||
// Катушка справа: центр около (340, 140), длина 130
|
||
const coilCx = 340, coilCy = 140, coilL = 130, coilR = 40;
|
||
// Магнит — прямоугольник; позиция: магнит-середина по x
|
||
// Сопоставим x ∈ [-0.5 .. 0.3] м со SVG-координатой от 70 (далеко слева) до 320 (входит в катушку)
|
||
const magW = 90, magH = 40;
|
||
const magCx = 70 + (x + 0.5) * (320 - 70) / 0.8;
|
||
const magCy = coilCy;
|
||
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Опыт Фарадея: магнит и катушка с амперметром</text>';
|
||
|
||
// Катушка — серия дуг (имитация витков)
|
||
const turns = 10;
|
||
for(let i=0;i<turns;i++){
|
||
const cx = coilCx - coilL/2 + i * (coilL / (turns - 1));
|
||
g += '<ellipse cx="'+cx.toFixed(1)+'" cy="'+coilCy+'" rx="14" ry="'+coilR+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
|
||
}
|
||
// Боковые «проводки» катушки к амперметру
|
||
g += PHYS.wire(coilCx - coilL/2 - 4, coilCy + coilR, coilCx - coilL/2 - 4, coilCy + coilR + 30);
|
||
g += PHYS.wire(coilCx + coilL/2 + 4, coilCy + coilR, coilCx + coilL/2 + 4, coilCy + coilR + 30);
|
||
g += PHYS.wire(coilCx - coilL/2 - 4, coilCy + coilR + 30, 405, coilCy + coilR + 30);
|
||
g += PHYS.wire(coilCx + coilL/2 + 4, coilCy + coilR + 30, 435, coilCy + coilR + 30);
|
||
// Амперметр
|
||
g += PHYS.ammeterSymbol(420, coilCy + coilR + 30, 14);
|
||
|
||
// Магнит: N (красный) | S (синий)
|
||
g += '<rect x="'+(magCx - magW/2).toFixed(1)+'" y="'+(magCy - magH/2).toFixed(1)+'" width="'+(magW/2).toFixed(1)+'" height="'+magH+'" fill="#fecaca" stroke="#dc2626" stroke-width="1.8"/>';
|
||
g += '<rect x="'+magCx.toFixed(1)+'" y="'+(magCy - magH/2).toFixed(1)+'" width="'+(magW/2).toFixed(1)+'" height="'+magH+'" fill="#bfdbfe" stroke="#2563eb" stroke-width="1.8"/>';
|
||
g += '<text x="'+(magCx - magW/4).toFixed(1)+'" y="'+(magCy + 5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="15" font-weight="800" fill="#dc2626">N</text>';
|
||
g += '<text x="'+(magCx + magW/4).toFixed(1)+'" y="'+(magCy + 5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="15" font-weight="800" fill="#2563eb">S</text>';
|
||
|
||
// Поле магнита (стрелка из N) — направление движения магнита
|
||
if(Math.abs(vel) > 0.02){
|
||
const dir = vel > 0 ? 1 : -1;
|
||
const arrFromX = magCx + dir * (magW/2 + 6);
|
||
const arrToX = arrFromX + dir * 40;
|
||
g += PHYS.drawArrow(arrFromX, magCy - 30, arrToX, magCy - 30, '#0f172a', 2, 9);
|
||
g += '<text x="'+((arrFromX+arrToX)/2).toFixed(1)+'" y="'+(magCy - 36)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">движение</text>';
|
||
}
|
||
|
||
// Стрелка амперметра + ток в катушке
|
||
let curSign = 0; // -1, 0, +1
|
||
let curMag = 0;
|
||
if(Math.abs(vel) > 0.02){
|
||
// приближение N-полюса справа → поток через катушку растёт. Индукционный ток создаёт поле S к магниту,
|
||
// то есть ток виден против часовой стрелки (со стороны магнита). Возьмём знак произвольно.
|
||
curSign = vel > 0 ? +1 : -1;
|
||
curMag = Math.min(1, Math.abs(vel) * 8);
|
||
}
|
||
// Стрелка на амперметре
|
||
const ndlAng = curSign * curMag * 0.7; // рад от вертикали
|
||
const ax = 420, ay = coilCy + coilR + 30;
|
||
const ndlEx = ax + 11 * Math.sin(ndlAng);
|
||
const ndlEy = ay - 11 * Math.cos(ndlAng);
|
||
g += '<line x1="'+ax+'" y1="'+ay+'" x2="'+ndlEx.toFixed(1)+'" y2="'+ndlEy.toFixed(1)+'" stroke="#0f172a" stroke-width="2"/>';
|
||
|
||
// Стрелочки тока в первом витке катушки
|
||
if(curMag > 0){
|
||
const dirColor = curSign > 0 ? '#dc2626' : '#0891b2';
|
||
const ccx = coilCx, ccy = coilCy;
|
||
const r = coilR + 4;
|
||
// Верх витка — стрелка вправо/влево
|
||
const dxArr = curSign > 0 ? +14 : -14;
|
||
g += PHYS.drawArrow(ccx - dxArr/2, ccy - r, ccx + dxArr/2, ccy - r, dirColor, 2, 8);
|
||
g += PHYS.drawArrow(ccx + dxArr/2, ccy + r, ccx - dxArr/2, ccy + r, dirColor, 2, 8);
|
||
g += '<text x="240" y="266" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="'+dirColor+'">Индукционный ток '+(curSign>0?'→':'←')+'</text>';
|
||
} else {
|
||
g += '<text x="240" y="266" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#64748b">Тока нет — магнит покоится</text>';
|
||
}
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let note = '';
|
||
if(curMag < 0.05) note = '<b>Магнит покоится.</b> Поток $\\Phi$ через катушку не меняется → индукционного тока <b>нет</b>.';
|
||
else if(curSign > 0) note = '<b>Магнит приближается</b> к катушке. Поток $\\Phi$ растёт → возникает индукционный ток (одного знака).';
|
||
else note = '<b>Магнит удаляется</b> от катушки. Поток $\\Phi$ убывает → возникает индукционный ток <b>противоположного</b> знака.';
|
||
out.innerHTML = note;
|
||
renderMath(out);
|
||
|
||
if(curSign > 0) movesPos = 1;
|
||
if(curSign < 0) movesNeg = 1;
|
||
if(!_done && movesPos && movesNeg){ _done = true; addXp(10, 'p31-iv2'); bumpProgress('p31', 15); }
|
||
}
|
||
xS.addEventListener('input', draw);
|
||
// Тикать чаще, чтобы дать стрелке амперметра «остыть»
|
||
setInterval(draw, 250);
|
||
draw();
|
||
})();
|
||
|
||
/* IV3 — Возникает ли индукционный ток? */
|
||
(function(){
|
||
const OPTS = ['Возникает', 'Не возникает'];
|
||
const Q = [
|
||
{ q:'Магнит вдвигают в катушку.', ans:0, why:'$\\Phi$ через катушку меняется → ток есть.' },
|
||
{ q:'Магнит покоится в катушке.', ans:1, why:'$\\Phi$ не меняется → тока нет.' },
|
||
{ q:'Катушка вращается в постоянном магнитном поле.', ans:0, why:'Меняется угол $\\alpha$ → меняется $\\Phi$ → ток есть. (Принцип генератора.)' },
|
||
{ q:'Ток в соседней катушке плавно растёт.', ans:0, why:'$B$ растёт → $\\Phi$ меняется → ток в нашей катушке есть.' },
|
||
{ q:'В соседней катушке течёт постоянный ток.', ans:1, why:'$B$ постоянно → $\\Phi$ не меняется → тока нет.' },
|
||
{ q:'Площадь контура в постоянном поле плавно уменьшается.', ans:0, why:'$S$ меняется → $\\Phi = BS\\cos\\alpha$ меняется → ток есть.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p31-iv3-q');
|
||
const oEl = document.getElementById('p31-iv3-opts');
|
||
const fb = document.getElementById('p31-iv3-fb');
|
||
const iEl = document.getElementById('p31-iv3-i');
|
||
const sEl = document.getElementById('p31-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p31-iv3'); bumpProgress('p31', 25); }
|
||
else if(score >= 4){ addXp(8, 'p31-iv3'); bumpProgress('p31', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1800);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p31-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'$B = 0{,}5$ Тл, $S = 0{,}04$ м², $\\alpha = 0°$. Найди $\\Phi$ (в <b>мВб</b>).', ans:20, tol:1, why:'$\\Phi = BS\\cos 0° = 0{,}5\\cdot 0{,}04 = 0{,}02$ Вб = 20 мВб.' },
|
||
{ q:'То же поле и площадь, но $\\alpha = 60°$. Найди $\\Phi$ (в <b>мВб</b>).', ans:10, tol:0.6, why:'$\\Phi = 20\\cdot \\cos 60° = 20\\cdot 0{,}5 = 10$ мВб.' },
|
||
{ q:'$\\alpha = 90°$. Чему равен $\\Phi$ (в мВб)?', ans:0, tol:0.01, why:'$\\cos 90° = 0$ → $\\Phi = 0$. Линии поля скользят вдоль рамки.' },
|
||
{ q:'При $B = 0$ Тл — каким будет $\\Phi$ (в Вб)?', ans:0, tol:0.01, why:'Нет поля → нет потока. $\\Phi = 0$.' },
|
||
{ q:'Если площадь $S$ удвоить (при тех же $B$ и $\\alpha$), во сколько раз изменится $\\Phi$?', ans:2, tol:0.1, why:'$\\Phi \\propto S$ → удвоится.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p31-iv4-q');
|
||
const fb = document.getElementById('p31-iv4-fb');
|
||
const iEl = document.getElementById('p31-iv4-i');
|
||
const sEl = document.getElementById('p31-iv4-s');
|
||
const inp = document.getElementById('p31-iv4-inp');
|
||
const bGo = document.getElementById('p31-iv4-go');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
if(score === Q.length){ addXp(15, 'p31-iv4'); bumpProgress('p31', 25); }
|
||
else if(score >= 3){ addXp(8, 'p31-iv4'); bumpProgress('p31', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
fb.style.display = 'none';
|
||
inp.value = ''; inp.disabled = false; bGo.disabled = false; inp.focus();
|
||
renderMath(qEl);
|
||
}
|
||
function check(){
|
||
if(inp.disabled) return;
|
||
const v = parseFloat(inp.value.replace(',','.'));
|
||
if(!isFinite(v)){ feedback(fb, false, 'Введи число.'); return; }
|
||
const tol = Math.max(Q[i].tol, Math.abs(Q[i].ans)*0.05);
|
||
const ok = Math.abs(v - Q[i].ans) <= tol;
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].ans + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
i++;
|
||
setTimeout(show, 1900);
|
||
}
|
||
bGo.addEventListener('click', check);
|
||
inp.addEventListener('keydown', e => { if(e.key==='Enter') check(); });
|
||
document.getElementById('p31-iv4-restart').addEventListener('click', () => { i = 0; score = 0; inp.disabled=false; bGo.disabled=false; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p31');
|
||
}
|
||
|
||
function build_p32(){
|
||
const box = document.getElementById('p32-body');
|
||
let html = '';
|
||
|
||
/* THEORY 1 — Правило Ленца */
|
||
html += makeCard('theory', "Правило Ленца", "§32", `
|
||
<p><b>Правило Ленца</b> (1834): <b>индукционный ток направлен так, чтобы своим магнитным полем препятствовать изменению магнитного потока, его вызвавшему</b>.</p>
|
||
<p style="margin-top:10px">Это правило отражает <b>закон сохранения энергии</b>: если бы ток усиливал изменение, можно было бы получать неограниченную энергию из ничего.</p>
|
||
<p style="margin-top:10px"><b>Пример 1</b>: магнит N-полюсом <b>приближается</b> к катушке.</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>Поток $\\Phi$ через катушку растёт.</li>
|
||
<li>Индукционный ток создаёт <b>встречное</b> магнитное поле (его «N-полюс» обращён к магниту).</li>
|
||
<li>Магнит <b>отталкивается</b> — приходится приложить силу, чтобы продолжать движение.</li>
|
||
<li>Эта работа преобразуется в энергию индукционного тока.</li>
|
||
</ul>
|
||
<p style="margin-top:10px"><b>Пример 2</b>: магнит <b>удаляется</b>. Поток убывает. Индукционный ток создаёт поле, <b>поддерживающее</b> убывающий поток — катушка <b>притягивает</b> магнит.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Короткая мнемоника.</b> «Природа сопротивляется изменению»: ток всегда <b>против</b> того, кто его вызвал.</p>
|
||
`);
|
||
|
||
/* THEORY 2 — Закон Фарадея */
|
||
html += makeCard('rule', "Закон электромагнитной индукции (Фарадея)", "§32", `
|
||
<p>Величина <b>ЭДС индукции</b> в контуре равна <b>скорости изменения</b> магнитного потока:</p>
|
||
<p style="text-align:center;margin:8px 0">$$\\mathcal{E}_i = -\\dfrac{\\Delta\\Phi}{\\Delta t}$$</p>
|
||
<p>Знак «минус» — математическая запись правила Ленца: ЭДС <b>противодействует</b> изменению потока.</p>
|
||
<p style="margin-top:10px"><b>Для катушки из $N$ витков</b>:</p>
|
||
<p style="text-align:center;margin:8px 0">$$\\mathcal{E}_i = -N\\,\\dfrac{\\Delta\\Phi}{\\Delta t}$$</p>
|
||
<p>Сила индукционного тока: $I_i = \\mathcal{E}_i / R$, где $R$ — сопротивление катушки.</p>
|
||
<p style="margin-top:10px"><b>Единица</b>: вольт (В).</p>
|
||
<p style="margin-top:10px"><b>Большая ЭДС</b> возникает при:</p>
|
||
<ul style="margin:6px 0 6px 22px">
|
||
<li>быстром изменении $\\Phi$ (резкое движение, быстрое вращение);</li>
|
||
<li>большом числе витков $N$.</li>
|
||
</ul>
|
||
`);
|
||
|
||
/* THEORY 3 — Применение и пример */
|
||
html += makeCard('example', "Применение и пример", "§32", `
|
||
<p><b>Генератор переменного тока.</b> Рамка вращается в магнитном поле, угол $\\alpha$ непрерывно меняется: $\\Phi = BS\\cos(\\omega t)$. Тогда ЭДС:</p>
|
||
<p style="text-align:center;margin:8px 0">$$\\mathcal{E}_i = -\\dfrac{d\\Phi}{dt} = BS\\omega\\sin(\\omega t)$$</p>
|
||
<p>Это <b>переменная синусоидальная</b> ЭДС. Так работают электростанции.</p>
|
||
<p style="margin-top:10px"><b>Трансформатор.</b> Переменный ток в первичной катушке создаёт переменный $\\Phi$ через сердечник → во вторичной катушке возникает ЭДС. Отношение напряжений равно отношению чисел витков:</p>
|
||
<p style="text-align:center;margin:8px 0">$$\\dfrac{U_2}{U_1} = \\dfrac{N_2}{N_1}$$</p>
|
||
<p style="margin-top:10px"><b>Пример расчёта.</b> За $\\Delta t = 0{,}1$ с магнитный поток через катушку из $N = 50$ витков изменился с $0$ до $0{,}4$ Вб. Найди модуль ЭДС индукции.</p>
|
||
<p style="text-align:center;margin:8px 0">$$|\\mathcal{E}_i| = N\\,\\dfrac{|\\Delta\\Phi|}{\\Delta t} = 50\\cdot \\dfrac{0{,}4}{0{,}1} = 200 \\text{ В}$$</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Вывод.</b> Индукция позволяет получать высокие напряжения «из ничего» — но не нарушая закона сохранения энергии: работу совершает тот, кто меняет $\\Phi$.</p>
|
||
`);
|
||
|
||
/* INTERACTIVE 1 — Правило Ленца */
|
||
html += `<div class="wg" id="p32-iv1">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Правило Ленца в действии</div></div>
|
||
<div class="wg-help">Двигай магнит к катушке и от неё. Индукционный ток создаёт поле, <b>препятствующее</b> изменению потока — магнит чувствует силу отталкивания (приближается) или притяжения (удаляется).</div>
|
||
<div class="sliders">
|
||
<label>Положение магнита: <b id="p32-iv1-xL">−0.20</b> <input type="range" id="p32-iv1-x" min="-0.50" max="0.30" value="-0.20" step="0.01"></label>
|
||
</div>
|
||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||
<svg id="p32-iv1-svg" viewBox="0 0 480 280" width="100%" style="height:auto"></svg>
|
||
</div>
|
||
<div id="p32-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.96rem;line-height:1.7;text-align:center"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 2 — Калькулятор ЭДС */
|
||
html += `<div class="wg" id="p32-iv2">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор ЭДС индукции</div></div>
|
||
<div class="wg-help">$|\\mathcal{E}_i| = N\\,|\\Delta\\Phi|/\\Delta t$. Введи число витков, изменение потока и время.</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:10px">
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$N$ (витков):<input type="number" id="p32-iv2-N" value="100" step="1" min="1" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$\\Delta\\Phi$ (Вб):<input type="number" id="p32-iv2-dP" value="0.01" step="any" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
<label style="display:flex;flex-direction:column;gap:4px;font-size:.92rem">$\\Delta t$ (с):<input type="number" id="p32-iv2-dt" value="0.1" step="any" min="0" style="padding:7px;border:1px solid var(--border);border-radius:6px"></label>
|
||
</div>
|
||
<div class="actions"><button class="btn primary" id="p32-iv2-calc">Вычислить $|\\mathcal{E}_i|$</button></div>
|
||
<div id="p32-iv2-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.98rem;line-height:1.8;text-align:center;min-height:40px"></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 3 — Куда направлен ток? */
|
||
html += `<div class="wg" id="p32-iv3">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Куда направлен индукционный ток?</div></div>
|
||
<div class="wg-help">По правилу Ленца ток своим полем <b>противодействует</b> изменению потока (рост → встречное поле; убывание → поддерживающее).</div>
|
||
<div class="score-display"><span>Задача <b id="p32-iv3-i">1</b> / 6</span><span>Очки: <b id="p32-iv3-s">0</b> / 6</span></div>
|
||
<div id="p32-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p32-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:8px"></div>
|
||
<div class="feedback" id="p32-iv3-fb"></div>
|
||
<div class="actions"><button class="btn" id="p32-iv3-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
/* INTERACTIVE 4 — Тренажёр */
|
||
html += `<div class="wg" id="p32-iv4">
|
||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: закон Фарадея</div></div>
|
||
<div class="wg-help">5 задач. Допуск ±5%. Используй $|\\mathcal{E}_i| = N\\,|\\Delta\\Phi|/\\Delta t$.</div>
|
||
<div class="score-display"><span>Задача <b id="p32-iv4-i">1</b> / 5</span><span>Очки: <b id="p32-iv4-s">0</b> / 5</span></div>
|
||
<div id="p32-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
|
||
<div id="p32-iv4-form" style="display:flex;gap:8px;justify-content:center;margin-bottom:8px;flex-wrap:wrap">
|
||
<input type="number" id="p32-iv4-inp" step="any" style="padding:8px 10px;border:1px solid var(--border);border-radius:6px;width:140px;font-size:1rem">
|
||
<button class="btn primary" id="p32-iv4-go">Проверить</button>
|
||
</div>
|
||
<div class="feedback" id="p32-iv4-fb"></div>
|
||
<div class="actions"><button class="btn" id="p32-iv4-restart">Начать заново</button></div>
|
||
</div>`;
|
||
|
||
html += secNav('p31', 'p33');
|
||
html += readButton('p32');
|
||
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
|
||
/* IV1 — Правило Ленца */
|
||
(function(){
|
||
const svg = document.getElementById('p32-iv1-svg');
|
||
const out = document.getElementById('p32-iv1-out');
|
||
const xS = document.getElementById('p32-iv1-x');
|
||
const xL = document.getElementById('p32-iv1-xL');
|
||
let lastX = +xS.value, lastT = performance.now();
|
||
let movesPos = 0, movesNeg = 0, _done = false;
|
||
|
||
function draw(){
|
||
const W = 480, H = 280;
|
||
const x = +xS.value;
|
||
xL.textContent = x.toFixed(2);
|
||
const now = performance.now();
|
||
const dt = Math.max(0.01, (now - lastT) / 1000);
|
||
const dx = x - lastX;
|
||
const vel = dx / dt;
|
||
lastX = x; lastT = now;
|
||
|
||
// Катушка слева, магнит справа подъезжает влево (или удаляется вправо)
|
||
const coilCx = 160, coilCy = 140, coilL = 120, coilR = 40;
|
||
const magW = 90, magH = 40;
|
||
// x: -0.5..0.3, отображаем магнит между 430 (далеко справа) и 240 (внутри катушки правой стороной)
|
||
const magCx = 430 - (x + 0.5) * (430 - 240) / 0.8;
|
||
const magCy = coilCy;
|
||
|
||
let g = '';
|
||
g += '<rect x="0" y="0" width="'+W+'" height="'+H+'" fill="#fafafa"/>';
|
||
g += '<text x="240" y="22" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">Правило Ленца: индукционный ток противодействует движению магнита</text>';
|
||
|
||
// Катушка
|
||
const turns = 9;
|
||
for(let i=0;i<turns;i++){
|
||
const cx = coilCx - coilL/2 + i * (coilL / (turns - 1));
|
||
g += '<ellipse cx="'+cx.toFixed(1)+'" cy="'+coilCy+'" rx="12" ry="'+coilR+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
|
||
}
|
||
|
||
// Магнит: левая сторона = N (красный, обращён к катушке), правая = S (синий)
|
||
g += '<rect x="'+(magCx - magW/2).toFixed(1)+'" y="'+(magCy - magH/2).toFixed(1)+'" width="'+(magW/2).toFixed(1)+'" height="'+magH+'" fill="#fecaca" stroke="#dc2626" stroke-width="1.8"/>';
|
||
g += '<rect x="'+magCx.toFixed(1)+'" y="'+(magCy - magH/2).toFixed(1)+'" width="'+(magW/2).toFixed(1)+'" height="'+magH+'" fill="#bfdbfe" stroke="#2563eb" stroke-width="1.8"/>';
|
||
g += '<text x="'+(magCx - magW/4).toFixed(1)+'" y="'+(magCy + 5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="15" font-weight="800" fill="#dc2626">N</text>';
|
||
g += '<text x="'+(magCx + magW/4).toFixed(1)+'" y="'+(magCy + 5).toFixed(1)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="15" font-weight="800" fill="#2563eb">S</text>';
|
||
|
||
// Направление движения магнита (стрелка над магнитом)
|
||
const moving = Math.abs(vel) > 0.02;
|
||
// приближение: vel > 0 (x растёт) → магнит едет влево к катушке
|
||
const approaching = vel > 0.02;
|
||
const receding = vel < -0.02;
|
||
if(moving){
|
||
const dir = approaching ? -1 : +1; // экранно: влево или вправо
|
||
const arrFromX = magCx + dir * (magW/2 + 6);
|
||
const arrToX = arrFromX + dir * 40;
|
||
g += PHYS.drawArrow(arrFromX, magCy - 32, arrToX, magCy - 32, '#0f172a', 2, 9);
|
||
g += '<text x="'+((arrFromX+arrToX)/2).toFixed(1)+'" y="'+(magCy - 38)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">движение</text>';
|
||
}
|
||
|
||
// Сила Ленца на магнит (отталкивание при приближ., притяжение при удалении)
|
||
// отталкивание: вправо (от катушки); притяжение: влево (к катушке)
|
||
if(moving){
|
||
const forceDir = approaching ? +1 : -1;
|
||
const fLabel = approaching ? 'отталкивание' : 'притяжение';
|
||
const fColor = approaching ? '#dc2626' : '#0891b2';
|
||
const fFromX = magCx + (forceDir > 0 ? +magW/2 + 6 : -magW/2 - 6);
|
||
const fToX = fFromX + forceDir * 36;
|
||
g += PHYS.drawArrow(fFromX, magCy + 36, fToX, magCy + 36, fColor, 2.4, 10);
|
||
g += '<text x="'+((fFromX+fToX)/2).toFixed(1)+'" y="'+(magCy + 56)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="'+fColor+'">F ('+fLabel+')</text>';
|
||
}
|
||
|
||
// Индукционный ток в витках катушки (стрелочки)
|
||
let curSign = 0;
|
||
if(approaching) curSign = +1;
|
||
else if(receding) curSign = -1;
|
||
if(curSign !== 0){
|
||
const dirColor = '#0891b2';
|
||
const ccx = coilCx, ccy = coilCy;
|
||
const r = coilR + 4;
|
||
const dxArr = curSign > 0 ? +14 : -14;
|
||
g += PHYS.drawArrow(ccx - dxArr/2, ccy - r, ccx + dxArr/2, ccy - r, dirColor, 2, 8);
|
||
g += PHYS.drawArrow(ccx + dxArr/2, ccy + r, ccx - dxArr/2, ccy + r, dirColor, 2, 8);
|
||
g += '<text x="'+coilCx+'" y="'+(coilCy - r - 8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="'+dirColor+'">индукц. ток</text>';
|
||
}
|
||
|
||
// Эквивалентный «магнит-катушка» — какой полюс смотрит на магнит
|
||
if(curSign !== 0){
|
||
// приближение: на магнит смотрит N (отталкивает) → правая сторона катушки = N
|
||
// удаление: на магнит смотрит S (притягивает) → правая сторона катушки = S
|
||
const eqPole = approaching ? 'N' : 'S';
|
||
const eqColor = approaching ? '#dc2626' : '#2563eb';
|
||
g += '<text x="'+(coilCx + coilL/2 + 10)+'" y="'+(coilCy + 28)+'" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="'+eqColor+'">↔ '+eqPole+'</text>';
|
||
}
|
||
|
||
svg.innerHTML = g;
|
||
|
||
let note = '';
|
||
if(!moving) note = '<b>Магнит покоится.</b> $\\Phi$ не меняется — индукционного тока и силы нет.';
|
||
else if(approaching) note = '<b>Магнит приближается</b> N-полюсом. $\\Phi$ растёт → индукционный ток создаёт <b>встречное</b> поле (N навстречу N). Катушка <b>отталкивает</b> магнит.';
|
||
else note = '<b>Магнит удаляется.</b> $\\Phi$ убывает → индукционный ток создаёт <b>поддерживающее</b> поле (S к N). Катушка <b>притягивает</b> магнит.';
|
||
out.innerHTML = note;
|
||
renderMath(out);
|
||
|
||
if(approaching) movesPos = 1;
|
||
if(receding) movesNeg = 1;
|
||
if(!_done && movesPos && movesNeg){ _done = true; addXp(10, 'p32-iv1'); bumpProgress('p32', 15); }
|
||
}
|
||
xS.addEventListener('input', draw);
|
||
setInterval(draw, 250);
|
||
draw();
|
||
})();
|
||
|
||
/* IV2 — Калькулятор ЭДС индукции */
|
||
(function(){
|
||
const out = document.getElementById('p32-iv2-out');
|
||
const bGo = document.getElementById('p32-iv2-calc');
|
||
const inN = document.getElementById('p32-iv2-N');
|
||
const inP = document.getElementById('p32-iv2-dP');
|
||
const inT = document.getElementById('p32-iv2-dt');
|
||
let count = 0, _done = false;
|
||
|
||
function calc(){
|
||
const N = +inN.value, dP = +inP.value, dt = +inT.value;
|
||
if(![N,dP,dt].every(isFinite) || dt <= 0 || N < 1){
|
||
out.innerHTML = '<b style="color:#dc2626">Проверь ввод: $N \\ge 1$, $\\Delta t > 0$.</b>';
|
||
renderMath(out);
|
||
return;
|
||
}
|
||
const E = N * Math.abs(dP) / dt;
|
||
let html = '$|\\mathcal{E}_i| = N\\,\\dfrac{|\\Delta\\Phi|}{\\Delta t} = '+N+'\\cdot \\dfrac{'+Math.abs(dP)+'}{'+dt+'} = $ <b>'+E.toFixed(3)+' В</b>';
|
||
// Скорость изменения
|
||
html += '<br><span style="font-size:.9rem;color:#64748b">Скорость изменения потока: $\\Delta\\Phi/\\Delta t = '+(Math.abs(dP)/dt).toFixed(4)+'$ Вб/с.</span>';
|
||
out.innerHTML = html;
|
||
renderMath(out);
|
||
count++;
|
||
if(!_done && count >= 3){ _done = true; addXp(10, 'p32-iv2'); bumpProgress('p32', 15); }
|
||
}
|
||
bGo.addEventListener('click', calc);
|
||
[inN,inP,inT].forEach(x => x.addEventListener('keydown', e => { if(e.key==='Enter') calc(); }));
|
||
})();
|
||
|
||
/* IV3 — Куда направлен индукционный ток? */
|
||
(function(){
|
||
const OPTS = ['Противодействует (встречное поле)', 'Усиливает (поддерживающее поле)'];
|
||
const Q = [
|
||
{ q:'Магнит N-полюсом <b>приближается</b> к катушке.', ans:0, why:'$\\Phi$ растёт → ток создаёт встречное поле, навстречу N-полюсу. Магнит отталкивается.' },
|
||
{ q:'Магнит S-полюсом <b>приближается</b> к катушке.', ans:0, why:'$\\Phi$ растёт (с другим знаком) → ток создаёт встречное поле. Магнит отталкивается.' },
|
||
{ q:'Магнит N-полюсом <b>удаляется</b> от катушки.', ans:1, why:'$\\Phi$ убывает → ток создаёт поле, поддерживающее убывающий поток. Магнит притягивается.' },
|
||
{ q:'Магнит S-полюсом <b>удаляется</b> от катушки.', ans:1, why:'$\\Phi$ убывает → ток создаёт поддерживающее поле. Магнит притягивается.' },
|
||
{ q:'Ток в соседней катушке <b>растёт</b>.', ans:0, why:'$B$ через нашу катушку растёт → $\\Phi$ растёт → ток противодействует.' },
|
||
{ q:'Ток в соседней катушке <b>убывает</b>.', ans:1, why:'$B$ убывает → $\\Phi$ убывает → ток поддерживает (усиливает) поле.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p32-iv3-q');
|
||
const oEl = document.getElementById('p32-iv3-opts');
|
||
const fb = document.getElementById('p32-iv3-fb');
|
||
const iEl = document.getElementById('p32-iv3-i');
|
||
const sEl = document.getElementById('p32-iv3-s');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
oEl.innerHTML = '';
|
||
if(score === Q.length){ addXp(15, 'p32-iv3'); bumpProgress('p32', 25); }
|
||
else if(score >= 4){ addXp(8, 'p32-iv3'); bumpProgress('p32', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
oEl.innerHTML = OPTS.map((t, k) => '<button class="btn primary" data-v="' + k + '">' + t + '</button>').join('');
|
||
fb.style.display = 'none';
|
||
renderMath(qEl);
|
||
oEl.querySelectorAll('button').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
const v = +b.dataset.v;
|
||
if(v === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + OPTS[Q[i].ans] + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
|
||
i++;
|
||
setTimeout(show, 1900);
|
||
});
|
||
});
|
||
}
|
||
document.getElementById('p32-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||
show();
|
||
})();
|
||
|
||
/* IV4 — Тренажёр Фарадея */
|
||
(function(){
|
||
const Q = [
|
||
{ q:'$N = 100$, $\\Delta\\Phi = 0{,}01$ Вб, $\\Delta t = 0{,}1$ с. Найди $|\\mathcal{E}_i|$ (в <b>В</b>).', ans:10, tol:0.6, why:'$|\\mathcal{E}| = 100\\cdot 0{,}01/0{,}1 = 10$ В.' },
|
||
{ q:'$N = 50$, $\\Delta\\Phi = 0{,}4$ Вб, $\\Delta t = 0{,}1$ с. Найди $|\\mathcal{E}_i|$ (в <b>В</b>).', ans:200, tol:10, why:'$|\\mathcal{E}| = 50\\cdot 0{,}4/0{,}1 = 200$ В.' },
|
||
{ q:'$N = 200$. За 0,5 с поток изменился от 0,2 Вб до 0,6 Вб. Найди $|\\mathcal{E}_i|$ (в <b>В</b>).', ans:160, tol:8, why:'$|\\Delta\\Phi| = 0{,}4$ Вб → $|\\mathcal{E}| = 200\\cdot 0{,}4/0{,}5 = 160$ В.' },
|
||
{ q:'Знак «−» в формуле $\\mathcal{E}_i = -\\Delta\\Phi/\\Delta t$ означает: <br>1 = правило Ленца, 2 = отрицательный заряд, 3 = ничего. Введи номер.', ans:1, tol:0.1, why:'Минус — математическая форма правила Ленца: ЭДС противодействует изменению потока.' },
|
||
{ q:'Если скорость изменения потока удвоить, во сколько раз изменится $|\\mathcal{E}_i|$?', ans:2, tol:0.1, why:'$|\\mathcal{E}| \\propto |\\Delta\\Phi|/\\Delta t$ — удвоится.' }
|
||
];
|
||
let i = 0, score = 0;
|
||
const qEl = document.getElementById('p32-iv4-q');
|
||
const fb = document.getElementById('p32-iv4-fb');
|
||
const iEl = document.getElementById('p32-iv4-i');
|
||
const sEl = document.getElementById('p32-iv4-s');
|
||
const inp = document.getElementById('p32-iv4-inp');
|
||
const bGo = document.getElementById('p32-iv4-go');
|
||
|
||
function show(){
|
||
if(i >= Q.length){
|
||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
if(score === Q.length){ addXp(15, 'p32-iv4'); bumpProgress('p32', 25); }
|
||
else if(score >= 3){ addXp(8, 'p32-iv4'); bumpProgress('p32', 15); }
|
||
return;
|
||
}
|
||
iEl.textContent = (i+1);
|
||
sEl.textContent = score;
|
||
qEl.innerHTML = Q[i].q;
|
||
fb.style.display = 'none';
|
||
inp.value = ''; inp.disabled = false; bGo.disabled = false; inp.focus();
|
||
renderMath(qEl);
|
||
}
|
||
function check(){
|
||
if(inp.disabled) return;
|
||
const v = parseFloat(inp.value.replace(',','.'));
|
||
if(!isFinite(v)){ feedback(fb, false, 'Введи число.'); return; }
|
||
const tol = Math.max(Q[i].tol, Math.abs(Q[i].ans)*0.05);
|
||
const ok = Math.abs(v - Q[i].ans) <= tol;
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! ' + Q[i].why + ' Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Верно: ' + Q[i].ans + '. ' + Q[i].why + ' Дальше ▶');
|
||
sEl.textContent = score;
|
||
inp.disabled = true; bGo.disabled = true;
|
||
i++;
|
||
setTimeout(show, 1900);
|
||
}
|
||
bGo.addEventListener('click', check);
|
||
inp.addEventListener('keydown', e => { if(e.key==='Enter') check(); });
|
||
document.getElementById('p32-iv4-restart').addEventListener('click', () => { i = 0; score = 0; inp.disabled=false; bGo.disabled=false; show(); });
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p32');
|
||
}
|
||
|
||
function build_p33(){
|
||
const box = document.getElementById('p33-body');
|
||
let html = '';
|
||
html += makeCard('theory', "Самоиндукция", "§33", `
|
||
<p><b>Самоиндукция</b> — этот параграф в разработке (Phase 1+).</p>
|
||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 5+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||
</p>
|
||
`);
|
||
html += secNav('p32', 'final5');
|
||
html += readButton('p33');
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
wireReadBtn('p33');
|
||
}
|
||
|
||
function build_final5(){
|
||
const box = document.getElementById('final5-body');
|
||
let html = '';
|
||
html += makeCard('theory', "Финал главы 5", "★", `
|
||
<p><b>Финал главы 5</b> — этот параграф в разработке (Phase 1+).</p>
|
||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 5+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||
</p>
|
||
`);
|
||
html += secNav('p33', null);
|
||
html += readButton('final5');
|
||
box.innerHTML = html;
|
||
renderMath(box);
|
||
wireReadBtn('final5');
|
||
}
|
||
|
||
/* ===== Search ===== */
|
||
const SEARCH_INDEX = (function(){
|
||
const arr=[];
|
||
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
|
||
return arr;
|
||
})();
|
||
function initSearch(){
|
||
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
|
||
if(!modal||!inp||!out) return;
|
||
let cur=0,rows=[];
|
||
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
|
||
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
|
||
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'…':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
|
||
function pick(){ const r=rows[cur]; if(!r) return; close(); goTo(r.sec); }
|
||
function move(d){ const items=out.querySelectorAll('.search-row'); if(!items.length) return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active'); items[cur].scrollIntoView({block:'nearest'}); }
|
||
function open(){ modal.classList.add('show'); inp.value=''; rows=rank(''); render(); setTimeout(()=>inp.focus(),50); }
|
||
function close(){ modal.classList.remove('show'); }
|
||
btn&&btn.addEventListener('click',open);
|
||
modal.addEventListener('click',e=>{if(e.target===modal)close();});
|
||
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
|
||
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
|
||
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show')) close(); else open(); } });
|
||
}
|
||
|
||
function initSidebarToggle(){
|
||
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
|
||
if(!side||!btn) return;
|
||
function open(){ side.classList.add('open'); back.classList.add('show'); }
|
||
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
|
||
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
|
||
back.addEventListener('click',close);
|
||
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
|
||
}
|
||
|
||
function init(){
|
||
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
|
||
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
|
||
setTimeout(()=>achievement('start'), 600);
|
||
if(window.LS&&window.LS.xp){
|
||
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
|
||
}
|
||
}
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
|
||
/* === PHYS10 POLISH JS === */
|
||
(function(){
|
||
function bumpScore(el){
|
||
if(!el) return;
|
||
el.classList.remove('bump');
|
||
void el.offsetWidth;
|
||
el.classList.add('bump');
|
||
setTimeout(function(){ try{ el.classList.remove('bump'); }catch(e){} }, 270);
|
||
}
|
||
window.__phys10BumpScore = bumpScore;
|
||
|
||
function observeScores(root){
|
||
root = root || document;
|
||
var nodes = root.querySelectorAll('.score-display b');
|
||
nodes.forEach(function(b){
|
||
if(b.__scoreObs) return;
|
||
b.__scoreObs = true;
|
||
var last = b.textContent;
|
||
try{
|
||
var mo = new MutationObserver(function(){
|
||
var nv = b.textContent;
|
||
if(nv !== last){ last = nv; bumpScore(b); }
|
||
});
|
||
mo.observe(b, {childList:true, characterData:true, subtree:true});
|
||
}catch(e){}
|
||
});
|
||
}
|
||
function rescanScores(){ try{ observeScores(document); }catch(e){} }
|
||
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', rescanScores);
|
||
else rescanScores();
|
||
|
||
try{
|
||
var rootObs = new MutationObserver(function(muts){
|
||
var need = false;
|
||
for(var i=0;i<muts.length && !need;i++){
|
||
var m = muts[i];
|
||
for(var j=0;j<m.addedNodes.length;j++){
|
||
var n = m.addedNodes[j];
|
||
if(n.nodeType===1){
|
||
if(n.classList && n.classList.contains('score-display')) { need = true; break; }
|
||
if(n.querySelector && n.querySelector('.score-display b')) { need = true; break; }
|
||
}
|
||
}
|
||
}
|
||
if(need) rescanScores();
|
||
});
|
||
rootObs.observe(document.body, {childList:true, subtree:true});
|
||
}catch(e){}
|
||
|
||
function refreshDoneMarks(){
|
||
try{
|
||
if(typeof STATE === 'undefined' || !STATE.progress) return;
|
||
document.querySelectorAll('.psel-card').forEach(function(c){
|
||
var id = c.dataset.id || c.dataset.progCard;
|
||
if(!id) return;
|
||
var pct = +STATE.progress[id] || 0;
|
||
if(!c.querySelector('.psel-done')){
|
||
var s = document.createElement('span');
|
||
s.className = 'psel-done';
|
||
s.setAttribute('title','Прочитано');
|
||
s.innerHTML = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
|
||
c.appendChild(s);
|
||
}
|
||
c.classList.toggle('done', pct >= 50);
|
||
});
|
||
}catch(e){}
|
||
}
|
||
try{
|
||
if(typeof window.refreshProgressUI === 'function'){
|
||
var _origRP = window.refreshProgressUI;
|
||
window.refreshProgressUI = function(){ var r = _origRP.apply(this, arguments); setTimeout(refreshDoneMarks, 0); return r; };
|
||
}
|
||
}catch(e){}
|
||
setTimeout(refreshDoneMarks, 600);
|
||
setTimeout(refreshDoneMarks, 1800);
|
||
window.addEventListener('focus', function(){ setTimeout(refreshDoneMarks, 200); });
|
||
|
||
document.addEventListener('click', function(e){
|
||
var card = e.target.closest && e.target.closest('.psel-card');
|
||
if(!card) return;
|
||
var id = card.dataset.id;
|
||
if(!id) return;
|
||
setTimeout(function(){
|
||
var sec = document.getElementById('sec-' + id);
|
||
if(sec) try{ sec.scrollIntoView({behavior:'smooth', block:'start'}); }catch(e){}
|
||
}, 60);
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|