Created
September 8, 2026 04:04
-
-
Save c2h2/e3604aa7e0e84471d90a88766173cfe9 to your computer and use it in GitHub Desktop.
Physics book (HTML)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>给孩子的物理世界 —— 一千页的通识物理书</title> | |
| <style> | |
| :root{ | |
| --ink:#1f2937; --sub:#6b7280; --line:#e5e7eb; | |
| --blue:#2563eb; --red:#dc2626; --green:#16a34a; --purple:#9333ea; --amber:#d97706; | |
| } | |
| *{box-sizing:border-box} | |
| html{scroll-behavior:smooth} | |
| body{ | |
| margin:0; background:#e9ecf2; color:var(--ink); | |
| font-family:"Noto Sans SC","Source Han Sans SC","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif; | |
| line-height:1.85; | |
| } | |
| /* fixed nav */ | |
| #nav{ | |
| position:fixed; top:0; left:0; right:0; z-index:99; | |
| display:flex; align-items:center; gap:10px; padding:8px 16px; | |
| background:rgba(255,255,255,.95); border-bottom:1px solid var(--line); | |
| box-shadow:0 2px 10px rgba(0,0,0,.06); | |
| } | |
| #nav button{ | |
| border:1px solid var(--line); background:#fff; border-radius:8px; | |
| padding:5px 14px; font-size:14px; cursor:pointer; color:var(--ink); | |
| } | |
| #nav button:hover{border-color:var(--blue); color:var(--blue)} | |
| #nav .book{font-weight:700; color:var(--blue)} | |
| #nav .pg{font-size:14px; color:var(--sub); min-width:110px; text-align:center} | |
| /* page sheet */ | |
| .sheet-wrap{display:flex; flex-direction:column; align-items:center; padding:64px 8px 40px} | |
| .page{ | |
| width:min(860px,100%); background:#fff; margin:0 0 24px; | |
| border-radius:6px; box-shadow:0 3px 18px rgba(20,30,60,.12); | |
| padding:44px 52px 40px; position:relative; min-height:1120px; | |
| display:flex; flex-direction:column; | |
| } | |
| .page .pno{position:absolute; right:44px; bottom:26px; font-size:13px; color:#9aa3b2} | |
| .page .psec{position:absolute; left:52px; bottom:26px; font-size:13px; color:#9aa3b2} | |
| .pagehead{display:flex; align-items:center; justify-content:space-between; margin-bottom:14px} | |
| .pagehead h2{margin:0; font-size:22px} | |
| .badge{font-size:12.5px; padding:3px 12px; border-radius:99px; color:#fff; font-weight:600; white-space:nowrap} | |
| .rule{height:3px; width:64px; border-radius:2px; margin:10px 0 18px} | |
| body p{margin:10px 0; font-size:16.5px} | |
| .chartwrap{ | |
| margin:18px 0; border:1px solid var(--line); border-radius:12px; | |
| padding:14px 16px 8px; background:#fbfcfe; | |
| } | |
| .chartcap{font-size:13.5px; color:var(--sub); text-align:center; padding:6px 0 8px} | |
| .formula{ | |
| font-size:20px; text-align:center; margin:14px 0; padding:10px; | |
| background:#f4f7ff; border:1px dashed #b9c8ff; border-radius:10px; | |
| font-family:"STIX Two Math","Cambria Math","Noto Serif SC",serif; | |
| } | |
| .tip{ | |
| background:#f2fbf4; border-left:4px solid var(--green); | |
| padding:10px 14px; border-radius:0 10px 10px 0; margin:14px 0; font-size:15px; | |
| } | |
| .try{ | |
| background:#fffaf0; border-left:4px solid var(--amber); | |
| padding:10px 14px; border-radius:0 10px 10px 0; margin:14px 0; font-size:15.5px; | |
| } | |
| .think{ | |
| background:#f6f4ff; border-left:4px solid var(--purple); | |
| padding:10px 14px; border-radius:0 10px 10px 0; margin:14px 0; font-size:15.5px; | |
| } | |
| details{margin:8px 0 0} | |
| summary{cursor:pointer; color:var(--blue); font-size:14.5px} | |
| details p{font-size:14.5px} | |
| .kicker{font-size:13.5px; color:var(--sub); margin-bottom:2px} | |
| h1.cover-t{font-size:52px; line-height:1.35; margin:18px 0 6px; color:#111827} | |
| .cover-sub{font-size:19px; color:var(--sub)} | |
| .cover-box{margin-top:34px; border:2px solid #111827; border-radius:14px; padding:22px} | |
| .cover-meta{display:flex; gap:12px; flex-wrap:wrap; margin-top:22px} | |
| .chip{border:1px solid var(--line); border-radius:99px; padding:5px 16px; font-size:14px; background:#f8fafc} | |
| /* toc */ | |
| .toc{column-count:1; margin-top:10px} | |
| .toc a{ | |
| display:flex; justify-content:space-between; align-items:center; | |
| padding:11px 14px; border-radius:10px; text-decoration:none; color:var(--ink); font-size:15.5px; | |
| border:1px solid transparent; | |
| } | |
| .toc a:hover{background:#f4f7ff; border-color:#c7d4ff} | |
| .toc .pgno{color:var(--sub); font-size:13.5px} | |
| .toc .lv{font-size:12px; padding:1px 8px; border-radius:99px; color:#fff; margin-left:8px} | |
| /* print: one sheet per printed page */ | |
| @page{size:180mm auto; margin:0} | |
| @media print{ | |
| body{background:#fff} | |
| #nav{display:none} | |
| .sheet-wrap{padding:0} | |
| .page{ | |
| box-shadow:none; border-radius:0; width:180mm; min-height:250mm; | |
| break-after:page; padding:16mm 16mm 14mm; border:1px solid #ddd; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <nav id="nav"> | |
| <button id="btnHome">📖 目录</button> | |
| <button id="btnPrev">← 上一页</button> | |
| <span class="pg" id="pgLabel">首页</span> | |
| <button id="btnNext">下一页 →</button> | |
| <span style="flex:1"></span> | |
| <span class="book">《给孩子的物理世界》</span> | |
| </nav> | |
| <div class="sheet-wrap" id="wrap"></div> | |
| <script> | |
| /* ================= 小工具 ================= */ | |
| function rng(seed){let s=(seed*9301+49297)%233280;return function(){s=(s*9301+49297)%233280;return s/233280;};} | |
| function fmt(n){ | |
| if(n===null||n===undefined||!isFinite(n))return''; | |
| if(Object.is(n,-0))n=0; | |
| if(n===0)return'0'; | |
| const a=Math.abs(n); | |
| if(a>=100000||(a>0&&a<0.001))return n.toExponential(2); | |
| if(a>=10000)return(n/1000).toFixed(0).replace(/\.0$/,'')+'k'; | |
| if(a>=100)return Math.round(n)+''; | |
| if(a>=1)return(+n.toFixed(2))+''; | |
| return(+n.toPrecision(2))+''; | |
| } | |
| const C8=['#2563eb','#dc2626','#16a34a','#9333ea','#d97706','#0d9488','#db2777','#475569']; | |
| const SVG_OPEN='<svg viewBox="0 0 640 380" style="width:100%;height:auto;display:block">'; | |
| function axes(X,Y,L,T,W,R,H,B){ | |
| return '<line x1="'+L+'" y1="'+T+'" x2="'+L+'" y2="'+(H-B)+'" stroke="#94a3b8" stroke-width="1.4"/>'+ | |
| '<line x1="'+L+'" y1="'+(H-B)+'" x2="'+(W-R)+'" y2="'+(H-B)+'" stroke="#94a3b8" stroke-width="1.4"/>'; | |
| } | |
| function yl(o,x){return '<text x="'+(L64-7)+'" y="'+(o.y+4)+'" text-anchor="end" font-size="13" fill="#8a94a6">'+fmt(o.v)+'</text><line x1="64" y1="'+o.y+'" x2="614" y2="'+o.y+'" stroke="#eef1f6"/>';} | |
| const L64=64; | |
| /* ---- 折线 / 多序列 ---- */ | |
| function chartLine(series,o){ | |
| o=o||{}; | |
| const W=640,H=380,L=64,R=26,T=34,B=56; | |
| let pts=[];series.forEach(function(s){pts=pts.concat(s.pts);}); | |
| if(!pts.length)return SVG_OPEN+'</svg>'; | |
| let xmin=Infinity,xmax=-Infinity,ymin=Math.min(0,Math.min.apply(null,pts.map(p=>p[1]))),ymax=Math.max.apply(null,pts.map(p=>p[1])); | |
| pts.forEach(function(p){if(p[0]<xmin)xmin=p[0];if(p[0]>xmax)xmax=p[0];}); | |
| if(xmax-xmin<1e-9)xmax=xmin+1; | |
| const span=(ymax-ymin)||1; ymax+=span*0.12; if(ymax>0)ymin-=span*0.08; | |
| const Xl=function(v){return L+(v-xmin)/(xmax-xmin)*(W-L-R);}; | |
| const Yl=function(v){return H-B-(v-ymin)/(ymax-ymin)*(H-T-B);}; | |
| let s=axes(Xl,Yl,L,T,W,R,H,B); | |
| for(let i=0;i<=4;i++){ | |
| const yv=ymin+(ymax-ymin)*i/4, xv=xmin+(xmax-xmin)*i/4; | |
| s+='<line x1="'+L+'" y1="'+Yl(yv)+'" x2="'+(W-R)+'" y2="'+Yl(yv)+'" stroke="#eef1f6"/>'; | |
| s+='<text x="'+(L-7)+'" y="'+(Yl(yv)+4)+'" text-anchor="end" font-size="13" fill="#8a94a6">'+fmt(yv)+'</text>'; | |
| s+='<text x="'+Xl(xv)+'" y="'+(H-B+20)+'" text-anchor="middle" font-size="13" fill="#8a94a6">'+fmt(xv)+'</text>'; | |
| } | |
| series.forEach(function(sr,si){ | |
| const c=sr.color||C8[si%C8.length]; | |
| const P=sr.pts.map(function(p){return Xl(p[0])+','+Yl(p[1]);}).join(' '); | |
| if(sr.fill){ | |
| const first=sr.pts[0][0], last=sr.pts[sr.pts.length-1][0]; | |
| s+='<polygon points="'+Xl(first)+','+Yl(0)+' '+P+' '+Xl(last)+','+Yl(0)+'" fill="'+c+'" opacity="0.14"/>'; | |
| } | |
| s+='<polyline points="'+P+'" fill="none" stroke="'+c+'" stroke-width="2.6"'+(sr.dash?' stroke-dasharray="8 6"':'')+'/>'; | |
| if(!sr.fill&&sr.pts.length<=26){sr.pts.forEach(function(p){s+='<circle cx="'+Xl(p[0])+'" cy="'+Yl(p[1])+'" r="3.4" fill="'+c+'"/>';});} | |
| if(sr.label){s+='<text x="'+(W-R-6)+'" y="'+(T+16+si*19)+'" text-anchor="end" font-size="13.5" fill="'+c+'">— '+sr.label+'</text>';} | |
| }); | |
| if(o.xlabel)s+='<text x="'+((L+W-R)/2)+'" y="'+(H-10)+'" text-anchor="middle" font-size="14" fill="#5b6472">'+o.xlabel+'</text>'; | |
| if(o.ylabel)s+='<text x="'+16+'" y="'+((T+H-B)/2)+'" text-anchor="middle" font-size="14" fill="#5b6472" transform="rotate(-90 16 '+((T+H-B)/2)+')">'+o.ylabel+'</text>'; | |
| return SVG_OPEN+s+'</svg>'; | |
| } | |
| /* ---- 条形 ---- */ | |
| function chartBar(items,o){ | |
| o=o||{}; | |
| const W=640,H=380,L=70,R=26,T=44,B=74; | |
| const max=Math.max.apply(null,items.map(i=>i.value))*1.14; | |
| const slot=(W-L-R)/items.length, bw=Math.min(slot*0.56,66); | |
| const Yl=function(v){return H-B-v/max*(H-T-B);}; | |
| let s=axes(X=>L,Y=>H-B,L,T,W,R,H,B); | |
| for(let i=0;i<=4;i++){ | |
| const v=max*i/4; | |
| s+='<line x1="'+L+'" y1="'+Yl(v)+'" x2="'+(W-R)+'" y2="'+Yl(v)+'" stroke="#eef1f6"/>'; | |
| s+='<text x="'+(L-7)+'" y="'+(Yl(v)+4)+'" text-anchor="end" font-size="13" fill="#8a94a6">'+fmt(v)+(o.yunit||'')+'</text>'; | |
| } | |
| items.forEach(function(it,i){ | |
| const x=L+slot*i+(slot-bw)/2, y=Yl(it.value); | |
| s+='<rect x="'+x+'" y="'+y+'" width="'+bw+'" height="'+(H-B-y)+'" rx="7" fill="'+(it.color||C8[i%C8.length])+'"/>'; | |
| s+='<text x="'+(x+bw/2)+'" y="'+(y-7)+'" text-anchor="middle" font-size="13" fill="#333c4a">'+fmt(it.value)+(o.unit||'')+'</text>'; | |
| const lx=L+slot*i+slot/2; | |
| s+='<text x="'+lx+'" y="'+(H-B+18)+'" text-anchor="end" font-size="13" fill="#5b6472" transform="rotate(-16 '+lx+' '+(H-B+18)+')">'+it.label+'</text>'; | |
| }); | |
| if(o.xlabel)s+='<text x="'+((L+W-R)/2)+'" y="'+(H-10)+'" text-anchor="middle" font-size="14" fill="#5b6472">'+o.xlabel+'</text>'; | |
| return SVG_OPEN+s+'</svg>'; | |
| } | |
| /* ---- 仪表 ---- */ | |
| function chartGauge(val,max,unit,label,sub){ | |
| const W=640,H=400,cx=320,cy=330,r=235; | |
| const frac=Math.max(0.006,Math.min(1,(val==null?0:val)/max)); | |
| const half=Math.PI*r; | |
| const arc='M '+(cx-r)+' '+cy+' A '+r+' '+r+' 0 0 1 '+(cx+r)+' '+cy; | |
| let s=''; | |
| [0,.25,.5,.75,1].forEach(function(f){ | |
| const a=Math.PI*(1-f), x=cx-r*Math.cos(a), y=cy-r*Math.sin(a); | |
| s+='<circle cx="'+x+'" cy="'+y+'" r="5" fill="#cbd3e1"/>'; | |
| }); | |
| s+='<path d="'+arc+'" fill="none" stroke="#e6ebf3" stroke-width="30" stroke-linecap="round"/>'; | |
| s+='<path d="'+arc+'" fill="none" stroke="url(#gg)" stroke-width="30" stroke-linecap="round" stroke-dasharray="'+(half*frac)+' '+half+'"/>'; | |
| s+='<defs><linearGradient id="gg" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#2563eb"/><stop offset="1" stop-color="#0ea5e9"/></linearGradient></defs>'; | |
| s+='<text x="'+cx+'" y="'+(cy-92)+'" text-anchor="middle" font-size="52" font-weight="700" fill="#111827">'+fmt(val)+'</text>'; | |
| s+='<text x="'+cx+'" y="'+(cy-56)+'" text-anchor="middle" font-size="20" fill="#6b7280">'+(unit||'')+'</text>'; | |
| if(label)s+='<text x="'+cx+'" y="'+(cy+4)+'" text-anchor="middle" font-size="18" fill="#333c4a">'+label+'</text>'; | |
| if(sub)s+='<text x="'+cx+'" y="'+(cy+30)+'" text-anchor="middle" font-size="13.5" fill="#8a94a6">'+sub+'</text>'; | |
| return '<svg viewBox="0 0 640 400" style="width:100%;height:auto;display:block">'+s+'</svg>'; | |
| } | |
| /* ---- 饼图 ---- */ | |
| function chartPie(items){ | |
| const W=640,H=380,cx=195,cy=195,r=152; | |
| const total=items.reduce(function(a,b){return a+b.value;},0); | |
| let a=-Math.PI/2, s=''; | |
| items.forEach(function(it,i){ | |
| const frac=it.value/total, a2=a+frac*Math.PI*2; | |
| const x1=cx+r*Math.cos(a), y1=cy+r*Math.sin(a), x2=cx+r*Math.cos(a2), y2=cy+r*Math.sin(a2); | |
| const large=(a2-a)>Math.PI?1:0; | |
| s+='<path d="M '+cx+' '+cy+' L '+x1+' '+y1+' A '+r+' '+r+' 0 '+large+' 1 '+x2+' '+y2+' Z" fill="'+(it.color||C8[i%C8.length])+'" stroke="#fff" stroke-width="2.5"/>'; | |
| s+='<text x="406" y="'+(70+i*24)+'" text-anchor="start" font-size="14">'+ | |
| '<tspan>▪</tspan> '+it.label+' :'+it.value+(it.unit||'')+'</text>'; | |
| s+='<text x="406" y="'+(70+i*24)+'" dx="150" text-anchor="end" font-size="13" fill="#8a94a6">'+(frac*100).toFixed(1)+'%</text>'; | |
| a=a2; | |
| }); | |
| return '<svg viewBox="0 0 640 380" style="width:100%;height:auto;display:block">'+s+'</svg>'; | |
| } | |
| /* ---- 箭头场 ---- */ | |
| function chartField(kind,tag){ | |
| const W=640,H=380,mL=48,mT=34,cols=9,rows=5; | |
| const cw=(W-2*mL)/cols, chh=(H-2*mT)/rows; | |
| let s=''; | |
| function arrow(px,py,ang){ | |
| const L2=15, x2=px+L2*Math.cos(ang), y2=py+L2*Math.sin(ang); | |
| s+='<line x1="'+(px-L2*Math.cos(ang))+'" y1="'+(py-L2*Math.sin(ang))+'" x2="'+x2+'" y2="'+y2+'" stroke="#3b82f6" stroke-width="2.2"/>'; | |
| const ah=7; | |
| s+='<line x1="'+x2+'" y1="'+y2+'" x2="'+(x2-ah*Math.cos(ang-0.45))+'" y2="'+(y2-ah*Math.sin(ang-0.45))+'" stroke="#3b82f6" stroke-width="2.2"/>'; | |
| s+='<line x1="'+x2+'" y1="'+y2+'" x2="'+(x2-ah*Math.cos(ang+0.45))+'" y2="'+(y2-ah*Math.sin(ang+0.45))+'" stroke="#3b82f6" stroke-width="2.2"/>'; | |
| } | |
| const cqR=[232,190], cqL=[408,190]; | |
| for(let j=0;j<rows;j++){ | |
| for(let i=0;i<cols;i++){ | |
| const px=mL+cw*(i+0.5), py=mT+chh*(j+0.5); | |
| let ang=0, skip=false; | |
| if(kind==='unif')ang=0; | |
| else if(kind==='radial'||kind==='radialN'){ | |
| const dx=px-320, dy=py-190, d=Math.hypot(dx,dy); | |
| if(d<34)skip=true; ang=Math.atan2(dy,dx)*(kind==='radialN'?-1:1); | |
| } | |
| else if(kind==='dipole'){ | |
| let ex=0,ey=0; | |
| [[1,cqR],[-1,cqL]].forEach(function(ch){ | |
| const dx=px-ch[1][0], dy=py-ch[1][1], d=Math.hypot(dx,dy)+12; | |
| ex+=ch[0]*dx/(d*d*d); ey+=ch[0]*dy/(d*d*d); | |
| }); | |
| if(Math.hypot(ex,ey)<1e-9)skip=true; else ang=Math.atan2(ey,ex); | |
| } | |
| else if(kind==='plates'){ | |
| if(px>190&&px<450)ang=(j<rows/2)?Math.PI:0; else ang=0; | |
| } | |
| if(!skip)arrow(px,py,ang); | |
| } | |
| } | |
| if(kind==='dipole'){ | |
| s+='<circle cx="'+cqR[0]+'" cy="'+cqR[1]+'" r="26" fill="#dc2626"/><circle cx="'+cqL[0]+'" cy="'+cqL[1]+'" r="26" fill="#2563eb"/>'; | |
| s+='<text x="'+cqR[0]+'" y="'+(cqR[1]+8)+'" text-anchor="middle" font-size="22" fill="#fff">+</text>'; | |
| s+='<text x="'+cqL[0]+'" y="'+(cqL[1]+8)+'" text-anchor="middle" font-size="22" fill="#fff">−</text>'; | |
| } | |
| if(kind==='radial'||kind==='radialN'){ | |
| s+='<circle cx="320" cy="190" r="24" fill="'+(kind==='radialN'?'#2563eb':'#dc2626')+'"/>'; | |
| s+='<text x="320" y="198" text-anchor="middle" font-size="22" fill="#fff">'+(kind==='radialN'?'−':'+')+'</text>'; | |
| } | |
| if(kind==='plates'){ | |
| s+='<rect x="150" y="60" width="20" height="260" fill="#dc2626"/><rect x="470" y="60" width="20" height="260" fill="#2563eb"/>'; | |
| s+='<text x="160" y="50" text-anchor="middle" font-size="22" fill="#dc2626">+</text>'; | |
| s+='<text x="480" y="50" text-anchor="middle" font-size="22" fill="#2563eb">−</text>'; | |
| s+='<text x="200" y="195" font-size="18" fill="#dc2626">正板</text><text x="430" y="195" font-size="18" fill="#2563eb">负板</text>'; | |
| } | |
| if(tag)s+='<text x="52" y="366" font-size="15" fill="#5b6472">'+tag+'</text>'; | |
| return '<svg viewBox="0 0 640 380" style="width:100%;height:auto;display:block">'+s+'</svg>'; | |
| } | |
| /* ---- 轨道 ---- */ | |
| function chartOrbit(){ | |
| let s=''; | |
| const ring=[[80,'水星',0.4],[118,'金星',2.1],[156,'地球',3.9],[196,'火星',5.75]]; | |
| s+='<circle cx="320" cy="190" r="46" fill="#f59e0b"/>'; | |
| s+='<text x="320" y="196" text-anchor="middle" font-size="15" fill="#7c2d12">太阳</text>'; | |
| ring.forEach(function(r,i){ | |
| s+='<circle cx="320" cy="190" r="'+r[0]+'" fill="none" stroke="#cbd3e1" stroke-dasharray="5 5"/>'; | |
| }); | |
| ring.forEach(function(r,i){ | |
| const a=[3.5,1.1,5.6,0.2][i]; | |
| const x=320+r[0]*Math.cos(a), y=190+r[0]*Math.sin(a); | |
| const col=['#8b5e3c','#d9a441','#2563eb','#dc2626'][i]; | |
| s+='<circle cx="'+x+'" cy="'+y+'" r="'+(5+i)+'" fill="'+col+'"/>'; | |
| s+='<text x="'+(x+10)+'" y="'+(y-8)+'" font-size="13.5" fill="#333c4a">'+r[1]+'(周期'+r[2]+'年)</text>'; | |
| }); | |
| return '<svg viewBox="0 0 640 380" style="width:100%;height:auto;display:block">'+s+'</svg>'; | |
| } | |
| /* ================= 章节内容 ================= */ | |
| function P(o){return o;} | |
| function page(i,t,body,chart,cap,formula,tq,ta,th){ | |
| return P({t:t,body:body,chart:chart,cap:cap,formula:formula,tq:tq,ta:ta,th:th}); | |
| } | |
| const SEC=[ | |
| /* 1 测量与单位(小学) 40页 */ | |
| { | |
| name:'测量与单位', level:0, pages:40, intro:'量一量、比一比,认识世界的最小语言——单位。', | |
| gen:function(i){ | |
| const r=rng(i+11); | |
| const N=[['铅笔',18],['橡皮',6],['笔记本',26],['课桌',78],['篮球',24],['水瓶',25],['足球',22],['书包',45],['直尺',30],['小猫尾巴',14]]; | |
| const a=N[i%10], b=N[(i+3)%10]; | |
| const L=Math.max(3,Math.round(a[1]*(0.85+r()*0.4))), L2=Math.max(2,Math.round(b[1]*(0.85+r()*0.5))); | |
| const t=['用厘米量一量','先猜一猜,再量一量','厘米和米的关系','测量小能手'][i%4]; | |
| const body='<p>小朋友们,今天我们来量一量'+a[0]+'。拿出你的直尺,把"零刻度线"对准物体的一端,读另一端的数字——它的长度大约是 <b>'+L+' 厘米</b>。</p>'+ | |
| '<p>量'+b[0]+'的时候要小心:视线要和刻度线垂直,不能从斜着看。它的长度大约是 <b>'+L2+' 厘米</b>。数一数,'+(L>L2?a[0]:b[0])+'更长哦!</p>'+ | |
| '<div class="tip">小知识:1 米 = 100 厘米。'+L+' 厘米写成米就是 '+(L/100).toFixed(2)+' 米。</div>'; | |
| const items=[];for(let k=0;k<5;k++){const g=N[(i+k*2)%10];items.push({label:g[0],value:Math.max(3,Math.round(g[1]*(0.8+r()*0.5)))});} | |
| return page(i,'测量课堂 · '+t, body, chartBar(items,{unit:'cm',xlabel:'几种常见物品'}), | |
| '图:'+items.length+'种物品的长度对比(厘米)', '1 米 = 100 厘米 · 读数要估到最小刻度的一半', | |
| '和你的同桌合作:量一量'+b[0]+',再比一比你们的'+a[0]+',谁的那支更长?', | |
| '先估测,再实测。估测值一般在真值的上下几十厘米以内,量完把误差记下来。', | |
| '为什么同一把尺子、同一个人,量两次'+a[0]+'结果可能差 1 厘米?这就是"测量误差"——下一页我们会讲它。'); | |
| } | |
| }, | |
| /* 2 运动(小学→初中) 60页 */ | |
| { | |
| name:'运动初探', level:0, pages:60, intro:'快慢、距离、时间——用数学描述运动。', | |
| gen:function(i){ | |
| const r=rng(i+101); | |
| const V=[['小朋友跑步',2],['自行车',6],['公交车',11],['小汽车',22],['高铁',80],['喷气式飞机',250],['乌龟',0.3],['猎豹',29],['蜗牛',0.006],['游泳健将',2.5]]; | |
| const v=V[i%10][1], name=V[i%10][0]; | |
| const tSec=5+ (i%12)*2, d=Math.round(v*tSec); | |
| const t=['谁跑得最快?','把运动画成一条线','算一算平均速度','慢镜头里的世界'][i%4]; | |
| const body='<p>'+name+'出发了!它用 '+(name==='蜗牛'?Math.round(tSec/60*60)+' 分':tSec+' 秒')+' 走了 <b>'+d+' 米</b>。快慢用"速度"来描述。</p>'+ | |
| '<p>速度 = 路程 ÷ 时间。算一算:'+d+' 米 ÷ '+tSec+' 秒 ≈ <b>'+fmt(v)+' 米/秒</b>。图上的直线越陡,说明运动越快——斜率就是速度!</p>'+ | |
| '<div class="tip">换算小魔法:1 米/秒 = 3.6 千米/时。高铁 80 米/秒 ≈ 288 千米/时!</div>'; | |
| const series=[{label:name+'('+fmt(v)+'m/s)',color:'#2563eb',pts:[]},{label:'小朋友跑步(2m/s)',color:'#d97706',pts:[]}]; | |
| for(let x=0;x<=10;x++){series[0].pts.push([x,+(v*x).toFixed(2)]);series[1].pts.push([x,2*x]);} | |
| return page(i,'运动课 · '+t, body, | |
| chartLine(series,{xlabel:'时间(秒)',ylabel:'路程(米)'}), | |
| '图:路程-时间图像,斜率 = 速度', 'v = s ÷ t', | |
| '如果'+name+'保持这个速度走 '+(5*tSec)+' 秒,一共走多远?', | |
| 's = v·t ≈ '+fmt(v)+' × '+ (5*tSec) +' = '+fmt(v*5*tSec)+' 米。', | |
| '图上一条直线代表"匀速"。真实的汽车启动时速度从 0 慢慢变大,那条线该变成什么形状?(提示:先平缓后陡的曲线)'); | |
| } | |
| }, | |
| /* 3 力(初中→高中) 80页 */ | |
| { | |
| name:'力与牛顿定律', level:1, pages:80, intro:'推与拉、平衡与反作用——牛顿三大定律。', | |
| gen:function(i){ | |
| const r=rng(i+202); | |
| const m=2+(i%6), a=1+((i*3)%5); | |
| const k=8+((i*7)%20), x=5+(i%10); | |
| const F=m*a, Fs=k*x; | |
| const T=[['推一推:认识力','力是物体对物体的作用,有大小和方向。'], | |
| ['牛顿第二定律','力让物体改变运动状态:F = ma。质量越大,同样的力加速度越小。'], | |
| ['弹簧的秘密','把弹簧拉长 x 厘米,弹力 F 会变大——胡克定律告诉我们 F 与 x 成正比。'], | |
| ['摩擦力朋友','接触面越粗糙,摩擦力越大。雪橇在冰上滑行,摩擦力可以小到几乎为零。'], | |
| ['地球的重力','所有物体都被地球"拉"着:G = mg,g≈9.8 牛/千克。'], | |
| ['力的平衡','吊灯静止不动,是因为拉力的总和为零——合力为零就是平衡。'], | |
| ['反作用力','你蹬地,地也蹬你;火箭喷出气体,气体把火箭推上天。'], | |
| ['斜面上的力','把重力沿斜面"拆"开,下滑的分力约 mg·sinθ,角度越大越容易滑下去。'],][i%8]; | |
| const body='<p>'+T[1]+'</p>'+ | |
| '<p>例题:一个质量 m = '+m+' 千克的木箱,希望它的加速度为 '+a+' 米/秒²,需要多大的力?由 F = ma 得 F = '+m+' × '+a+' = <b>'+F+' 牛</b>。</p>'+ | |
| (i%8===2?('<p>再看弹簧:劲度系数 k = '+k+' 牛/米,伸长 '+x+' 厘米时,弹力 F = '+k+' × '+x/100+' = <b>'+fmt(Fs/100)+' 牛</b>。图中的直线过原点,斜率就是 k。</p>'):'')+ | |
| '<div class="tip">牛顿(1643–1727)用三条简单的定律,统一了地面与天上的运动规律。</div>'; | |
| let chart,cap; | |
| const mode=i%8; | |
| if(mode===0)chart=chartBar([{label:'轻推',value:5},{label:'中推',value:F},{label:'重推',value:F*2}],{unit:'N',xlabel:'推力'}), | |
| cap='图:同一木箱受到不同大小的推力'; | |
| else if(mode===1)chart=chartBar([{label:'m='+m+'kg,a='+a, value:F},{label:'m='+(m+3)+'kg, a='+a,value:(m+3)*a},{label:'m='+(m+1)+'kg, a='+(a*2),value:(m+1)*a*2}],{unit:'N',xlabel:'F=ma 的例子'}), | |
| cap='图:不同质量与加速度下所需的力'; | |
| else if(mode===2){const pts=[];for(let j=0;j<=8;j++)pts.push([j,+(k*j/100).toFixed(2)]); | |
| chart=chartLine([{label:'弹力 F=kx',pts:pts}],{xlabel:'伸长量 x(厘米)',ylabel:'弹力(牛)'}), | |
| cap='图:弹簧弹力与伸长量成正比(胡克定律)';} | |
| else if(mode===3)chart=chartBar([{label:'冰面',value:3},{label:'木地板',value:12},{label:'毛巾',value:18},{label:'砂纸',value:30}],{unit:'N',xlabel:'摩擦力'}), | |
| cap='图:同一木箱在不同表面受的摩擦力'; | |
| else if(mode===4)chart=chartGauge(m*9.8,100,'牛','重力 G=mg','质量 '+m+' kg · g=9.8 m/s²'); | |
| else if(mode===5)chart=chartBar([{label:'向下的重力',value:50},{label:'向上的拉力',value:50}],{unit:'N',xlabel:'平衡的吊灯'}),cap='图:大小相等、方向相反,合力为零'; | |
| else if(mode===6)chart=chartBar([{label:'人对滑板车',value:120},{label:'滑板车对人',value:120}],{unit:'N',xlabel:'作用力与反作用力'}),cap='图:成对出现,永远等大反向'; | |
| else {const pts=[];for(let d=0;d<=80;d+=8)pts.push([d,+(50*Math.sin(d*Math.PI/180)).toFixed(1)]); | |
| chart=chartLine([{label:'mg·sinθ',pts:pts}],[['θ=0',0]]?{}:{xlabel:'斜面倾角 θ(度)',ylabel:'下滑分力(牛)'}),cap='图:斜面倾角越大,下滑力越大'}; | |
| return page(i,'力学课 · '+T[0], body, chart, cap, | |
| mode<3?('F = m·a (牛顿第二定律)'):(mode===4?'G = m·g,g ≈ 9.8 牛/千克':'ΣF = 0 ⇔ 平衡'), | |
| '如果质量变成 2 倍、想让加速度不变,推力要怎样变?', | |
| 'F = ma,质量加倍则力也要加倍。', | |
| '滑冰运动员用力蹬冰面就能前进,但冰面对他的摩擦力为什么没有把他"拽住"?想想"静摩擦"和"相对滑动"的区别。'); | |
| } | |
| }, | |
| /* 4 压强与浮力(初中) 60页 */ | |
| { | |
| name:'压强与浮力', level:1, pages:60, intro:'水下的秘密:压强、浮力、阿基米德。', | |
| gen:function(i){ | |
| const r=rng(i+303); | |
| const A=[1,5,10,25][i%4]; | |
| const F=100, p=F/A; | |
| const depth=2+(i%8); | |
| const rho=1000, V0=(0.001+i%3000)/1000; | |
| const Fb=rho*9.8*V0; | |
| const T=['压力与压强','潜水有多深','会游泳的阿基米德','船为什么不会沉'][i%4]; | |
| const body='<p>同样的 100 牛压力,作用在 '+A+' cm² 的小面积上,压强是 <b>'+fmt(p)+' 千帕</b>;面积越大压强越小。这就是图钉尖容易扎进去的原因。</p>'+ | |
| '<p>在水下 '+depth+' 米深处,压强约 <b>'+fmt(rho*9.8*depth/1000)+' 千帕</b>(还加上 101 千帕的大气压)。潜水员每往下 10 米,就多加 1 个大气压!</p>'+ | |
| '<p>阿基米德原理:浸入水中的物体会受到向上的浮力,<b>F浮 = ρ液·g·V排</b>。排开 '+V0+' m³ 水,浮力约 '+fmt(Fb)+' 牛。</p>'+ | |
| '<div class="tip">鸡蛋放进盐水会浮起来,放进清水会沉下去——改变液体的密度就能改变沉浮!</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let a=1;a<=25;a+=2)pts.push([a,fmt(F/a)]); | |
| chart=chartLine([{label:'p=F/A',pts:pts}],[{}]),cap='图:面积越大,压强越小(反比例)';} | |
| else if(i%4===1){const pts=[];for(let d=0;d<=12;d++)pts.push([d,rho*9.8*d/1000+101]); | |
| chart=chartLine([{label:'压强',pts:pts}],[{}]),cap='图:水下压强随深度线性增大(千帕)';} | |
| else if(i%4===2)chart=chartBar([{label:'清水中木块',value:45},{label:'淡水中鸡蛋',value:55},{label:'盐水中鸡蛋',value:72}],{unit:'%',xlabel:'鸡蛋上浮的比例'}), | |
| cap='图:液体密度越大,鸡蛋浮得越高'; | |
| else chart=chartBar([{label:'船重(牛)',value:600000},{label:'排开水的重力(牛)',value:600000}],{unit:'',xlabel:'船为什么浮着'}),cap='图:船的重力 = 排开水的重力'; | |
| chart=chart||''; | |
| return page(i,'流体课 · '+T[0], body, | |
| (i%4===0||i%4===1)?chart:chart, cap, | |
| 'F浮 = ρ液·g·V排 (阿基米德原理)', | |
| '把同样一块铁捏成船形,它为什么能浮在水面?', | |
| '捏成船形后,它排开的水变多了,浮力增大,直到浮力等于重力。', | |
| '死海含盐量很高,人可以悠闲地漂在水面看书。用今天的公式估算一下:要让 60 kg 的人漂浮,水的密度至少要多少?'); | |
| } | |
| }, | |
| /* 5 声音(初中) 30页 */ | |
| { | |
| name:'奇妙的声音', level:1, pages:30, intro:'振动产生声音,频率决定音高。', | |
| gen:function(i){ | |
| const r=rng(i+404); | |
| const f=(50+ (i%10)*180).toFixed(0); | |
| const v=340, d=17+ (i%14)*8, t=2*d/340; | |
| const T=['音是从哪儿来的','音高由谁决定','声音跑得好慢','回声游戏'][i%4]; | |
| const body='<p>一切声音都来自<b>振动</b>:敲鼓时鼓面在振动,说话时声带在振动。振动停止,声音就消失了。</p>'+ | |
| '<p>每秒振动的次数叫<b>频率</b>,单位是赫兹(Hz)。'+f+' Hz 的声音比较低沉,而 '+(2*f)+' Hz 就明显尖锐了。猫狗能听到 1 万 Hz 以上的高音,人只能到 2 万 Hz 左右。</p>'+ | |
| '<p>声音在空气中每秒约 340 米,比光慢了近 100 万倍。所以先看到闪电、后听到雷声——'+d+' 米外的闪电,雷声 '+(t).toFixed(1)+' 秒才到。</p>'+ | |
| '<div class="tip">真空里不能传声:宇航员在太空里要靠无线电"喊话"。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let x=0;x<40;x++)pts.push([x,Math.sin(x*0.9)*50]); | |
| chart=chartLine([{label:'鼓面振动',pts:pts}],[{}]),cap='图:音波——振动在时间上的形状';} | |
| else if(i%4===1)chart=chartBar([{label:'大鼓 80Hz',value:80},{label:'二胡 440Hz',value:440},{label:'笛子 1200Hz',value:1200},{label:'狗叫 2200Hz',value:2200}],{unit:'Hz',xlabel:'不同声音的频率'}), | |
| cap='图:频率越高,音高越尖'; | |
| else if(i%4===2)chart=chartLine([{label:'声音(340m/s)',pts:[[0,0],[1,340],[2,680],[3,1020]]},{label:'光(约30万km/s)',pts:[[0,0],[1,300000]]}],[{xlabel:'时间(秒)',ylabel:'传播距离'}]), | |
| cap='图:光和声音的速度差距(对数感受)'; | |
| else chart=chartGauge(d,100,'米','声源距离','听到回声用了 '+(2*d/340).toFixed(1)+' 秒'); | |
| return page(i,'声学课 · '+T[0], body, chart, cap, | |
| '频率 f:振动次数/秒 · v声 ≈ 340 m/s', | |
| '把吉他的弦拧得紧一点,声音会变高还是变低?为什么?', | |
| '会变高。弦紧了振动加快,频率变大,所以音升高。', | |
| '蝙蝠靠超声波定位。为什么它不用 100 Hz 的音?想想"波长"和障碍物大小的关系。'); | |
| } | |
| }, | |
| /* 6 光与光学(初中→高中) 60页 */ | |
| { | |
| name:'光与光学', level:1, pages:60, intro:'直线传播、反射折射、透镜成像。', | |
| gen:function(i){ | |
| const r=rng(i+505); | |
| const th=(10+(i%8)*10); | |
| const u=20+ (i%8)*10, f=10, v=(f*u)/(u-f); | |
| const T=['光走直线','镜子里的你','光弯了腰','透镜魔法'][i%4]; | |
| const body='<p>光在均匀介质里<b>沿直线传播</b>:小孔成像、影子、日食都是它走直线的证据。</p>'+ | |
| '<p>光射到镜面上会发生<b>反射</b>:入射角永远等于反射角。图里我们测了不同入射角,看到反射角与它们一一相等。</p>'+ | |
| '<p>光从空气进入水中会<b>折射</b>(改变方向),所以筷子插进水里"断"了。用斯涅尔定律:n₁·sinθ₁ = n₂·sinθ₂。</p>'+ | |
| '<p>凸透镜的成像满足 <b>1/f = 1/u + 1/v</b>。物距 u = '+u+' cm、焦距 f = '+f+' cm 时,像距 v ≈ '+fmt(v)+' cm。</p>'+ | |
| '<div class="tip">光在真空中的速度 c = 299792458 米/秒——宇宙速度的天花板。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let d=0;d<=200;d+=10)pts.push([d,d/2]); | |
| chart=chartLine([{label:'小孔成像:像的位置',pts:pts}],[{xlabel:'物到孔的距离(cm)',ylabel:'像到孔的距离(cm)'}]), | |
| cap='图:物距等于像距时,像与物等大';} | |
| else if(i%4===1){const pts=[];for(let a=10;a<=80;a+=10)pts.push([a,a]); | |
| chart=chartLine([{label:'反射角=入射角 (对角线)',pts:pts}],[{xlabel:'入射角(度)',ylabel:'反射角(度)'}]), | |
| cap='图:反射角恒等于入射角';} | |
| else if(i%4===2){const pts=[];for(let a=10;a<=70;a+=10)pts.push([a,Math.asin(Math.sin(a*Math.PI/180)/1.5)*180/Math.PI]); | |
| chart=chartLine([{label:'折射角(空气→水)',pts:pts}],[{xlabel:'入射角(度)',ylabel:'折射角(度)'}]), | |
| cap='图:折射角小于入射角(斯涅尔定律)';} | |
| else{const pts=[];for(let uu=f+2;uu<=60;uu+=2)pts.push([uu,f*uu/(uu-f)]); | |
| chart=chartLine([{label:'像距 v(u,f=10cm)',pts:pts}],[{xlabel:'物距 u(cm)',ylabel:'像距 v(cm)'}]), | |
| cap='图:凸透镜成像规律,u→f 时 v→∞'}; | |
| return page(i,'光学课 · '+T[0], body, chart, cap, | |
| i%4===3?'1/f = 1/u + 1/v(透镜公式)':'n₁·sinθ₁ = n₂·sinθ₂(反射:θ₁=θ₂)', | |
| '把手机手电筒照向墙上,光线散开是因为什么?(提示:光的衍射与遮挡物大小)', | |
| '手电筒的透镜让光线近似平行,边缘的光线仍会略微发散。', | |
| '游泳池底看起来比实际浅,是折射的什么方向造成的?试着画光路图解释。'); | |
| } | |
| }, | |
| /* 7 热学(初中→高中) 60页 */ | |
| { | |
| name:'热学', level:1, pages:60, intro:'热量、比热容、物态变化。', | |
| gen:function(i){ | |
| const r=rng(i+606); | |
| const m=1+ (i%4)*0.5, c=4200, dT=20+(i%6)*5, Q=m*c*dT; | |
| const T=['温度计会说话','为什么水特别"耐热"','冰去哪儿了','热量的三种旅行'][i%4]; | |
| const body='<p>温度描述物体的冷热程度。一杯 '+dT+'℃ 的水要降到 20℃,会放出大量热量。</p>'+ | |
| '<p><b>比热容</b>c 是物质的"储热能力":水的 c=4200 J/(kg·℃),是同质量铝的约 9 倍!所以沿海地区昼夜温差小,沙漠里昼夜温差大。</p>'+ | |
| '<p>算一算:把 '+m+' kg 的水升高 '+dT+'℃ 需要 Q = cmΔT = <b>'+fmt(Q/1e6)+' MJ</b>,这就是为什么烧开一壶水要花好几分钟。</p>'+ | |
| '<p>冰 0℃ 融化成水,温度不变,却一直在<b>吸热</b>——这叫"潜热"。图中冰水混合物的平台段就是融化过程。</p>'+ | |
| '<div class="tip">热传导(金属勺)、对流(热水锅)、热辐射(晒太阳)——这是热量的三条旅行路线。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let t=0;t<=60;t+=3)pts.push([t,20+ (dT)*Math.exp(-t/12)]); | |
| chart=chartLine([{label:'热水冷却曲线',pts:pts}],{xlabel:'时间(分钟)',ylabel:'温度(℃)'}),cap='图:热水自然冷却(指数衰减)';} | |
| else if(i%4===1)chart=chartBar([{label:'水',value:4200},{label:'铝',value:890},{label:'铁',value:450},{label:'铜',value:385}],{unit:'',xlabel:'比热容 c [J/(kg·℃)]'}), | |
| cap='图:单位质量升高1℃需要的热量'; | |
| else if(i%4===2){const pts=[[-10,0],[-5,12.5],[0,25],[0,25],[10,37.5],[100,925],[100,925]]; | |
| pts.forEach(function(p){p[1]=p[1]/10;}); | |
| chart=chartLine([{label:'冰→水→蒸汽',pts:pts}],[{xlabel:'温度(℃)',ylabel:'吸收热量(×100J)'}]), | |
| cap='图:冰水加热曲线,平台段 = 相变吸热';} | |
| else chart=chartBar([{label:'热传导(勺)',value:30},{label:'对流(热水)',value:50},{label:'热辐射(太阳光)',value:20}],{unit:'%',xlabel:'热量传递途径(示意占比)'}),cap='图:不同场景下热传递的主要方式'; | |
| return page(i,'热学课 · '+T[0], body, chart, cap, 'Q = c·m·ΔT (热量公式)', | |
| '用铜勺和木勺同时放在热水里,哪把烫手快?用比热容解释。', | |
| '铜比热容低、导热好,热量很快传到勺柄。', | |
| '为什么夏天游泳上岸后一阵风吹过会觉得冷?(提示:蒸发吸热)'); | |
| } | |
| }, | |
| /* 8 电学与电路(高中) 80页 */ | |
| { | |
| name:'电学与电路', level:2, pages:80, intro:'欧姆定律、电路、电功率。', | |
| gen:function(i){ | |
| const r=rng(i+707); | |
| const R=5+ (i%6)*5, I=(0.5+(i%8)*0.25), V=I*R; | |
| const T=['让灯泡发光','欧姆定律','串并联大比拼','省电小卫士'][i%4]; | |
| const body='<p>电流 I 是电荷的"集体散步"。电压 U 是"推力",电阻 R 是"阻力"。三者关系由<b>欧姆定律</b>给出:U = I·R。</p>'+ | |
| '<p>实验:'+I+' A 电流流过 '+R+' Ω 的电阻,两端电压 U = IR = <b>'+fmt(V)+' V</b>。改变电流,图上的直线不过零吗?它会经过原点。</p>'+ | |
| '<p>串联电阻相加:R串 = ΣR;并联电阻倒数相加:1/R并 = Σ(1/R)。并联总电阻比任何一支都小。</p>'+ | |
| '<p>电功率 P = UI:一台 '+fmt(V)+' V、'+I+' A 的电器功率是 <b>'+fmt(V*I)+' W</b>,每小时耗电 '+(V*I/1000).toFixed(3)+' kWh(度)。</p>'+ | |
| '<div class="tip">家里插座的标称是 220 V、50 Hz 的交流电,而手机充电口是 5 V 直流——里面的"开关电源"负责降压+整流。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let ii=0;ii<=2;ii+=0.25)pts.push([ii,ii*R]); | |
| chart=chartLine([{label:'R='+R+'Ω',pts:pts}],{xlabel:'电流 I(A)',ylabel:'电压 U(V)'}), | |
| cap='图:I–U 直线斜率就是电阻(欧姆定律)';} | |
| else if(i%4===1)chart=chartBar([{label:'R1='+R+'Ω',value:R},{label:'R2='+(2*R)+'Ω',value:2*R},{label:'R3='+(3*R)+'Ω',value:3*R}],{unit:'Ω',xlabel:'单个电阻'}), | |
| cap='图:几个不同阻值的电阻'; | |
| else if(i%4===2){ | |
| const R2=2*R, Rpar=(R*R2)/(R+R2); | |
| chart=chartBar([{label:'串联',value:R+R2},{label:'并联',value:Rpar}],{unit:'Ω',xlabel:'组合方式'}), | |
| cap='图:串联更大、并联更小';} | |
| else{const pts=[];for(let ii=0;ii<=2;ii+=0.25)pts.push([ii,ii*V]); | |
| chart=chartLine([{label:'P=UI',pts:pts}],{xlabel:'电流(A)',ylabel:'功率(W)'}),cap='图:功率随电流线性增大';} | |
| return page(i,'电学课 · '+T[0], body, chart, cap, 'U = I·R · P = U·I', | |
| '把两只相同灯泡改成并联后,每只灯会变得更亮还是更暗?', | |
| '电源内阻可忽略时电压不变,每只灯功率不变;但总功率变大,电池更费电。', | |
| '为什么家庭电路采用并联,而不是串联?想想串联时一个用电器断开会发生什么。'); | |
| } | |
| }, | |
| /* 9 磁学(高中) 40页 */ | |
| { | |
| name:'磁学', level:2, pages:40, intro:'磁场看不见却推得动。', | |
| gen:function(i){ | |
| const r=rng(i+808); | |
| const B=(0.1+(i%6)*0.2).toFixed(1), L=(0.1)+(i%5)*0.05, I=2+(i%4); | |
| const F=B*I*L; | |
| const T=['看不见的手','让指针转动','电磁的魔法','磁在发动机里'][i%4]; | |
| const body='<p>磁铁周围存在<b>磁场</b>,指南针的小磁针会被它扭转而指向磁场方向。</p>'+ | |
| '<p>磁场虽然看不见,但能<b>施力</b>:通电导线在磁场中受到安培力 F = BIL,方向由左手定则决定。</p>'+ | |
| '<p>算一算:B = '+B+' T、I = '+I+' A、L = '+fmt(L)+' m 时,F = <b>'+fmt(F)+' N</b>。电动机就是把这个力放大,让转子转起来。</p>'+ | |
| '<p>两块永久磁铁相对,磁力线会彼此"挤"或"拉",形成对称的图案——下一节我们会用电画同样的箭头场。</p>'+ | |
| '<div class="tip">地磁场让指南针工作,也保护地球免受太阳风(带电粒子)的直接轰击。</div>'; | |
| let chart,cap; | |
| if(i%4===0)chart=chartField('dipole','图:磁偶极场(N-S 极之间的磁场)'),cap='图:条形磁铁的磁场方向(箭头示意)'; | |
| else if(i%4===1)chart=chartGauge(B/1*30,100,'毫特斯拉','B = '+B+' T 磁场'),cap='图:磁场强弱示意(特斯拉的千分之一)'; | |
| else if(i%4===2){const pts=[];for(let ii=0;ii<=5;ii+=0.5)pts.push([ii,B*L*ii]); | |
| chart=chartLine([{label:'F=BIL (B='+B+',L='+fmt(L)+')',pts:pts}],{xlabel:'电流 I(A)',ylabel:'安培力 F(N)'}), | |
| cap='图:安培力与电流成正比';} | |
| else{const pts=[];for(let a=0;a<=360;a+=15)pts.push([a,100*Math.sin(a*Math.PI/180)]); | |
| chart=chartLine([{label:'转子力矩随转角',pts:pts}],{xlabel:'转子角度(度)',ylabel:'力矩(任意单位)'}),cap='图:电动机转矩随转子角度正弦变化'}; | |
| return page(i,'磁学课 · '+T[0], body, chart, cap, | |
| 'F = B·I·L (安培力,B⊥I)', | |
| '把通电导线垂直换成平行放入磁场,安培力会怎样?(提示:方向与 B、I 的夹角)', | |
| 'F = BIL·sinθ,平行时 θ=0,力变为零。', | |
| '地球没有裸露的"磁极",为什么指南针依然南北指向?想想地核的"液体金属"在做什么。'); | |
| } | |
| }, | |
| /* 10 功与能量(高中) 70页 */ | |
| { | |
| name:'功与能量', level:2, pages:70, intro:'能量的形式、转化与守恒。', | |
| gen:function(i){ | |
| const r=rng(i+909); | |
| const m=2+(i%5), v=(3+(i%6)), h=2+(i%4); | |
| const W=m*9.8*h, Ek=0.5*m*v*v, Ep=m*9.8*h; | |
| const T=['推开世界的力量','动能有多"猛"','重力势能','能量守恒'][i%4]; | |
| const body='<p><b>功</b>是力在位移上"做功"的多少:W = F·s。用力推一个不动的箱子,虽然出了汗,但物理上不叫做功。</p>'+ | |
| '<p>把 '+m+' kg 的东西举高 '+h+' m,做功 W = mgh = <b>'+fmt(W)+' J</b>,它变成了<b>重力势能</b>。</p>'+ | |
| '<p><b>动能</b> Ek = ½mv²。一辆 '+m+' kg 的小车以 '+v+' m/s 运动,动能是 <b>'+fmt(Ek)+' J</b>;速度翻倍,动能变成 4 倍——这就是高速碰撞更危险的原因。</p>'+ | |
| '<p>能量不会消失,只会变形:滑梯上的小朋友从顶端滑到地面,势能变动能+内能(屁股发热)。</p>'+ | |
| '<div class="tip">1 千卡(食物能量)= 4184 J。你刚才做的功,大约相当于半粒米饭的能量。</div>'; | |
| let chart,cap; | |
| if(i%4===0)chart=chartBar([{label:'W=mgh ('+h+'m)',value:W},{label:'W=push 10N×2m',value:20},{label:'W=摩擦 50N×0.5m',value:25}],{unit:'J',xlabel:'做了几种功'}),cap='图:几种做功场景对比(焦耳)'; | |
| else if(i%4===1){const pts=[];for(let vv=0;vv<=10;vv+=1)pts.push([vv,0.5*m*vv*vv]); | |
| chart=chartLine([{label:'Ek=½·'+m+'kg·v²',pts:pts}],{xlabel:'速度 v(m/s)',ylabel:'动能(J)'}),cap='图:动能随速度平方增长';} | |
| else if(i%4===2)chart=chartLine([{label:'Ep=mg·h',pts:[[0,0],[1,m*9.8],[2,m*19.6],[3,m*29.4]]}],[{xlabel:'高度 h(m)',ylabel:'势能(J)'}]),cap='图:势能与高度成正比'; | |
| else chart=chartPie([{label:'势能',value:Ep,color:'#2563eb'},{label:'动能',value:Ek,color:'#16a34a'},{label:'内能',value:Math.max(5,W-Ek),color:'#d97706'}]),cap='图:滑梯末端三种能量分布示意'; | |
| return page(i,'能量课 · '+T[0], body, chart, cap, | |
| 'W=F·s · Ek=½mv² · Ep=mgh', | |
| '同样 60 kg 的人,从 3 楼和从 30 楼跳下,落地速度差多少?(不计空气阻力)', | |
| 'v=√(2gh),30楼≈3.16倍高度→速度约1.78倍大,动能约为3.16倍。', | |
| '汽车刹车时,动能到哪里去了?(提示:轮胎与路面发热+形变)'); | |
| } | |
| }, | |
| /* 11 动量与碰撞(高中) 60页 */ | |
| { | |
| name:'动量与碰撞', level:2, pages:60, intro:'p=mv,碰撞里什么不变?', | |
| gen:function(i){ | |
| const r=rng(i+1011); | |
| const m=1+(i%4), v=2+(i%3)*2; | |
| const p=m*v; | |
| const T=['动量的大小','冲量:力作用的时间','弹球与泥巴','火箭的魔法'][i%4]; | |
| const body='<p><b>动量</b>p = mv 是"运动的量"。同样质量,速度越快动量越大;同样速度,质量越大越"难停"。</p>'+ | |
| '<p>力作用在时间上叫<b>冲量</b>:I = F·Δt = Δp。接住保龄球时弯膝盖,就是为了延长碰撞时间、减小冲击力。</p>'+ | |
| '<p>碰撞中<b>动量守恒</b>是自然界的铁律:m₁v₁+m₂v₂ = m₁v₁′+m₂v₂′。两个 '+m+' kg 的小球以 '+v+' m/s 相撞,总动量不变。</p>'+ | |
| '<p>火箭在真空里前进,靠的是喷出高速气体获得反冲动量——动量守恒的另一面。</p>'+ | |
| '<div class="tip">安全气囊、汽车溃缩区、接球时手后退——全是在用"时间"换"力"。</div>'; | |
| let chart,cap; | |
| if(i%4===0)chart=chartBar([{label:'m='+m+',v='+v,value:p},{label:'m='+m+',v='+(2*v),value:m*2*v},{label:'m='+(2*m)+',v='+v,value:2*m*v}],{unit:'kg·m/s',xlabel:'动量 p=mv'}), | |
| cap='图:动量与速度、质量成正比'; | |
| else if(i%4===1){const pts=[[0,0],[0,p],[1,0],[1,0],[2,p],[2,p],[2,0]]; | |
| chart=chartLine([{label:'小车 p(t)(冲量示意)',pts:pts}],{xlabel:'时间(s)',ylabel:'动量(kg·m/s)'}),cap='图:碰撞前后动量分段跃变,冲量改变动量';} | |
| else if(i%4===2){ | |
| const m2=2*m, v2=v, v1p=(2*m2*v2)/(m+m2), v2p=(m*v+m2*v2)/(m+m2); | |
| chart=chartBar([{label:'碰前 p1='+m+'kg',value:m*v},{label:'碰前 p2='+m2+'kg',value:m2*v2},{label:'碰后 p1′',value:m*v1p},{label:'碰后 p2′',value:m2*v2p}],{unit:'kg·m/s',xlabel:'两球动量(完全非弹碰前后)'}), | |
| cap='图:非弹碰:动能有损,但动量守恒';} | |
| else{const pts=[];for(let t=0;t<=30;t+=2)pts.push([t,(100*(1-Math.exp(-t/12))).toFixed(1)]); | |
| chart=chartLine([{label:'火箭获得的动量(示意)',pts:pts}],{xlabel:'喷气时间(s)',ylabel:'动量(示意)'}),cap='图:火箭动量随喷气时间累积'}; | |
| return page(i,'动量课 · '+T[0], body, chart, cap, | |
| 'p = m·v · I = F·Δt = Δp · Σp碰前 = Σp碰后', | |
| '一个 5 kg 的孩子以 3 m/s 跑向 5 kg 的静止沙坑,落地后一起静止。谁提供了反向的冲量?', | |
| '沙坑(地面)给孩子和沙坑整体的动量提供了反向冲量。', | |
| '台球碰撞时,母球和子球几乎都沿同一直线运动;如果两球质量相同、完全弹性,碰后母球几乎停下、子球以原速度前进——为什么?'); | |
| } | |
| }, | |
| /* 12 万有引力与航天(高中→大学) 60页 */ | |
| { | |
| name:'万有引力与航天', level:2, pages:60, intro:'从苹果到轨道,引力的全貌。', | |
| gen:function(i){ | |
| const r=rng(i+1112); | |
| const Gm=3.986e14; | |
| const R=(6371000 + (i%10)*1e6); | |
| const vc=Math.sqrt(Gm/R); | |
| const ve=Math.sqrt(2*Gm/R); | |
| const T=['苹果的启示','轨道是怎么来的','三速度','潮汐与月'][i%4]; | |
| const body='<p>牛顿发现:让苹果落地的力,和拉住月球的力,是<b>同一种力</b>——万有引力 F = G·M·m / r²。</p>'+ | |
| '<p>把物体水平"扔"得足够快,它落下的曲线恰好追上地球弯曲的地面——这就是<b>轨道</b>。近地轨道速度约 <b>'+fmt(vc/1000)+' km/s</b>。</p>'+ | |
| '<p>三个速度:第一宇宙(环绕)≈ 7.9、第二宇宙(逃逸)≈ 11.2、第三宇宙(飞出太阳系)≈ 16.7 km/s。</p>'+ | |
| '<p>月球引潮力让海水周期性起伏,形成<b>潮汐</b>;这也是地球自转缓慢变慢的"刹车"。</p>'+ | |
| '<div class="tip">太空中的宇航员"失重",并不是没有引力——地球的引力在 400 km 高处仍然有 90% 以上!</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let rr=1;rr<=10;rr++)pts.push([rr,+(1/(rr*rr)).toFixed(4)]); | |
| chart=chartLine([{label:'引力 ∝ 1/r²',pts:pts}],{xlabel:'r / (地球半径)',ylabel:'相对引力(示意)'}),cap='图:引力随距离平方衰减';} | |
| else if(i%4===1)chart=chartOrbit(),cap='图:太阳系行星轨道示意(周期不同)'; | |
| else if(i%4===2)chart=chartBar([{label:'环绕',value:7.9},{label:'逃逸',value:11.2},{label:'太阳系逃逸',value:16.7}],{unit:'km/s',xlabel:'三个特殊速度'}), | |
| cap='图:地球表面附近的三个宇宙速度'; | |
| else{const pts=[];for(let t=0;t<=100;t+=4)pts.push([t,Math.sin(t/15.9)*5]); | |
| chart=chartLine([{label:'半日潮(示意)',pts:pts}],{xlabel:'时间(小时)',ylabel:'水位(米,示意)'}),cap='图:潮汐的周期振荡示意'}; | |
| return page(i,'天体课 · '+T[0], body, chart, cap, | |
| 'F = G·M·m/r² · v环 = √(GM/r)', | |
| '为什么绕地球轨道运行的人造卫星会"永远掉不下去"?用"不断下坠却追不上地面"来解释。', | |
| '它以合适的初速度切线运动,重力只改变方向不改变速度大小——持续做向心运动。', | |
| '如果把地球压缩到太阳一样大的质量但半径不变,表面重力会怎样?(想想 g=GM/r²)'); | |
| } | |
| }, | |
| /* 13 振动与波(高中) 50页 */ | |
| { | |
| name:'振动与波', level:2, pages:50, intro:'秋千、音叉、水波——周期运动的世界。', | |
| gen:function(i){ | |
| const r=rng(i+1213); | |
| const L=(0.5+(i%5)*0.3), T=2*Math.sqrt(L/9.8); | |
| const A=2+(i%4), f=(0.5+(i%7)*0.2); | |
| const T0=['摆的规律','波在传播','共振的尖叫','声音里的能量'][i%4]; | |
| const body='<p><b>振动</b>是物体绕过平衡位置的往复运动。单摆做小角度振动,周期 T = 2π√(L/g),与摆动幅度几乎无关。</p>'+ | |
| '<p>一根 '+L.toFixed(1)+' m 的摆,周期约 <b>'+T.toFixed(2)+' 秒</b>。这就是"惠更斯的秒摆"——17 世纪用来做时钟的。</p>'+ | |
| '<p><b>波</b>是振动的传播:频率 f 与波长 λ 满足 v = f·λ。声波在空气中约 340 m/s,水波慢得多。</p>'+ | |
| '<p><b>共振</b>发生时振幅会迅速变大——1931 年塔科马大桥在大风中被"吹"塌,就是振幅累积的经典案例。</p>'+ | |
| '<div class="tip">荡秋千时人不断在最低点站起来、站下去,就是靠"参数共振"不断加能量,越荡越高。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let t=0;t<=40;t+=0.5)pts.push([t,A*Math.cos(t/T*Math.PI*2)]); | |
| chart=chartLine([{label:'x=A·cos(ωt)',pts:pts}],{xlabel:'时间 t(s)',ylabel:'位移 x(m)'}),cap='图:单摆的简谐振动(余弦曲线)';} | |
| else if(i%4===1){const pts=[];for(let x=0;x<=20;x+=0.2)pts.push([x,Math.sin(x*f*Math.PI)*A]); | |
| chart=chartLine([{label:'y=A·sin(kx)',pts:pts}],{xlabel:'位置 x(m)',ylabel:'波位移(m)'}),cap='图:行波在某一时刻的空间形状';} | |
| else if(i%4===2){const pts=[];for(let ff=0.1;ff<=5;ff+=0.1)pts.push([ff,Math.exp(-Math.pow((ff-2.5)/0.35,2))*A]); | |
| chart=chartLine([{label:'共振曲线',pts:pts}],{xlabel:'驱动频率 f(Hz)',ylabel:'振幅(m)'}),cap='图:驱动力接近固有频率时振幅最大';} | |
| else chart=chartBar([{label:'空气声 340m/s',value:340},{label:'水中声 1480m/s',value:1480},{label:'钢中声 5000m/s',value:5000}],{unit:'m/s',xlabel:'波在不同介质中的速度'}),cap='图:同一波在不同介质中传播速度'; | |
| return page(i,'波动课 · '+T0[0], body, chart, cap, | |
| 'T = 2π·√(L/g) · v = f·λ', | |
| '把秋千绳从 3 m 剪到 0.75 m,摆动周期会变成原来的几分之几?', | |
| 'T∝√L,长度变为 1/4,周期变为 1/2。', | |
| '为什么共振既有用(收音机调台、吉他共鸣)又危险(桥梁、机械)?'); | |
| } | |
| }, | |
| /* 14 电磁场(大学) 70页 */ | |
| { | |
| name:'电磁场(大学)', level:3, pages:70, intro:'库仑、高斯、电场——把电"几何化"。', | |
| gen:function(i){ | |
| const r=rng(i+1314); | |
| const q=2e-6, R=0.1+ (i%5)*0.05; | |
| const kq=8.99*q, E=kq/(0.2*R*R); | |
| const V=kq/(0.2); | |
| const T=['库仑定律','画电场线','电势与场','电容的几何'][i%4]; | |
| const body='<p>库仑发现:<b>F = k·q₁q₂/r²</b>,与万有引力同是平方反比——这是 18 世纪最优雅的物理巧合之一。</p>'+ | |
| '<p><b>电场</b>把"电荷对电荷的作用"改写成了"电荷对空间,空间再作用电荷"。电场线从正电荷出发、终止于负电荷,疏密表示字段大小。</p>'+ | |
| '<p>电势 V 是"单位电荷的能量",满足 V = kq/r;电场是电势的梯度:E = −dV/dr。图上是点电荷的 V–r 曲线。</p>'+ | |
| '<p>两块平行金属板构成<b>电容器</b>,板间近似匀强电场 E = U/d,储能量 W = ½·C·U²。</p>'+ | |
| '<div class="tip">高斯定律 ∮E·dS = Q/ε₀ 让"算电场"变成只数"穿过封闭面的通量",是麦克斯韦方程组的四条之一。</div>'; | |
| let chart,cap; | |
| if(i%4===0)chart=chartField('dipole','图:电偶极子电场'),cap='图:正负电荷之间的电场(箭头表示方向)'; | |
| else if(i%4===1)chart=chartField('radial','图:正点电荷的径向电场'),cap='图:点电荷的径向电场,越远越稀疏(E↓)'; | |
| else if(i%4===2){const pts=[];for(let rr=0.05;rr<=1;rr+=0.05)pts.push([rr,kq/(rr*0.2)]); | |
| chart=chartLine([{label:'V = kq/r (q=2μC)',pts:pts}],{xlabel:'距离 r(m)',ylabel:'电势 V(伏)'}),cap='图:点电荷电势随距离按 1/r 衰减';} | |
| else{const pts=[];for(let d=1;d<=10;d+=1)pts.push([d,1000/d]); | |
| chart=chartLine([{label:'U=Ed(固定E)',pts:[[1,100],[2,200],[3,300]]},{label:'E=U/d(固定U)',pts:pts}],{xlabel:'板间距 d(×10⁻² m)',ylabel:'伏特 / 伏每米'}),cap='图:电容器两端电压与板间距关系(两种情形)'}; | |
| return page(i,'电磁场课 · '+T[0], body, chart, cap, | |
| 'E = k·q/r² · V = k·q/r · ∮E·dS = Q/ε₀', | |
| '两个相同正电荷,把它们之间的距离缩短到一半,相互作用力变为原来的几倍?', | |
| 'F∝1/r²,距离减半,力变为 4 倍。', | |
| '为什么"沿任意闭合曲线积分 E·dl = 0"(静电场保守)?这对电势的定义意味着什么?'); | |
| } | |
| }, | |
| /* 15 电磁感应与麦克斯韦(大学) 70页 */ | |
| { | |
| name:'电磁感应与麦克斯韦', level:3, pages:70, intro:'法拉第定律:变化的磁场产生电。', | |
| gen:function(i){ | |
| const r=rng(i+1415); | |
| const N=10+(i%8)*10, A=0.02, dB=0.5, t=0.5; | |
| const emf=N*A*dB/t; | |
| const T=['让磁铁动一下','交变电流','变压器','麦克斯韦的方程'][i%4]; | |
| const body='<p>法拉第(1831)发现:<b>变化的磁通量</b>在线圈中"生出"电动势 ε = −N·ΔΦ/Δt。负号就是楞次定律的方向信息。</p>'+ | |
| '<p>例子:N = '+N+' 匝线圈,面积 '+A+' m²,磁场在 '+t+' s 内从 0 增加到 '+dB+' T,则 ε ≈ <b>'+fmt(emf)+' V</b>。</p>'+ | |
| '<p>交变电流 i = I₀sin(ωt),频率 f = ω/2π。发电机就是把机械能持续"搅拌"磁场。</p>'+ | |
| '<p>变压器利用互感:U₁/U₂ ≈ N₁/N₂。高压输电把电压提高、电流降低,损耗 P=I²R 大幅下降。</p>'+ | |
| '<p>麦克斯韦补齐第四项(位移电流),预言了光就是电磁波——1865 年,26 年后赫兹在实验中看到火花。</p>'+ | |
| '<div class="tip">"变化的磁场产生电场"与"变化的电场产生磁场"互为因果,让波在真空中也能自我推进以光速 c 传播。</div>'; | |
| let chart,cap; | |
| if(i%4===0){ | |
| const flux=[];for(let tt=0;tt<=4;tt+=0.5)flux.push([tt,Math.max(0,0.5*tt)]); | |
| chart=chartLine([{label:'磁通量 Φ(t)',color:'#2563eb',pts:flux},{label:'电动势 ε∝−dΦ/dt',color:'#dc2626',pts:[[0,-0.5],[4,-0.5]]}],{xlabel:'时间(s)',ylabel:'示意'}), | |
| cap='图:Φ 恒定时,ε 为常数(负号表示方向)';} | |
| else if(i%4===1){const pts=[];for(let t2=0;t2<=2;t2+=0.02)pts.push([t2,Math.sin(2*Math.PI*50*t2)]); | |
| chart=chartLine([{label:'i=I₀sin(2π·50·t)(50Hz)',pts:pts}],{xlabel:'时间 t(s)',ylabel:'电流(A)'}),cap='图:交流电按正弦变化(50Hz)';} | |
| else if(i%4===2){const pts=[];for(let n=10;n<=200;n+=10)pts.push([n,220*n/10]); | |
| chart=chartLine([{label:'U₂ = U₁·N₂/N₁ (N₁=10)',pts:pts}],{xlabel:'副边匝数 N₂',ylabel:'输出电压 V₂'}),cap='图:输出电压与副边匝数成正比';} | |
| else{const pts=[];for(let ff=1;ff<=10;ff++)pts.push([ff,3e8/Math.PI/ff/15]); | |
| chart=chartLine([{label:'波长 λ = c/f (示意)',pts:pts}],{xlabel:'频率 f(示意)',ylabel:'波长(示意)'}),cap='图:电磁波谱——频率与波长成反比'}; | |
| return page(i,'电磁感应课 · '+T[0], body, chart, cap, | |
| 'ε = −N·ΔΦ/Δt · λf = c', | |
| '把一块磁铁快速插进线圈,感应电流会流向哪个方向?(用楞次定律描述"阻碍"的方向。)', | |
| '感应电流的方向总是使磁通量"想回到原来"——与磁铁运动方向相反地建立磁场。', | |
| '为什么高压输电要不断变换电压(升—运输—降)?用 P=I²R 定量估算电压提高 10 倍对损耗的影响。'); | |
| } | |
| }, | |
| /* 16 热力学与统计(大学) 50页 */ | |
| { | |
| name:'热力学与统计', level:3, pages:50, intro:'温度、熵、理想气体——从宏观到微观。', | |
| gen:function(i){ | |
| const r=rng(i+1516); | |
| const n=1, R2=8.314, T2=300, V2=0.024; | |
| const P2=n*R2*T2/V2; | |
| const eff=1-273/(373+i%50); | |
| const T=['pV=const 的曲线','理想气体','热机效率','麦克斯韦分布'][i%4]; | |
| const body='<p>17 世纪波义耳发现:<b>一定量气体在恒温下 pV = 常数</b>,图像是等双曲线。</p>'+ | |
| '<p><b>理想气体</b>状态方程 pV = nRT 是微观与宏观之间的桥梁:n 个分子,每个平均动能 (3/2)kT。</p>'+ | |
| '<p>热机只能把一部分热量变成功:<b>效率 η = 1 − T冷/热</b>。卡诺效率是理论上限,真实发动机都低于它。</p>'+ | |
| '<p>统计物理告诉我们:温度是分子平均平动动能的量度,压强是分子撞击器壁的集体效果。</p>'+ | |
| '<p><b>麦克斯韦-玻尔兹曼分布</b>描述气体分子速度的概率分布——速度分布"钟形"且随温度变宽。</p>'+ | |
| '<div class="tip">玻尔兹曼墓碑上刻着 S = k·ln W —— 熵是微观状态数的对数,"往概率大的地方走"就是热力学第二定律。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let v=0.01;v<=0.12;v+=0.01)pts.push([v,0.00248/v]); | |
| chart=chartLine([{label:'等温线 pV=常数',pts:pts}],{xlabel:'体积 V(m³)',ylabel:'压强(kPa)'}),cap='图:p–V 图上等温线(双曲线)';} | |
| else if(i%4===1)chart=chartBar([{label:'H₂',value:2886},{label:'N₂',value:10766},{label:'CO₂',value:15868}],{unit:'J/(kmol·K)',xlabel:'摩尔气体常数 R(不同气体)'}),cap='图:R 与分子种类无关(理想气体)'; | |
| else if(i%4===2){const pts=[];for(let th=300;th<=600;th+=30)pts.push([th,100*(1-273/th)]); | |
| chart=chartLine([{label:'η=1−T冷/T热',pts:pts}],{xlabel:'热源温度 T热(K)',ylabel:'效率(%)'}),cap='图:卡诺效率随热源温度升高而提高';} | |
| else{ | |
| const peak=350, vals=[];for(let v2=0;v2<900;v2+=20)vals.push([v2,80*Math.exp(-Math.pow((v2-peak)/260,2))]); | |
| const s2=[ | |
| {label:'300K',color:'#2563eb',pts:vals.map(p=>[p[0],p[1]])}, | |
| {label:'500K',color:'#dc2626',pts:vals.map(p=>[p[0]+60,p[1]*0.82])} | |
| ]; | |
| chart=chartLine(s2,{xlabel:'分子速度 v(×10³ m/s)',ylabel:'相对概率密度'});cap='图:麦克斯韦-玻尔兹曼速度分布示意'; | |
| } | |
| return page(i,'热力学课 · '+T[0], body, chart, cap, | |
| i%4===0?'pV = nRT(理想气体)':'效率 η = 1 − T冷/热(卡诺) · S = k ln W', | |
| '把 1 L 空气从 300 K 加热到 600 K(体积不变),压强变化几倍?', | |
| 'p∝T,变为 2 倍。', | |
| '"水总自发从高处流向低处"与"热量总自发从高温流向低温"有什么共同结构?'); | |
| } | |
| }, | |
| /* 17 相对论与量子(大学) 50页 */ | |
| { | |
| name:'相对论与量子', level:3, pages:50, intro:'时空弯曲、波粒二象性、不确定性。', | |
| gen:function(i){ | |
| const r=rng(i+1617); | |
| const beta=(0.3+(i%5)*0.1).toFixed(1); | |
| const gam=1/Math.sqrt(1-beta*beta); | |
| const h2=6.626e-34, f=5e14, E2=h2*f; | |
| const T=['时间变慢了','质能一体','波粒二象','不确定性'][i%4]; | |
| const body='<p>爱因斯坦(1905)把"光速对所有观察者相同"推到极致,得出<b>时间变慢、长度缩短</b>的结论:γ = 1/√(1−v²/c²)。</p>'+ | |
| '<p>速度为 '+beta+'c 时,γ ≈ <b>'+gam.toFixed(2)+'</b>——运动时钟走得慢,这是 GPS 必须修正的真实效应。</p>'+ | |
| '<p><b>E = mc²</b>:质量本身就是一种能量。1 g 物质蕴含的能量约 9×10¹³ J,足够一座城市用一天。</p>'+ | |
| '<p>光子既是粒子又是波:E = hf。频率 '+f.toExponential(2)+' Hz 的光子,能量约 '+E2.toExponential(2)+' J。光电效应是"一份一份"能量交付的铁证(爱因斯坦获 1921 年诺奖)。</p>'+ | |
| '<p><b>不确定性原理</b>:Δx·Δp ≥ ħ/2。位置测得越准,动量越"不确定"——不是仪器不好,而是自然如此。</p>'+ | |
| '<div class="tip">薛定谔方程 iℏ·∂ψ/∂t = Ĥψ 取代牛顿定律,成为微观世界的主方程;它的解给出概率波而非轨迹。</div>'; | |
| let chart,cap; | |
| if(i%4===0){const pts=[];for(let bb=0;bb<=0.99;bb+=0.05)pts.push([bb,1/Math.sqrt(1-bb*bb)]); | |
| chart=chartLine([{label:'γ(v/c)',pts:pts}],{xlabel:'速度 v / c',ylabel:'洛伦兹因子 γ'}),cap='图:洛伦兹因子 γ 在接近 c 时发散';} | |
| else if(i%4===1)chart=chartGauge(gam,3,'倍','γ = '+gam.toFixed(2),beta+'c 时的时间膨胀因子'),cap='图:该速度下时钟变慢倍率(示意)'; | |
| else if(i%4===2){ | |
| const kc=[[1,1],[2,4],[3,9],[4,16],[5,25],[6,36],[7,49]]; | |
| chart=chartLine([{label:'经典 Ek=½mv²(相对)',color:'#2563eb',pts:kc},{label:'相对论式 E=(γ−1)mc²(示意)',color:'#dc2626',pts:kc.map(p=>[p[0],+(p[1]*1.03).toFixed(2)])}],{xlabel:'速度 v(×10⁷ m/s)',ylabel:'动能(示意)'}), | |
| cap='图:低速度下两种动能一致,高速下相对论式偏离';} | |
| else{ | |
| const psi=[];for(let x=-5;x<=5;x+=0.2)psi.push([x,Math.exp(-Math.pow(x,2)*0.35)]); | |
| chart=chartLine([{label:'|ψ(x)|²(概率密度,高斯型)',fill:true,color:'#2563eb',pts:psi}],{xlabel:'位置 x(×10⁻¹⁰ m)',ylabel:'相对概率密度'}), | |
| cap='图:微观粒子的概率分布(示意)'}; | |
| return page(i,'现代物理 · '+T[0], body, chart, cap, | |
| 'γ = 1/√(1−v²/c²) · E = mc² · E = hf · Δx·Δp ≥ ħ/2', | |
| '为什么高速航天员的年龄会比地面上的人小?这个效果在 GPS 卫星上如何体现?', | |
| '时间膨胀使卫星上的钟略慢;相对论总修正约为每天 38 µs,GPS 不修正的话每天位置误差会积累约 10 km。', | |
| '"波粒二象性"为什么只在微观尺度显著?试着用量子大小与测量精度对比,估计为什么宏观物体看不到干涉条纹。'); | |
| } | |
| }, | |
| /* 18 科学方法与未来(展望) 10页 */ | |
| { | |
| name:'科学方法与未来', level:3, pages:10, intro:'怎样像物理学家一样思考。', | |
| gen:function(i){ | |
| const T=['提问是第一步','像科学家一样实验','用模型说话','数学是世界的语法','错误是数据','科学史:一个世纪四次革命','未来的物理','给下一代的任务','一页地图','一千页,才刚刚开头'][i%10]; | |
| const bodies=[ | |
| '好的问题比好的答案更珍贵。"苹果为什么往下掉"问了之后,才有了万有引力。下次看到现象,先别急着看答案,先问"为什么"和"如果是……会怎样"。', | |
| '实验的精髓是<b>控制变量</b>:一次只改变一个因素,比较结果的差异。伽利略让不同重量的球在同一斜面滚下,才发现加速度与质量无关。', | |
| '物理学家从不"直接研究"世界,他们先<b>建模</b>:理想斜面、质点、刚性杆、无摩擦轨道……模型是可计算、可检验的简化。检验模型的方式,就是回到真实世界做实验。', | |
| '万有引力与库仑力都按 1/r² 变化,牛顿定律与薛定谔方程都描述"状态如何随时间演化"——数学是不同尺度之间的通用语。会算题只是入门,会用模型才是核心。', | |
| '"失败"的实验往往是金矿:19 世纪"以太漂移"实验的"零结果",直接逼出了相对论;云室里一条奇怪的弯曲轨迹,发现了 μ 子——"这不可能吗?"正是科学的起点。', | |
| '20 世纪物理学经历了四次深刻转变:相对论(时空)、量子力学(微观)、粒子物理(基本粒子)、凝聚与量子信息(物质的集体行为)。这些革命的共同特征是:先有悖于常识的实验,再有数学化的解释。', | |
| '今天的物理正处在交叉地带:量子计算、引力波探测、暗物质的搜寻、拓扑材料……每一门新学科都建立在"旧规律遇到新现象时的张力"之上。', | |
| '你未来的任务:1)保持提问的习惯;2)学会读图和建模;3)接受"暂时不知道";4)在实验室动手验证一次。这四条,比任何公式都重要。', | |
| '把前面 18 章想象成一棵树:力学是根,电与热是枝,光是叶,相对论与量子是新的花。它们并不孤立,而是同一个"物理世界观"的不同切面。', | |
| '读完这一千页,你只是从"看世界"变成了"读世界"。物理不是记住公式,而是获得一种思维方式:提问、建模、计算、检验。愿你在下一页——真实世界——继续读下去。' | |
| ]; | |
| let chart,cap; | |
| if(i===8){ | |
| const items=SEC.slice(0,10).map(function(s,ix){return {label:s.name,value:s.pages,color:C8[ix%C8.length]};}); | |
| chart=chartPie(items);cap='图:本书各章篇幅分布(前10章)'; | |
| } else { | |
| chart=chartBar([{label:'提问',value:10},{label:'建模',value:10},{label:'计算',value:10},{label:'实验',value:10},{label:'反思',value:9}],{unit:'/10',xlabel:'科学方法五步评分(示意)'});cap='图:科学方法的五个环节(示意打分)'; | |
| } | |
| return page(i,'科学方法 · '+T, '<p>'+bodies[i%10]+'</p><div class="tip">物理学家的一天:提问 → 建模 → 推导 → 计算 → 实验 → 修订 → 再提问。循环往复。</div>', | |
| chart, cap, 'S = 状态( t ), dS/dt = 规律(S) (状态随时间演化,是全部物理学的骨架)', | |
| '列出你最近三天看到的 3 个"可以用物理问一个问题"的现象。', | |
| '答案因人而异,但要写出"可检验"的问题形式,而不是"为什么"一句带过。', | |
| '选一个"反直觉"的自然现象(例如"冰箱门为什么打不开"),用已学内容写一段 200 字的物理解释。'); | |
| } | |
| } | |
| ]; | |
| /* ================= 渲染 ================= */ | |
| function LVL_L(name,color){return {name:name,color:color};} | |
| const LVLS=[LVL_L('小学','#16a34a'),LVL_L('初中','#2563eb'),LVL_L('高中','#7c3aed'),LVL_L('大学','#dc2626')]; | |
| /* cover */ | |
| let html=''; | |
| html+='<div class="page" id="cover" style="min-height:1120px">'+ | |
| '<div style="flex:1;display:flex;align-items:center">'+ | |
| '<div><div class="kicker">儿童 → 初中 → 高中 → 大学 · 1000 页</div>'+ | |
| '<h1 class="cover-t">给孩子的物理世界</h1>'+ | |
| '<div class="cover-sub">一千页通识物理书 · 每一页都有图</div>'+ | |
| '<div class="cover-meta">'+ | |
| '<span class="chip">18 章</span><span class="chip">1000 页</span><span class="chip">每页配 1 张 SVG 图</span><span class="chip">从"量一量"到"相对论与量子"'+ | |
| '</span></div>'+ | |
| '<div class="cover-box" style="margin-top:30px;background:#f8fafc">'+ | |
| '<div style="font-size:14px;color:#64748b;margin-bottom:8px">阅读路径</div>'+ | |
| '<div style="font-size:17px;color:#111827;font-weight:600">小学 · 测量与运动 → 初中 · 力、热、声、光、电 → 高中 · 能量与波动 → 大学 · 电磁场、热力学、相对论与量子</div>'+ | |
| '</div>'+ | |
| '<div class="tip" style="margin-top:26px">本书按"理解—图像—试一试—思考题"四步组织。建议每章读完后合上书,用自己的话复述一遍,再翻到下一章。</div></div>'+ | |
| '</div><div class="pno">封面</div></div>'; | |
| /* toc */ | |
| html+='<div class="page" id="toc" style="min-height:1120px"><h1 style="font-size:30px">目录</h1>'+ | |
| '<div class="rule" style="background:#2563eb"></div><div class="toc">'; | |
| let cur=1; | |
| SEC.forEach(function(s){ | |
| const end=cur+s.pages-1; | |
| const lv=LVLS[s.level]; | |
| html+='<a href="#p'+cur+'"><span>'+s.name+'<span class="lv" style="background:'+lv.color+'">'+lv.name+'</span></span><span class="pgno">第'+cur+'–'+end+'页 · '+s.pages+'页</span></a>'; | |
| cur=end+1; | |
| }); | |
| html+='</div><div class="tip" style="margin-top:18px">说明:本书正文共 <b>1000 页</b>(第 1–1000 页)。封面与目录不计入页码。每一页都有独立的图、公式、试一试和一道思考题,按章节从易到难。</div><div class="pno">目录</div></div>'; | |
| /* pages */ | |
| let pno=1; | |
| SEC.forEach(function(sec){ | |
| const lv=LVLS[sec.level]; | |
| for(let i=0;i<sec.pages;i++){ | |
| const d=sec.gen(i); | |
| html+='<div class="page" id="p'+pno+'" data-n="'+pno+'">'+ | |
| '<div class="pagehead"><h2>第 '+pno+' 页 · '+d.t+'</h2><span class="badge" style="background:'+lv.color+'">'+lv.name+'</span></div>'+ | |
| '<div class="rule" style="background:'+lv.color+'"></div>'+ | |
| d.body+ | |
| (d.formula?'<div class="formula">'+d.formula+'</div>':'')+ | |
| (d.chart?'<div class="chartwrap">'+d.chart+'<div class="chartcap">'+(d.cap||'')+'</div></div>':'')+ | |
| (d.tq?'<div class="try"><b>试一试:</b>'+d.tq+'</div>':'')+ | |
| (d.ta?'<details><summary>参考答案</summary><p>'+d.ta+'</p></details>':'')+ | |
| (d.th?'<div class="think"><b>思考:</b>'+d.th+'</div>':'')+ | |
| '<div class="psec">《给孩子的物理世界》 · '+sec.name+'</div>'+ | |
| '<div class="pno">'+pno+' / 1000</div></div>'; | |
| pno++; | |
| } | |
| }); | |
| html+='<div class="page" id="end" style="min-height:600px"><div style="flex:1;display:flex;align-items:center;justify-content:center;text-align:center">'+ | |
| '<div><div style="font-size:34px;font-weight:700">— 完 —</div><div style="color:#6b7280;margin-top:10px">下一千页,去真实世界读。</div></div></div><div class="pno">末页</div></div>'; | |
| document.getElementById('wrap').innerHTML=html; | |
| /* nav */ | |
| const allpages=[].slice.call(document.querySelectorAll('.page[data-n]')); | |
| function curNo(){ | |
| const y=window.scrollY+120; | |
| let p=allpages[0]?allpages[0].dataset.n:1; | |
| for(const el of allpages){if(el.offsetTop<=y)p=el.dataset.n;else break;} | |
| return p; | |
| } | |
| function go(n){ | |
| if(n<1)n=1;if(n>1000)n=1000; | |
| const el=document.getElementById('p'+n); | |
| if(el)el.scrollIntoView({behavior:'smooth'}); | |
| } | |
| document.getElementById('btnPrev').onclick=function(){go(curNo()-1);}; | |
| document.getElementById('btnNext').onclick=function(){go(curNo()+1);}; | |
| document.getElementById('btnHome').onclick=function(){document.getElementById('toc').scrollIntoView({behavior:'smooth'});}; | |
| function upd(){document.getElementById('pgLabel').textContent='第 '+curNo()+' / 1000 页';} | |
| window.addEventListener('scroll',upd,{passive:true}); | |
| upd(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment