diff --git a/.gitattributes b/.gitattributes index 8f59fe915ff797a45f1585196e48facf7828a846..c937e6b250fc53f676f44cc819e0dbe66f38d11e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -215,3 +215,12 @@ wolf-coming/web/public/wolf/live/05-eyes.jpg filter=lfs diff=lfs merge=lfs -text wolf-coming/web/public/wolf/live/06-regret.jpg filter=lfs diff=lfs merge=lfs -text wolf-coming/web/public/wolf/qa/sheet_landscape.jpg filter=lfs diff=lfs merge=lfs -text wolf-coming/web/public/wolf/qa/sheet_portrait.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/01-idea.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/02-shout.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/03-run.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/04-laugh.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/05-eyes.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/live/06-regret.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/qa/sheet_landscape.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/qa/sheet_portrait.jpg filter=lfs diff=lfs merge=lfs -text +ugly-duckling/web/public/wolf/voice.mp3 filter=lfs diff=lfs merge=lfs -text diff --git a/ugly-duckling/README.md b/ugly-duckling/README.md index 67c660a9fb9a5e89d312350afeed1d93b634d5f9..a295c03dd23b5e32a224e9bc85943198c74acc8a 100644 --- a/ugly-duckling/README.md +++ b/ugly-duckling/README.md @@ -103,6 +103,7 @@ ugly-duckling/ - **2026-10-17** **最终质检**:竖屏 `vibe qa` 规格 / 响度 −14.0 LUFS / 真峰值 −1.4 dBTP / 黑场 / 冻结 / 13 场字幕 OCR 全 PASS(倒影一场为检查窗口跨两句字幕的误报,当前字幕已读出);横屏同样规格全 PASS,字幕带补充检查 13/13 PASS(墨色对纸带对比度 10–13:1)。结果写入网页「制作与质检」。 - **2026-10-17** 部署:`dist/` 由 `vite build --base ./` 生成,Dockerfile 新增 `COPY ugly-duckling/dist /app/site/ugly-duckling`,访问 `/ugly-duckling/`。 - **2026-10-17** 按反馈修颈部:原先颈线用两个固定偏移点穿样条,任何姿态都被拧成 Z 字形 S 弯(飞行、低头时尤其别扭)。改为随姿态自适应的单段平滑曲线(直立:从胸前竖直升起、到头部才柔和前弯;飞行:几乎笔直前伸;低头:先拱起再垂向水面),颈根粗、近头处渐细并圆润并入胸口。重渲实况图 6 组、横竖屏全片并复跑 `vibe qa`(结论同前:规格 / 响度 / 黑场 / 冻结全 PASS)。 +- **2026-10-18** 随《狼来了》(`wolf-coming/`)把网页工程改为多片:本片代码移到 `web/src/films/duckling/`,共用引擎留在 `web/src/film/`。改造前后 11 个时间点逐帧 MD5 完全一致。另外两处共用改进:同一句内的分段字幕不再淡出淡入(不闪),字幕宣纸纸带上下留白加大。页头新增系列互链。重新构建 `dist/`。 ## 6. 交付清单 | 内容 | 位置 | diff --git a/ugly-duckling/dist/assets/App-BEh-_Ljg.js b/ugly-duckling/dist/assets/App-BEh-_Ljg.js new file mode 100644 index 0000000000000000000000000000000000000000..12992dd0c46772b8b92ff54560c448f2d8b00728 --- /dev/null +++ b/ugly-duckling/dist/assets/App-BEh-_Ljg.js @@ -0,0 +1,370 @@ +var Ha=Object.defineProperty;var Ga=(e,t,r)=>t in e?Ha(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var Ve=(e,t,r)=>Ga(e,typeof t!="symbol"?t+"":t,r);import{j as v}from"./jsx-dev-runtime-Czb3f8pQ.js";import{r as te}from"./index-Br42UDbl.js";import"./index-BlGQAcOD.js";/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Xa=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),No=(...e)=>e.filter((t,r,n)=>!!t&&t.trim()!==""&&n.indexOf(t)===r).join(" ").trim();/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var Qa={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ka=te.forwardRef(({color:e="currentColor",size:t=24,strokeWidth:r=2,absoluteStrokeWidth:n,className:s="",children:i,iconNode:o,...a},l)=>te.createElement("svg",{ref:l,...Qa,width:t,height:t,stroke:e,strokeWidth:n?Number(r)*24/Number(t):r,className:No("lucide",s),...a},[...o.map(([f,c])=>te.createElement(f,c)),...Array.isArray(i)?i:[i]]));/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ne=(e,t)=>{const r=te.forwardRef(({className:n,...s},i)=>te.createElement(Ka,{ref:i,iconNode:t,className:No(`lucide-${Xa(e)}`,n),...s}));return r.displayName=`${e}`,r};/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ya=Ne("Apple",[["path",{d:"M12 20.94c1.5 0 2.75 1.06 4 1.06 3 0 6-8 6-12.22A4.91 4.91 0 0 0 17 5c-2.22 0-4 1.44-5 2-1-.56-2.78-2-5-2a4.9 4.9 0 0 0-5 4.78C2 14 5 22 8 22c1.25 0 2.5-1.06 4-1.06Z",key:"3s7exb"}],["path",{d:"M10 2c1 .5 2 2 2 5",key:"fcco2y"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Za=Ne("Captions",[["rect",{width:"18",height:"14",x:"3",y:"5",rx:"2",ry:"2",key:"12ruh7"}],["path",{d:"M7 15h4M15 15h2M7 11h2M13 11h4",key:"1ueiar"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Bo=Ne("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ja=Ne("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Fs=Ne("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const e0=Ne("Film",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M7 3v18",key:"bbkbws"}],["path",{d:"M3 7.5h4",key:"zfgn84"}],["path",{d:"M3 12h18",key:"1i2n21"}],["path",{d:"M3 16.5h4",key:"1230mu"}],["path",{d:"M17 3v18",key:"in4fa5"}],["path",{d:"M17 7.5h4",key:"myr1c1"}],["path",{d:"M17 16.5h4",key:"go4c1d"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const hn=Ne("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const t0=Ne("Maximize2",[["polyline",{points:"15 3 21 3 21 9",key:"mznyad"}],["polyline",{points:"9 21 3 21 3 15",key:"1avn1i"}],["line",{x1:"21",x2:"14",y1:"3",y2:"10",key:"ota7mn"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ao=Ne("Monitor",[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2",key:"48i651"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21",key:"1svkeh"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21",key:"vw1qmm"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const _o=Ne("PackageOpen",[["path",{d:"M12 22v-9",key:"x3hkom"}],["path",{d:"M15.17 2.21a1.67 1.67 0 0 1 1.63 0L21 4.57a1.93 1.93 0 0 1 0 3.36L8.82 14.79a1.655 1.655 0 0 1-1.64 0L3 12.43a1.93 1.93 0 0 1 0-3.36z",key:"2ntwy6"}],["path",{d:"M20 13v3.87a2.06 2.06 0 0 1-1.11 1.83l-6 3.08a1.93 1.93 0 0 1-1.78 0l-6-3.08A2.06 2.06 0 0 1 4 16.87V13",key:"1pmm1c"}],["path",{d:"M21 12.43a1.93 1.93 0 0 0 0-3.36L8.83 2.2a1.64 1.64 0 0 0-1.63 0L3 4.57a1.93 1.93 0 0 0 0 3.36l12.18 6.86a1.636 1.636 0 0 0 1.63 0z",key:"12ttoo"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const r0=Ne("Pause",[["rect",{x:"14",y:"4",width:"4",height:"16",rx:"1",key:"zuxfzm"}],["rect",{x:"6",y:"4",width:"4",height:"16",rx:"1",key:"1okwgv"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const ri=Ne("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const n0=Ne("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Io=Ne("Smartphone",[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2",key:"1yt0o3"}],["path",{d:"M12 18h.01",key:"mhygvu"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const s0=Ne("Volume2",[["path",{d:"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z",key:"uqj9uw"}],["path",{d:"M16 9a5 5 0 0 1 0 6",key:"1q6k2b"}],["path",{d:"M19.364 18.364a9 9 0 0 0 0-12.728",key:"ijwkga"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const i0=Ne("VolumeX",[["path",{d:"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z",key:"uqj9uw"}],["line",{x1:"22",x2:"16",y1:"9",y2:"15",key:"1ewh16"}],["line",{x1:"16",x2:"22",y1:"9",y2:"15",key:"5ykzw1"}]]);/** + * @license lucide-react v0.454.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const o0=Ne("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);function de(e){let t=e>>>0||1;return()=>{t=t+1831565813>>>0;let r=t;return r=Math.imul(r^r>>>15,r|1),r^=r+Math.imul(r^r>>>7,r|61),((r^r>>>14)>>>0)/4294967296}}function oe(e){const t=Math.sin(e*127.1+311.7)*43758.5453;return t-Math.floor(t)}function mn(e,t=0){const r=Math.floor(e),n=e-r,s=oe(r+t*101.3),i=oe(r+1+t*101.3),o=n*n*(3-2*n);return s+(i-s)*o}const mt=(e,t=0)=>mn(e,t)*2-1,Re=(e,t=0,r=1)=>Math.max(t,Math.min(r,e)),E=(e,t,r)=>e+(t-e)*r,M=(e,t,r)=>Re((r-e)/(t-e)),$=e=>(e=Re(e),e*e*(3-2*e)),me=e=>1-Math.pow(1-Re(e),3),yr=e=>Math.pow(Re(e),3),ne=e=>(e=Re(e),e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2),ni=e=>(e=Math.max(0,e),1-Math.exp(-6*e)*Math.cos(11*e)),G="#1d1a17",Ge="#f6efdf",a0="#b8322a";function P(e,t){const r=parseInt(e.slice(1),16);return`rgba(${r>>16&255},${r>>8&255},${r&255},${t})`}function ht(e,t,r){const n=parseInt(e.slice(1),16),s=parseInt(t.slice(1),16),i=o=>Math.round(E(n>>o&255,s>>o&255,Re(r)));return"#"+(1<<24|i(16)<<16|i(8)<<8|i(0)).toString(16).slice(1)}function Ro(e,t){if(typeof OffscreenCanvas<"u")return new OffscreenCanvas(e,t);const r=document.createElement("canvas");return r.width=e,r.height=t,r}function Oo(e){return e.getContext("2d")}const Cr=new Map;function Fo(e,t){const r=e+"x"+t,n=Cr.get(r);if(n)return n;const s=Ro(e,t),i=Oo(s),o=de(20261017);i.fillStyle=Ge,i.fillRect(0,0,e,t);const a=Math.min(e,t)/1080;for(let c=0;c<46;c++){const u=o()*e,h=o()*t,d=(120+o()*420)*a,m=i.createRadialGradient(u,h,0,u,h,d),b=o()<.55;m.addColorStop(0,b?"rgba(170,140,90,0.028)":"rgba(255,252,240,0.08)"),m.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=m,i.fillRect(u-d,h-d,d*2,d*2)}i.strokeStyle="rgba(150,125,85,0.035)",i.lineWidth=Math.max(1,a);for(let c=0;c>8)%15-7;u[d]+=m,u[d+1]+=m,u[d+2]+=m}i.putImageData(c,0,0)}catch{}const f=i.createRadialGradient(e/2,t/2,Math.min(e,t)*.35,e/2,t/2,Math.hypot(e,t)*.62);return f.addColorStop(0,"rgba(0,0,0,0)"),f.addColorStop(1,"rgba(120,90,50,0.13)"),i.fillStyle=f,i.fillRect(0,0,e,t),Cr.set(r,s),Cr.size>4&&Cr.delete(Cr.keys().next().value),s}function fe(e,t=8,r=!1){const n=[],s=e.length;if(s<3)return e.slice();const i=a=>r?e[(a+s)%s]:e[Math.max(0,Math.min(s-1,a))],o=r?s:s-1;for(let a=0;a{for(let c=1;c=f){const u=(f-n[c-1])/Math.max(1e-6,n[c]-n[c-1]);return[E(e[c-1][0],e[c][0],u),E(e[c-1][1],e[c][1],u)]}return e[e.length-1]};a.push(l(i));for(let f=0;fi&&n[f]=0;b--)e.lineTo(m[b][0],m[b][1]);if(e.closePath(),e.fill(),(n.alpha??.92)>.5&&r>3&&(e.fillStyle=P(n.color??G,.12),e.beginPath(),e.arc(i[0][0],i[0][1],r/2*.55,0,Math.PI*2),e.fill()),n.dry&&n.dry>0){e.strokeStyle=P(Ge,.75*n.dry);const b=4;for(let p=0;pa&&z(e,gr(s,a,l),r,{taperIn:.12,taperOut:.25,...n,seed:(n.seed??1)+o*7})}}function ue(e,t,r,n=.6,s={}){const i=fe(t,8,s.closed??!0);e.save(),e.translate(s.dx??0,s.dy??0),$t(e,i,!0),e.fillStyle=P(r,n),e.fill(),(s.edge??1)>0&&(e.strokeStyle=P(r,Math.min(1,n*.5*(s.edge??1))),e.lineWidth=s.lw??2.2,e.lineJoin="round",e.stroke()),e.restore()}function le(e,t,r,n,s,i,o=1,a=1){if(n<=0||i<=0)return;e.save(),e.translate(t,r),e.scale(o,a);const l=e.createRadialGradient(0,0,0,0,0,n);l.addColorStop(0,P(s,i)),l.addColorStop(.55,P(s,i*.6)),l.addColorStop(1,P(s,0)),e.fillStyle=l,e.fillRect(-n,-n,n*2,n*2),e.restore()}function nr(e,t,r,n,s=.18,i=28){const o=[];for(let a=0;ad[1])),h=e.createLinearGradient(0,u,0,r);h.addColorStop(0,P(s,i)),h.addColorStop(.6,P(s,i*.55)),h.addColorStop(1,P(s,0)),e.fillStyle=h,e.beginPath(),e.moveTo(0,r+2);for(const d of f)e.lineTo(d[0],d[1]);if(e.lineTo(t,r+2),e.closePath(),e.fill(),(a.soft??0)<1){const d=de(o*991);for(let m=0;m<6;m++){const b=d()*.85,p=b+.06+d()*.12;z(e,gr(f,b,Math.min(1,p)),2.5+d()*3,{color:s,alpha:i*.9,seed:o+m,jitter:.4,dry:.4})}}}function Hr(e,t,r,n,s,i={}){const o=i.alpha??1;if(o<=0)return;e.save(),e.translate(t,r);const a=n;e.globalAlpha=o;const l=[[-a/2,-a/2],[a/2,-a/2],[a/2,a/2],[-a/2,a/2]];if(e.fillStyle=a0,e.beginPath(),i.round)e.arc(0,0,a/2,0,Math.PI*2);else{e.moveTo(l[0][0],l[0][1]);for(const d of l.slice(1))e.lineTo(d[0],d[1]);e.closePath()}e.fill(),e.fillStyle=Ge,e.textAlign="center",e.textBaseline="middle";const f=s.length,c=Math.max(...s.map(d=>d.length)),u=a*.84/Math.max(f,c);e.font=`${u*.95}px ${i.font??Rt}`;for(let d=0;d"u")return Pr=Promise.resolve();const t=[["WenKaiFilm",e+"film/fonts/wenkai.woff2"],["MaShanZhengFilm",e+"film/fonts/mashanzheng.woff2"]];return Pr=Promise.all(t.map(async([r,n])=>{try{const s=new FontFace(r,`url(${n})`);await s.load(),document.fonts.add(s)}catch(s){console.warn("font load failed",r,s)}})).then(()=>{}),Pr}let ae=null,ot={scenes:[]},ve=1,Qr={};function c0(e){ae!==e&&(ae=e,ot=e.timeline,ve=e.timeline.total,Qr=e.scenes,jt.clear())}const jt=new Map;function l0(e,t,r,n){const s=`${e}:${t}x${r}`;let i=jt.get(s);if(i&&(jt.delete(s),jt.set(s,i)),!i){i=Ro(t,r);const o=Oo(i);n(o,t,r),jt.set(s,i),jt.size>8&&jt.delete(jt.keys().next().value)}return i}function Ds(e){const t=ot.scenes;for(let r=t.length-1;r>=0;r--)if(e>=t[r].start)return r;return 0}function In(e,t,r,n,s){const i=ot.scenes[n],o=Qr[i.id],a=Math.min(t,r)/1080,l=r>t*1.05,f=s-i.start,c={g:e,W:t,H:r,u:a,t:f,D:i.dur,T:s,portrait:l,gy:r*(l?.62:.6),X:b=>t/2+b*Math.min(t/2,r*.85)};e.drawImage(Fo(t,r),0,0),e.save();const u=o.cam?o.cam(c):{z:1,x:t/2,y:r/2,dx:0,dy:0},h=Math.max(1,u.z),d=Math.min(Math.max(u.x,t/(2*h)),t-t/(2*h)),m=Math.min(Math.max(u.y,r/(2*h)),r-r/(2*h));e.translate(t/2+(u.dx??0),r/2+(u.dy??0)),e.scale(h,h),e.translate(-d,-m),o.bg&&e.drawImage(l0(i.id,t,r,(b,p,w)=>o.bg({...c,g:b,W:p,H:w})),0,0),o.draw(c),e.restore(),o.overlay&&o.overlay(c)}const si=1.1;function zs(e,t,r,n,s={}){var l,f;n=Re(n,0,ve-1e-4);const i=Ds(n),o=ot.scenes[i],a=n-o.start;if(e.save(),e.setTransform(1,0,0,1,0,0),e.globalAlpha=1,e.globalCompositeOperation="source-over",i>0&&a0&&(e.globalAlpha=c,e.drawImage(Fo(t,r),0,0),e.globalAlpha=1)}e.restore()}function f0(e){const t=Ds(e),r=ot.scenes[t].subs??[];for(let n=0;n=s.t0-.05&&e0&&s.t0-r[n-1].t1<.12,a=n+1t*1.05,o=Math.min(t,r)/1080,a=(i?58:50)*o,l=i?r*.785:r-86*o;e.save(),e.globalAlpha=s.a,e.font=`${a}px ${xr}`,e.textAlign="center",e.textBaseline="middle";const c=e.measureText(s.text).width/2+a*2.2,u=a*1.35;e.save(),e.translate(t/2,l),e.scale(c/u,1);const h=e.createRadialGradient(0,0,0,0,0,u);h.addColorStop(0,P(Ge,.95)),h.addColorStop(.72,P(Ge,.9)),h.addColorStop(1,P(Ge,0)),e.fillStyle=h,e.fillRect(-u,-u,u*2,u*2),e.restore(),e.lineJoin="round",e.strokeStyle=P(Ge,.96),e.lineWidth=a*.2,e.strokeText(s.text,t/2,l),e.fillStyle=P(s.who&&((m=ae.subColors)==null?void 0:m[s.who])||G,.97),e.fillText(s.text,t/2,l),e.restore()}function Vo(){return ot.scenes.map(e=>{var t;return{id:e.id,start:e.start,dur:e.dur,label:((t=Qr[e.id])==null?void 0:t.label)??e.id}})}const Pe=44100,Zr=e=>440*Math.pow(2,(e-69)/12),x={E2:40,G2:43,A2:45,B2:47,D3:50,E3:52,G3:55,A3:57,B3:59,D4:62,E4:64,G4:67,A4:69,B4:71,D5:74,E5:76,G5:79,A5:81,B5:83,D6:86,E6:88},Tn=e=>{var t;return((t=ot.scenes.find(r=>r.id===e))==null?void 0:t.start)??0},js=e=>{var t;return((t=ot.scenes.find(r=>r.id===e))==null?void 0:t.dur)??0};class d0{constructor(t){Ve(this,"ctx");Ve(this,"music");Ve(this,"sfx");Ve(this,"voice");Ve(this,"rev");Ve(this,"revIn");Ve(this,"master");Ve(this,"noise");Ve(this,"ksCache",new Map);Ve(this,"r",de(4242));Ve(this,"q",[]);Ve(this,"voiceBuf",null);Ve(this,"pl",[]);this.ctx=t;const r=this;for(const o of["flute","pad","bell","block","noiseHit","tone","wind","quack","swanCall"]){const a=r[o].bind(this);r[o]=(...l)=>{this.q.push({t:l[0],fn:()=>a(...l)})}}this.master=t.createGain(),this.master.gain.value=.9;const n=t.createDynamicsCompressor();n.threshold.value=-10,n.knee.value=6,n.ratio.value=6,n.attack.value=.004,n.release.value=.2,this.master.connect(n),n.connect(t.destination),this.music=t.createGain(),this.music.connect(this.master),this.sfx=t.createGain(),this.sfx.gain.value=.9,this.sfx.connect(this.master),this.voice=t.createGain(),this.voice.gain.value=1,this.voice.connect(this.master),this.rev=t.createConvolver(),this.rev.buffer=this.impulse(2.3,2.4),this.revIn=t.createGain(),this.revIn.gain.value=.55,this.revIn.connect(this.rev),this.rev.connect(this.master),this.noise=t.createBuffer(1,Pe*2,Pe);const s=this.noise.getChannelData(0),i=de(99);for(let o=0;o{if(this.voiceBuf)for(let o=1;o<=i;o++){const a=this.ctx.createBufferSource();a.buffer=this.voiceBuf;const l=this.ctx.createBiquadFilter();l.type="lowpass",l.frequency.value=2800/o;const f=this.ctx.createGain();f.gain.value=s*Math.pow(.5,o-1),a.connect(l),l.connect(f),this.out(f,this.sfx,o%2?-.45:.45,.6),a.start(t+n*o+1/30,t,r)}})}impulse(t,r){const n=this.ctx.createBuffer(2,Math.floor(Pe*t),Pe),s=de(7);for(let i=0;i<2;i++){const o=n.getChannelData(i);for(let a=0;a0){const o=this.ctx.createGain();o.gain.value=s,i.connect(o),o.connect(this.revIn)}}ks(t){const r=Math.round(t*10);let n=this.ksCache.get(r);if(n)return n;const s=Math.floor(Pe*3),i=Math.max(2,Math.round(Pe/t));n=this.ctx.createBuffer(1,s,Pe);const o=n.getChannelData(0),a=de(r),l=new Float32Array(i);for(let h=0;h600?.994:.997;for(let h=0;hve+2)return;if(!this.pl.length){const A=this.ctx.length;this.pl=[new Float32Array(A),new Float32Array(A)]}const o=this.ks(Zr(r)).getChannelData(0),a=i.dur??2.6,l=Math.floor(t*Pe),f=Math.min(o.length-2,Math.floor((a+.6)*Pe)),c=n*Math.cos((s+1)*Math.PI/4),u=n*Math.sin((s+1)*Math.PI/4),h=a*.7*Pe,d=a*.15*Pe,m=Math.exp(-2*Math.PI*(2400+(i.bright??.5)*4e3)/Pe),b=this.pl[0],p=this.pl[1];let w=0,y=0;const g=i.bend?Math.pow(2,i.bend/12):1,k=.12*Pe,T=.32*Pe;for(let A=0;A=b.length)break;const S=i.bend?AT?g:1+(g-1)*(A-k)/(T-k):1,B=Math.floor(w),V=w-B;if(B+1>=o.length)break;const W=o[B]*(1-V)+o[B+1]*V;w+=S,y=W+m*(y-W);const L=A{const h=o.createOscillator();h.type="sine",h.frequency.value=a*f;const d=o.createGain();d.gain.setValueAtTime(0,t),d.gain.linearRampToValueAtTime(n*c,t+.005),d.gain.exponentialRampToValueAtTime(1e-4,t+u),h.connect(d),d.connect(l),h.start(t),h.stop(t+u+.05)}),this.out(l,i??this.music,s,.5)}block(t,r=.2,n=950,s=0){const i=this.ctx,o=i.createOscillator();o.type="sine",o.frequency.setValueAtTime(n,t),o.frequency.exponentialRampToValueAtTime(n*.6,t+.05);const a=i.createGain();a.gain.setValueAtTime(r,t),a.gain.exponentialRampToValueAtTime(1e-4,t+.09),o.connect(a),this.out(a,this.music,s,.15),o.start(t),o.stop(t+.12)}gliss(t,r,n=.06,s=.3,i=0){r.forEach((o,a)=>this.pluck(t+a*n,o,s*(.7+.3*a/r.length),i+(a/r.length-.5)*.6,{dur:2.2}))}melody(t,r,n,s=.22,i=0,o="flute"){let a=t;for(const[l,f]of n)l!=="-"&&(o==="flute"?this.flute(a,x[l]+i,f*r*.96,s):this.pluck(a,x[l]+i,s,.15,{bend:(f>=2&&this.r()<.5,0)})),a+=f*r;return a}noiseHit(t,r,n,s,i,o,a=0,l=1,f=.2,c){const u=this.ctx,h=u.createBufferSource();h.buffer=this.noise,h.loop=!0;const d=u.createBiquadFilter();d.type=n,d.Q.value=l,d.frequency.setValueAtTime(s,t),d.frequency.exponentialRampToValueAtTime(Math.max(20,i),t+r);const m=u.createGain();m.gain.setValueAtTime(0,t),m.gain.linearRampToValueAtTime(o,t+Math.min(.01,r*.2)),m.gain.exponentialRampToValueAtTime(1e-4,t+r),h.connect(d),d.connect(m),this.out(m,c??this.sfx,a,f),h.start(t,t*.731%1.8),h.stop(t+r+.05)}tone(t,r,n,s,i,o="sine",a=0,l=.2){const f=this.ctx,c=f.createOscillator();c.type=o,c.frequency.setValueAtTime(r,t),c.frequency.exponentialRampToValueAtTime(Math.max(20,n),t+s);const u=f.createGain();u.gain.setValueAtTime(0,t),u.gain.linearRampToValueAtTime(i,t+.008),u.gain.exponentialRampToValueAtTime(1e-4,t+s),c.connect(u),this.out(u,this.sfx,a,l),c.start(t),c.stop(t+s+.05)}wind(t,r,n,s=0){const i=this.ctx,o=i.createBufferSource();o.buffer=this.noise,o.loop=!0;const a=i.createBiquadFilter();a.type="bandpass",a.Q.value=.8;const l=i.createOscillator();l.frequency.value=.23;const f=i.createGain();f.gain.value=260,a.frequency.value=520,l.connect(f),f.connect(a.frequency);const c=i.createGain();c.gain.setValueAtTime(0,t),c.gain.linearRampToValueAtTime(n,t+Math.min(1.5,r/3)),c.gain.setValueAtTime(n,t+r-Math.min(1.5,r/3)),c.gain.linearRampToValueAtTime(0,t+r),o.connect(a),a.connect(c),this.out(c,this.sfx,s,.1),o.start(t),o.stop(t+r+.1),l.start(t),l.stop(t+r+.1)}chirp(t,r=.06,n=0){const s=2600+this.r()*1600;for(let i=0;i<2+Math.floor(this.r()*3);i++)this.tone(t+i*.09,s,s*(1.25+this.r()*.2),.06,r,"sine",n,.3)}peep(t,r=.12,n=0){this.tone(t,2100,2900,.09,r,"triangle",n,.15),this.tone(t+.13,2200,3e3,.08,r*.8,"triangle",n,.15)}quack(t,r=.14,n=0,s=1){const i=this.ctx,o=i.createOscillator();o.type="sawtooth",o.frequency.setValueAtTime(330*s,t),o.frequency.linearRampToValueAtTime(250*s,t+.16);const a=i.createBiquadFilter();a.type="bandpass",a.frequency.value=1150*s,a.Q.value=3.2;const l=i.createBiquadFilter();l.type="peaking",l.frequency.value=2400,l.gain.value=8;const f=i.createGain();f.gain.setValueAtTime(0,t),f.gain.linearRampToValueAtTime(r,t+.015),f.gain.setValueAtTime(r,t+.11),f.gain.exponentialRampToValueAtTime(1e-4,t+.2),o.connect(a),a.connect(l),l.connect(f),this.out(f,this.sfx,n,.15),o.start(t),o.stop(t+.25)}crack(t,r=.25,n=0,s=!1){const i=s?7:3;for(let o=0;o{var d;const i=Math.ceil(Pe*(ve+.3)),o=new OfflineAudioContext(2,i,Pe);t==null||t("加载旁白…");let a=null;try{const m=await(await fetch(e)).arrayBuffer();a=await o.decodeAudioData(m)}catch(m){console.warn("voice load failed",m)}t==null||t("合成配乐与音效…");const l=new d0(o);l.voiceBuf=a;const f=l.music.gain;f.setValueAtTime(.9,0);for(const m of ot.scenes){const b=(d=m.lines)!=null&&d.length?m.lines.map(p=>[p.t0,p.t0+p.dur]):m.voice_start&&m.voice_dur?[[m.voice_start,m.voice_start+m.voice_dur]]:[];for(const[p,w]of b)f.setTargetAtTime(.42,Math.max(0,p-.25),.08),f.setTargetAtTime(.9,w+.1,.25)}r.score(l),r.effects(l),l.flushPlucks(),l.q.sort((m,b)=>m.t-b.t);let c=0;const u=m=>{for(;c{u(b+1.5),o.resume()})}if(a){const m=o.createBufferSource();m.buffer=a;const b=o.createGain();b.gain.value=1.15;const p=o.createBiquadFilter();p.type="highpass",p.frequency.value=80,m.connect(p),p.connect(b),b.connect(l.voice),m.start(1/30)}const h=await o.startRendering();return t==null||t("响度标准化(−14 LUFS)…"),m0(h,-14,.84),t==null||t("完成"),h})();return Rn.set(r.id,s),s.catch(()=>Rn.delete(r.id)),s}function h0(e){const t=e.sampleRate,r=(y,g,k,T,A,N)=>{const S=new Float32Array(y.length);let B=0,V=0,W=0,L=0;for(let J=0;J{const k=r(e.getChannelData(g),l[0],l[1],l[2],l[3],l[4]);return r(k,f[0],f[1],f[2],f[3],f[4])}),u=Math.round(.4*t),h=Math.round(.1*t),d=[];for(let y=0;y+u<=e.length;y+=h){let g=0;for(const k of c){let T=0;for(let A=y;A-.691+10*Math.log10(y+1e-12),b=d.filter(y=>m(y)>-70);if(!b.length)return-70;const p=m(b.reduce((y,g)=>y+g,0)/b.length)-10,w=b.filter(y=>m(y)>p);return m(w.reduce((y,g)=>y+g,0)/Math.max(1,w.length))}function m0(e,t=-14,r=.84){const n=h0(e),s=Math.pow(10,(t-n)/20),i=e.length,o=Array.from({length:e.numberOfChannels},(u,h)=>e.getChannelData(h)),a=new Float32Array(i);for(let u=0;uh&&(h=m)}a[u]=h>r?r/h:1}const l=1/(.004*e.sampleRate),f=1/(.12*e.sampleRate);for(let u=i-2;u>=0;u--)a[u]=Math.min(a[u],a[u+1]+l);let c=1;for(let u=0;u`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,"0")}`;function p0({orientation:e,onOrientation:t}){var ie;const r=te.useRef(null),n=te.useRef(null),[s,i]=te.useState(!1),[o,a]=te.useState(0),[l,f]=te.useState(!0),[c,u]=te.useState(!1),[h,d]=te.useState("准备中…"),[m,b]=te.useState(!1),[p,w]=te.useState(!1),y=te.useRef(null),g=te.useRef(null),k=te.useRef(null),T=te.useRef(null),A=te.useRef(0),N=te.useRef(0),S=te.useRef(!1),B=te.useRef(!0);B.current=l;const V=Vo();te.useEffect(()=>{Vs(ii).then(()=>w(!0)),Do(`${ii}${ae.assets}voice.mp3`,d).then(O=>{y.current=O,b(!0)}).catch(O=>d("音轨合成失败:"+((O==null?void 0:O.message)??O)))},[]);const W=te.useCallback(()=>S.current&&g.current?g.current.currentTime-A.current:N.current,[]),L=()=>{var O,F;try{(O=k.current)==null||O.stop()}catch{}(F=k.current)==null||F.disconnect(),k.current=null},J=O=>{if(!g.current||!y.current)return;L();const F=g.current.createBufferSource();F.buffer=y.current,F.connect(T.current),F.start(0,Math.max(0,O)),k.current=F,A.current=g.current.currentTime-O},U=async()=>{y.current&&(g.current||(g.current=new AudioContext,T.current=g.current.createGain(),T.current.connect(g.current.destination)),await g.current.resume(),T.current.gain.value=c?0:1,N.current>=ve-.05&&(N.current=0),J(N.current),S.current=!0,i(!0))},I=()=>{N.current=W(),S.current=!1,L(),i(!1)},j=O=>{O=Math.max(0,Math.min(ve-.01,O)),N.current=O,S.current&&J(O),a(O)};te.useEffect(()=>{T.current&&(T.current.gain.value=c?0:1)},[c]),te.useEffect(()=>{const O=n.current;if(!O)return;const F=()=>{const q=Math.min(2,window.devicePixelRatio||1),H=O.getBoundingClientRect();if(e==="landscape"){const K=Math.min(1600,Math.round(H.width*q/2)*2);O.width=K,O.height=Math.round(K*9/16/2)*2}else{const K=Math.min(1600,Math.round(H.height*q/2)*2);O.height=K,O.width=Math.round(K*9/16/2)*2}};F();const Q=new ResizeObserver(F);return Q.observe(O),()=>Q.disconnect()},[e]),te.useEffect(()=>{var H;let O=0,F=0;const Q=(H=n.current)==null?void 0:H.getContext("2d"),q=K=>{O=requestAnimationFrame(q);const be=n.current;if(!be||!Q||!be.width)return;let we=W();we>=ve&&(we=ve-.001,S.current&&(I(),N.current=ve)),zs(Q,be.width,be.height,we,{subs:B.current}),K-F>120&&(F=K,a(we))};return O=requestAnimationFrame(q),()=>cancelAnimationFrame(O)},[p,e]),te.useEffect(()=>{const O=F=>{var Q,q;F.code==="Space"&&((Q=F.target)==null?void 0:Q.tagName)!=="INPUT"&&((q=F.target)==null?void 0:q.tagName)!=="TEXTAREA"&&(F.preventDefault(),S.current?I():U())};return window.addEventListener("keydown",O),()=>window.removeEventListener("keydown",O)}),te.useEffect(()=>()=>{var O;L(),(O=g.current)==null||O.close()},[]);const Z=V.reduce((O,F,Q)=>o>=F.start?Q:O,0),re=e==="portrait";return v.jsxDEV("div",{className:"flex flex-col gap-3",children:[v.jsxDEV("div",{ref:r,className:`relative mx-auto overflow-hidden rounded-md shadow-[0_8px_30px_rgba(60,40,20,0.18)] ring-1 ring-[#d6c9ad] bg-[#f2ead8] ${re?"w-[min(100%,calc(min(78vh,820px)*9/16))] aspect-[9/16]":"w-full aspect-video"}`,children:[v.jsxDEV("canvas",{ref:n,className:"block h-full w-full cursor-pointer object-contain",onClick:()=>s?I():U()},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:133,columnNumber:9},this),!s&&v.jsxDEV("button",{onClick:U,disabled:!m,className:"absolute inset-0 m-auto flex h-20 w-20 items-center justify-center rounded-full bg-[#b8322a]/90 text-[#f6efdf] shadow-lg transition hover:scale-105 disabled:opacity-60","aria-label":"播放",children:m?v.jsxDEV(ri,{className:"ml-1 h-9 w-9",fill:"currentColor"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:136,columnNumber:22},this):v.jsxDEV("span",{className:"text-xs leading-tight px-2 text-center",children:h},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:136,columnNumber:78},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:135,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:132,columnNumber:7},this),v.jsxDEV("div",{className:"flex flex-col gap-2 rounded-md border border-[#d9cdb2] bg-[#f8f3e7] px-3 py-2",children:[v.jsxDEV("div",{className:"relative h-6",children:[v.jsxDEV("input",{type:"range",min:0,max:ve,step:.01,value:o,onChange:O=>j(Number(O.target.value)),className:"ink-range absolute inset-0 w-full","aria-label":"进度"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:144,columnNumber:11},this),V.map(O=>v.jsxDEV("span",{className:"pointer-events-none absolute top-[19px] h-[5px] w-px bg-[#8a7a5a]",style:{left:`${O.start/ve*100}%`}},O.id,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:145,columnNumber:27},this))]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:143,columnNumber:9},this),v.jsxDEV("div",{className:"flex flex-wrap items-center gap-2 text-sm text-[#3a3128]",children:[v.jsxDEV("button",{onClick:()=>s?I():U(),disabled:!m,className:"ink-btn","aria-label":s?"暂停":"播放",children:[s?v.jsxDEV(r0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:149,columnNumber:24},this):v.jsxDEV(ri,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:149,columnNumber:56},this),v.jsxDEV("span",{children:s?"暂停":m?"播放":"合成中"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:149,columnNumber:85},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:148,columnNumber:11},this),v.jsxDEV("button",{onClick:()=>j(0),className:"ink-btn","aria-label":"从头",children:v.jsxDEV(n0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:151,columnNumber:79},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:151,columnNumber:11},this),v.jsxDEV("span",{className:"tabular-nums text-[#6b5d48]",children:[On(o)," / ",On(ve)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:152,columnNumber:11},this),v.jsxDEV("span",{className:"hidden sm:inline text-[#8a7a5a]",children:["· ",(ie=V[Z])==null?void 0:ie.label]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:153,columnNumber:11},this),v.jsxDEV("div",{className:"ml-auto flex flex-wrap items-center gap-2",children:[v.jsxDEV("button",{onClick:()=>u(!c),className:"ink-btn","aria-label":"静音",children:c?v.jsxDEV(i0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:155,columnNumber:99},this):v.jsxDEV(s0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:155,columnNumber:133},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:155,columnNumber:13},this),v.jsxDEV("button",{onClick:()=>f(!l),className:`ink-btn ${l?"ink-btn-on":""}`,children:[v.jsxDEV(Za,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:156,columnNumber:102},this),v.jsxDEV("span",{children:"字幕"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:156,columnNumber:135},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:156,columnNumber:13},this),v.jsxDEV("div",{className:"flex overflow-hidden rounded border border-[#cdbf9f]",children:[v.jsxDEV("button",{onClick:()=>t("landscape"),className:`flex items-center gap-1 px-2 py-1 ${re?"":"bg-[#2a2420] text-[#f6efdf]"}`,children:[v.jsxDEV(Ao,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:158,columnNumber:164},this),"横屏"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:158,columnNumber:15},this),v.jsxDEV("button",{onClick:()=>t("portrait"),className:`flex items-center gap-1 px-2 py-1 ${re?"bg-[#2a2420] text-[#f6efdf]":""}`,children:[v.jsxDEV(Io,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:159,columnNumber:162},this),"竖屏"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:159,columnNumber:15},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:157,columnNumber:13},this),v.jsxDEV("button",{onClick:()=>{var O,F;return(F=(O=r.current)==null?void 0:O.requestFullscreen)==null?void 0:F.call(O)},className:"ink-btn","aria-label":"全屏",children:v.jsxDEV(t0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:161,columnNumber:112},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:161,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:154,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:147,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:142,columnNumber:7},this),v.jsxDEV("div",{className:"flex gap-1.5 overflow-x-auto pb-1",children:V.map((O,F)=>v.jsxDEV("button",{onClick:()=>j(O.start+.01),className:`shrink-0 rounded border px-2.5 py-1 text-xs transition ${F===Z?"border-[#b8322a] bg-[#b8322a] text-[#f6efdf]":"border-[#d9cdb2] bg-[#f8f3e7] text-[#4a3f30] hover:border-[#8a7a5a]"}`,children:[v.jsxDEV("span",{className:"opacity-70 tabular-nums",children:On(O.start)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:170,columnNumber:13},this)," ",O.label]},O.id,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:169,columnNumber:11},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:167,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Player.tsx",lineNumber:131,columnNumber:5},this)}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */function C(e){if(!e)throw new Error("Assertion failed.")}const b0=Math.PI/180,w0=e=>{const t=(e%360+360)%360;if(t===0||t===90||t===180||t===270)return t;throw new Error(`Invalid rotation ${e}.`)},zo=[1,0,0,0,1,0,0,0,1],y0=(e,t,r)=>{const[n,s,,i,o]=e,a=Math.abs(n)*t+Math.abs(i)*r,l=Math.abs(s)*t+Math.abs(o)*r;return Es(Es(oi(-t/2,-r/2),e),oi(a/2,l/2))},g0=e=>{const t=e*b0,r=Math.round(Math.cos(t)),n=Math.round(Math.sin(t));return[r,n,0,-n,r,0,0,0,1]},oi=(e,t)=>[1,0,0,0,1,0,e,t,1],x0=(e,t)=>[e,0,0,0,t,0,0,0,1],Es=(e,t)=>{const r=new Array(9);for(let n=0;n<3;n++)for(let s=0;s<3;s++)r[3*n+s]=e[3*n]*t[s]+e[3*n+1]*t[3+s]+e[3*n+2]*t[6+s];return r},v0=(e,t,r,n)=>({rotation:w0(e+(t?-r:r)),flip:t!==n}),ze=e=>e&&e[e.length-1],It=e=>e>=0&&e<2**32,k0=e=>e>=-2147483648&&e<2**31,D=e=>{let t=0;for(;e.readBits(1)===0&&t<32;)t++;if(t>=32)throw new Error("Invalid exponential-Golomb code.");return(1<{const t=D(e);return(t&1)===0?-(t>>1):t+1>>1},Ie=e=>e.constructor===Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e),nt=e=>e.constructor===DataView?e:ArrayBuffer.isView(e)?new DataView(e.buffer,e.byteOffset,e.byteLength):new DataView(e),E0=typeof globalThis.TextEncoder<"u"?globalThis.TextEncoder:class{constructor(){this.encoding="utf-8"}encode(t=""){const r=new Uint8Array(3*t.length);let n=0;for(let s=0;s>6,r[n++]=128|i&63;else if(i<55296||i>57343)r[n++]=224|i>>12,r[n++]=128|i>>6&63,r[n++]=128|i&63;else{const o=s+1=56320&&o<=57343?(i=65536+(i-55296<<10)+(o-56320),s++,r[n++]=240|i>>18,r[n++]=128|i>>12&63,r[n++]=128|i>>6&63,r[n++]=128|i&63):(r[n++]=239,r[n++]=191,r[n++]=189)}}return r.slice(0,n)}},Tt=new E0,Er={bt709:1,bt470bg:5,smpte170m:6,bt2020:9,smpte432:12},Tr={bt709:1,smpte170m:6,linear:8,"iec61966-2-1":13,pq:16,hlg:18},Sr={rgb:0,bt709:1,bt470bg:5,smpte170m:6,"bt2020-ncl":9},T0=e=>!e||e.primaries==null&&e.transfer==null&&e.matrix==null&&e.fullRange==null,Sn=e=>e instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&e instanceof SharedArrayBuffer||ArrayBuffer.isView(e);class jo{constructor(){this.currentPromise=Promise.resolve(),this.pending=0}async acquire(){let t;const r=new Promise(s=>{let i=!1;t=()=>{i||(s(),this.pending--,i=!0)}}),n=this.currentPromise;return this.currentPromise=r,this.pending++,await n,t}}const ai=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join(""),ci=e=>(e=e>>1&1431655765|(e&1431655765)<<1,e=e>>2&858993459|(e&858993459)<<2,e=e>>4&252645135|(e&252645135)<<4,e=e>>8&16711935|(e&16711935)<<8,e=e>>16&65535|(e&65535)<<16,e>>>0),li=(e,t,r)=>{let n=0,s=e.length-1,i=-1;for(;n<=s;){const o=n+(s-n+1)/2|0;r(e[o])<=t?(i=o,n=o+1):s=o-1}return i},Wo=()=>{let e,t;return{promise:new Promise((n,s)=>{e=n,t=s}),resolve:e,reject:t}},Xt=e=>{throw new Error(`Unexpected value: ${e}`)},S0=(e,t,r)=>{const n=e.getUint8(t),s=e.getUint8(t+1),i=e.getUint8(t+2);return n<<16|s<<8|i},Ws=(e,t,r,n)=>{r=r>>>0,r=r&16777215,n?(e.setUint8(t,r&255),e.setUint8(t+1,r>>>8&255),e.setUint8(t+2,r>>>16&255)):(e.setUint8(t,r>>>16&255),e.setUint8(t+1,r>>>8&255),e.setUint8(t+2,r&255))},C0=(e,t,r,n)=>{r=_e(r,-8388608,8388607),r<0&&(r=r+16777216&16777215),Ws(e,t,r,n)},_e=(e,t,r)=>Math.max(t,Math.min(r,e)),P0=(e,t,r)=>e+(t-e)*r,M0="und",fi=(e,t)=>Math.round(e/t)*t,ui=(e,t)=>Math.round(e*t)/t,di=(e,t)=>Math.floor(e*t)/t,N0=e=>{let t=0;for(;e!==0;)e&=e-1,t++;return t},B0=/^[a-z]{3}$/,A0=e=>B0.test(e),Ut=1e6*(1+Number.EPSILON),_0=(e,t)=>{const r=e<0?-1:1;e=Math.abs(e);let n=0,s=1,i=1,o=0,a=e;for(;;){const l=Math.floor(a),f=l*i+n,c=l*o+s;if(c>t)return{num:r*i,den:o};if(n=i,s=o,i=f,o=c,a=1/(a-l),!isFinite(a))break}return{num:r*i,den:o}};class Lo{constructor(){this.currentPromise=Promise.resolve()}call(t){return this.currentPromise=this.currentPromise.then(t)}}let Fn=null;const I0=()=>{var e;return Fn!==null?Fn:Fn=!!(typeof navigator<"u"&&((e=navigator.vendor)!=null&&e.match(/apple/i)||/AppleWebKit/.test(navigator.userAgent)&&!/Chrome/.test(navigator.userAgent)||/\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent)))};let Vn=null;const gn=()=>{var e;return Vn!==null?Vn:Vn=typeof navigator<"u"&&((e=navigator.userAgent)==null?void 0:e.includes("Firefox"))};let Dn=null;const R0=()=>{var e;return Dn!==null?Dn:Dn=!!(typeof navigator<"u"&&((e=navigator.vendor)!=null&&e.includes("Google Inc")||/Chrome/.test(navigator.userAgent)))};let zn=null;const O0=()=>{if(zn!==null)return zn;if(typeof navigator>"u")return null;const e=/\bChrome\/(\d+)/.exec(navigator.userAgent);return e?zn=Number(e[1]):null},qo=e=>typeof globalThis.isSecureContext<"u"&&!globalThis.isSecureContext?`${e} is not available in this environment; this may be because this page is running in an insecure context. Try serving your page over HTTPS or use localhost.`:`${e} is not available in this environment.`,F0=e=>e instanceof DOMException&&e.name==="QuotaExceededError"&&/reclaimed/i.test(e.message),V0=(async()=>{})().constructor,Gt=e=>e instanceof V0||e instanceof Promise?!0:typeof(e==null?void 0:e.then)=="function",$o=function*(e){for(const t in e){const r=e[t];r!==void 0&&(yield{key:t,value:r})}},D0=()=>{Symbol.dispose??(Symbol.dispose=Symbol("Symbol.dispose"))},z0=e=>typeof e=="number"&&!Number.isNaN(e),j0=(e,t)=>{let r=-1,n=1/0;for(let s=0;s{C(Number.isInteger(e.num)),C(Number.isInteger(e.den)),C(e.den!==0);let t=Math.abs(e.num),r=Math.abs(e.den);for(;r!==0;){const s=t%r;t=r,r=s}const n=t||1;return{num:e.num/n,den:e.den/n}},jn=(e,t)=>{if(typeof e!="object"||!e)throw new TypeError(`${t} must be an object.`);if(!Number.isInteger(e.left)||e.left<0)throw new TypeError(`${t}.left must be a non-negative integer.`);if(!Number.isInteger(e.top)||e.top<0)throw new TypeError(`${t}.top must be a non-negative integer.`);if(!Number.isInteger(e.width)||e.width<0)throw new TypeError(`${t}.width must be a non-negative integer.`);if(!Number.isInteger(e.height)||e.height<0)throw new TypeError(`${t}.height must be a non-negative integer.`)},W0=e=>new Promise(t=>setTimeout(t,e)),hi=e=>Array.isArray(e)?e:[e];class Ls{constructor(){this._listeners=new Map}on(t,r,n){this._listeners.has(t)||this._listeners.set(t,new Set);const s={fn:r,once:(n==null?void 0:n.once)??!1};return this._listeners.get(t).add(s),()=>{var i;(i=this._listeners.get(t))==null||i.delete(s)}}_emit(...t){const[r,n]=t,s=this._listeners.get(r);if(s)for(const i of s){try{i.fn(n)}catch(o){console.error(o)}i.once&&s.delete(i)}}}const L0=e=>e!==null&&typeof e=="object"&&Object.getPrototypeOf(e)===Object.prototype&&Object.values(e).every(t=>typeof t=="string");/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */var vt;(function(e){e[e.Silent=0]="Silent",e[e.Errors=1]="Errors",e[e.Warnings=2]="Warnings",e[e.Info=3]="Info"})(vt||(vt={}));class ye{constructor(){}static get level(){return ye._level}static set level(t){if(t!==vt.Silent&&t!==vt.Errors&&t!==vt.Warnings&&t!==vt.Info)throw new TypeError("Invalid log level. Use one of the values of the LogLevel enum.");ye._level=t}static get _emitter(){return ye._emitterInstance??(ye._emitterInstance=new Ls)}static on(t,r,n){return ye._emitter.on(t,r,n)}static _error(...t){ye._emitter._emit("error",t),ye._level>=vt.Errors&&console.error(...t)}static _warn(...t){ye._emitter._emit("warn",t),ye._level>=vt.Warnings&&console.warn(...t)}static _info(...t){ye._emitter._emit("info",t),ye._level>=vt.Info&&console.info(...t)}}ye._level=vt.Info;ye._emitterInstance=null;/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class Ho{constructor(t,r){if(this.data=t,this.mimeType=r,!(t instanceof Uint8Array))throw new TypeError("data must be a Uint8Array.");if(typeof r!="string")throw new TypeError("mimeType must be a string.")}}class q0{constructor(t,r,n,s){if(this.data=t,this.mimeType=r,this.name=n,this.description=s,!(t instanceof Uint8Array))throw new TypeError("data must be a Uint8Array.");if(r!==void 0&&typeof r!="string")throw new TypeError("mimeType, when provided, must be a string.");if(n!==void 0&&typeof n!="string")throw new TypeError("name, when provided, must be a string.");if(s!==void 0&&typeof s!="string")throw new TypeError("description, when provided, must be a string.")}}const $0=e=>{if(!e||typeof e!="object")throw new TypeError("tags must be an object.");if(e.title!==void 0&&typeof e.title!="string")throw new TypeError("tags.title, when provided, must be a string.");if(e.description!==void 0&&typeof e.description!="string")throw new TypeError("tags.description, when provided, must be a string.");if(e.artist!==void 0&&typeof e.artist!="string")throw new TypeError("tags.artist, when provided, must be a string.");if(e.album!==void 0&&typeof e.album!="string")throw new TypeError("tags.album, when provided, must be a string.");if(e.albumArtist!==void 0&&typeof e.albumArtist!="string")throw new TypeError("tags.albumArtist, when provided, must be a string.");if(e.trackNumber!==void 0&&(!Number.isInteger(e.trackNumber)||e.trackNumber<=0))throw new TypeError("tags.trackNumber, when provided, must be a positive integer.");if(e.tracksTotal!==void 0&&(!Number.isInteger(e.tracksTotal)||e.tracksTotal<=0))throw new TypeError("tags.tracksTotal, when provided, must be a positive integer.");if(e.discNumber!==void 0&&(!Number.isInteger(e.discNumber)||e.discNumber<=0))throw new TypeError("tags.discNumber, when provided, must be a positive integer.");if(e.discsTotal!==void 0&&(!Number.isInteger(e.discsTotal)||e.discsTotal<=0))throw new TypeError("tags.discsTotal, when provided, must be a positive integer.");if(e.genre!==void 0&&typeof e.genre!="string")throw new TypeError("tags.genre, when provided, must be a string.");if(e.date!==void 0&&(!(e.date instanceof Date)||Number.isNaN(e.date.getTime())))throw new TypeError("tags.date, when provided, must be a valid Date.");if(e.beatsPerMinute!==void 0&&(!Number.isInteger(e.beatsPerMinute)||e.beatsPerMinute<=0))throw new TypeError("tags.beatsPerMinute, when provided, must be a positive integer.");if(e.lyrics!==void 0&&typeof e.lyrics!="string")throw new TypeError("tags.lyrics, when provided, must be a string.");if(e.images!==void 0){if(!Array.isArray(e.images))throw new TypeError("tags.images, when provided, must be an array.");for(const t of e.images){if(!t||typeof t!="object")throw new TypeError("Each image in tags.images must be an object.");if(!(t.data instanceof Uint8Array))throw new TypeError("Each image.data must be a Uint8Array.");if(typeof t.mimeType!="string")throw new TypeError("Each image.mimeType must be a string.");if(!["coverFront","coverBack","unknown"].includes(t.kind))throw new TypeError("Each image.kind must be 'coverFront', 'coverBack', or 'unknown'.")}}if(e.comment!==void 0&&typeof e.comment!="string")throw new TypeError("tags.comment, when provided, must be a string.");if(e.raw!==void 0){if(!e.raw||typeof e.raw!="object")throw new TypeError("tags.raw, when provided, must be an object.");for(const t of Object.values(e.raw))if(t!==null&&typeof t!="string"&&!(Array.isArray(t)&&t.every(r=>typeof r=="string"))&&!(t instanceof Uint8Array)&&!(t instanceof Ho)&&!(t instanceof q0)&&!L0(t))throw new TypeError("Each value in tags.raw must be a string, string array, Uint8Array, RichImageData, AttachedFile, Record, or null.")}},U0=e=>{if(!e||typeof e!="object")throw new TypeError("disposition must be an object.");if(e.default!==void 0&&typeof e.default!="boolean")throw new TypeError("disposition.default must be a boolean.");if(e.primary!==void 0&&typeof e.primary!="boolean")throw new TypeError("disposition.primary must be a boolean.");if(e.forced!==void 0&&typeof e.forced!="boolean")throw new TypeError("disposition.forced must be a boolean.");if(e.original!==void 0&&typeof e.original!="boolean")throw new TypeError("disposition.original must be a boolean.");if(e.commentary!==void 0&&typeof e.commentary!="boolean")throw new TypeError("disposition.commentary must be a boolean.");if(e.hearingImpaired!==void 0&&typeof e.hearingImpaired!="boolean")throw new TypeError("disposition.hearingImpaired must be a boolean.");if(e.visuallyImpaired!==void 0&&typeof e.visuallyImpaired!="boolean")throw new TypeError("disposition.visuallyImpaired must be a boolean.")};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class Te{constructor(t){this.bytes=t,this.pos=0}seekToByte(t){this.pos=8*t}readBit(){const t=Math.floor(this.pos/8),r=this.bytes[t]??0,n=7-(this.pos&7),s=(r&1<>n;return this.pos++,s}readBits(t){if(t===1)return this.readBit();let r=0;for(let n=0;n>n-s-1<0&&this.writeBits(s,r.readBits(s))}readAlignedByte(){if(this.pos%8!==0)throw new Error("Bitstream is not byte-aligned.");const t=this.pos/8,r=this.bytes[t]??0;return this.pos+=8,r}skipBits(t){this.pos+=t}getBitsLeft(){return this.bytes.length*8-this.pos}clone(){const t=new Te(this.bytes);return t.pos=this.pos,t}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const xn=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],qs=[-1,1,2,3,4,5,6,8],H0=e=>{if(!e||e.byteLength<2)throw new TypeError("AAC description must be at least 2 bytes long.");const t=new Te(e),r=Wn(t),{frequencyIndex:n,sampleRate:s}=Ln(t),i=t.readBits(4);let o=null;i>=1&&i<=7&&(o=qs[i]);let a=r,l=!1,f=s;if(r===5||r===29)l=r===29,f=Ln(t).sampleRate,a=Wn(t),a===22&&t.skipBits(4);else for(;t.getBitsLeft()>15;){const c=t.pos;if(t.readBits(11)!==695){t.pos=c+1;continue}Wn(t)===5&&t.readBits(1)&&(f=Ln(t).sampleRate,t.getBitsLeft()>11&&t.readBits(11)===1352&&(l=!!t.readBits(1)));break}return o!==null&&o>1&&(l=!1),{objectType:r,coreObjectType:a,frequencyIndex:n,channelConfiguration:i,outputSampleRate:f,outputNumberOfChannels:l&&o===1?2:o}},Wn=e=>{const t=e.readBits(5);return t===31?32+e.readBits(6):t},Ln=e=>{const t=e.readBits(4);return t===15?{frequencyIndex:t,sampleRate:e.readBits(24)}:{frequencyIndex:t,sampleRate:t{const t=e.objectType===5||e.objectType===29,r=e.objectType===29,n=t?e.outputSampleRate/2:e.outputSampleRate,s=r?1:e.outputNumberOfChannels,i=qs.indexOf(s);if(i===-1)throw new TypeError(`Unsupported number of channels: ${e.outputNumberOfChannels}`);let o=16;e.objectType>=32&&(o+=6),Ts(n)===15&&(o+=24),t&&(o+=9,Ts(e.outputSampleRate)===15&&(o+=24));const a=Math.ceil(o/8),l=new Uint8Array(a),f=new Te(l);return mi(f,e.objectType),pi(f,n),f.writeBits(4,i),t&&(pi(f,e.outputSampleRate),mi(f,2)),f.writeBits(3,0),l},mi=(e,t)=>{t<32?e.writeBits(5,t):(e.writeBits(5,31),e.writeBits(6,t-32))},pi=(e,t)=>{const r=Ts(t);e.writeBits(4,r),r===15&&e.writeBits(24,t)},Ts=e=>{const t=xn.indexOf(e);return t===-1?15:t};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const G0=[48e3,44100,32e3],X0=[24e3,22050,16e3];/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Q0="1.61.6",qn=`Mediabunny v${Q0}`;/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */var _t;(function(e){e[e.NON_IDR_SLICE=1]="NON_IDR_SLICE",e[e.SLICE_DPA=2]="SLICE_DPA",e[e.SLICE_DPB=3]="SLICE_DPB",e[e.SLICE_DPC=4]="SLICE_DPC",e[e.IDR=5]="IDR",e[e.SEI=6]="SEI",e[e.SPS=7]="SPS",e[e.PPS=8]="PPS",e[e.AUD=9]="AUD",e[e.SPS_EXT=13]="SPS_EXT"})(_t||(_t={}));var tt;(function(e){e[e.RASL_N=8]="RASL_N",e[e.RASL_R=9]="RASL_R",e[e.BLA_W_LP=16]="BLA_W_LP",e[e.RSV_IRAP_VCL23=23]="RSV_IRAP_VCL23",e[e.VPS_NUT=32]="VPS_NUT",e[e.SPS_NUT=33]="SPS_NUT",e[e.PPS_NUT=34]="PPS_NUT",e[e.AUD_NUT=35]="AUD_NUT",e[e.PREFIX_SEI_NUT=39]="PREFIX_SEI_NUT",e[e.SUFFIX_SEI_NUT=40]="SUFFIX_SEI_NUT"})(tt||(tt={}));const Kr=function*(e){let t=0,r=-1;for(;t=e.length-2)break;t=n;let s=0;if(t+3r&&(yield{offset:r,length:t-r}),r=t+s,t=r}r!==-1&&r{if(t.description){const s=(Ie(t.description)[4]&3)+1;return Xo(e,s)}else return Kr(e)},Qo=e=>e&31,Cn=e=>{const t=[],r=e.length;for(let n=0;n{const r=e.reduce((i,o)=>i+t+o.byteLength,0),n=new Uint8Array(r);let s=0;for(const i of e){const o=new DataView(n.buffer,n.byteOffset,n.byteLength);switch(t){case 1:o.setUint8(s,i.byteLength);break;case 2:o.setUint16(s,i.byteLength,!1);break;case 3:Ws(o,s,i.byteLength,!1);break;case 4:o.setUint32(s,i.byteLength,!1);break}s+=t,n.set(i,s),s+=i.byteLength}return n},Ko=e=>{try{const t=[],r=[],n=[];for(const a of Kr(e)){const l=e.subarray(a.offset,a.offset+a.length),f=Qo(l[0]);f===_t.SPS?t.push(l):f===_t.PPS?r.push(l):f===_t.SPS_EXT&&n.push(l)}if(t.length===0||r.length===0)return null;const s=t[0],i=J0(s);C(i!==null);const o=i.profileIdc===100||i.profileIdc===110||i.profileIdc===122||i.profileIdc===144;return{configurationVersion:1,avcProfileIndication:i.profileIdc,profileCompatibility:i.constraintFlags,avcLevelIndication:i.levelIdc,lengthSizeMinusOne:3,sequenceParameterSets:t,pictureParameterSets:r,chromaFormat:o?i.chromaFormatIdc:null,bitDepthLumaMinus8:o?i.bitDepthLumaMinus8:null,bitDepthChromaMinus8:o?i.bitDepthChromaMinus8:null,sequenceParameterSetExt:o?n:null}}catch(t){return ye._error("Error building AVC Decoder Configuration Record:",t),null}},Yo=e=>{const t=[];t.push(e.configurationVersion),t.push(e.avcProfileIndication),t.push(e.profileCompatibility),t.push(e.avcLevelIndication),t.push(252|e.lengthSizeMinusOne&3),t.push(224|e.sequenceParameterSets.length&31);for(const r of e.sequenceParameterSets){const n=r.byteLength;t.push(n>>8),t.push(n&255);for(let s=0;s>8),t.push(n&255);for(let s=0;s>8),t.push(n&255);for(let s=0;s{try{const t=nt(e);let r=0;const n=t.getUint8(r++),s=t.getUint8(r++),i=t.getUint8(r++),o=t.getUint8(r++),a=t.getUint8(r++)&3,l=t.getUint8(r++)&31,f=[];for(let d=0;d{try{const t=Cn(e),r=new Te(t);if(r.skipBits(1),r.skipBits(2),r.readBits(5)!==7)return null;const s=r.readAlignedByte(),i=r.readAlignedByte(),o=r.readAlignedByte();D(r);let a=1,l=0,f=0,c=0;if((s===100||s===110||s===122||s===244||s===44||s===83||s===86||s===118||s===128)&&(a=D(r),a===3&&(c=r.readBits(1)),l=D(r),f=D(r),r.skipBits(1),r.readBits(1))){for(let j=0;j<(a!==3?8:12);j++)if(r.readBits(1)){const re=j<6?16:64;let ie=8,O=8;for(let F=0;FF.level>=o)??ze(vn),O=Math.min(Math.floor(ie.maxDpbMbs/(j*re)),16);B=O,V=O}}return C(V!==null),{emulationUnpreventedBytes:t,profileIdc:s,constraintFlags:i,levelIdc:o,frameMbsOnlyFlag:y,chromaFormatIdc:a,bitDepthLumaMinus8:l,bitDepthChromaMinus8:f,codedWidth:m,codedHeight:b,displayWidth:p,displayHeight:w,pixelAspectRatio:S,colourPrimaries:k,matrixCoefficients:A,transferCharacteristics:T,fullRangeFlag:N,numReorderFrames:B,maxDecFrameBuffering:V,vuiParametersFlagBitOffset:J,bitstreamRestrictionFlagBitOffset:W,bitstreamRestrictionFlag:L}}catch(t){return ye._error("Error parsing AVC SPS:",t),null}},bi=e=>{const t=D(e);e.skipBits(4),e.skipBits(4);for(let r=0;r<=t;r++)D(e),D(e),e.skipBits(1);e.skipBits(5),e.skipBits(5),e.skipBits(5),e.skipBits(5)},ec=(e,t)=>{if(t.description){const s=(Ie(t.description)[21]&3)+1;return Xo(e,s)}else return Kr(e)},Ss=e=>e>>1&63,tc=e=>{try{const t=new Te(Cn(e));t.skipBits(16),t.readBits(4);const r=t.readBits(3),n=t.readBits(1),{general_profile_space:s,general_tier_flag:i,general_profile_idc:o,general_profile_compatibility_flags:a,general_constraint_indicator_flags:l,general_level_idc:f}=rc(t,r);D(t);const c=D(t);let u=0;c===3&&(u=t.readBits(1));const h=D(t),d=D(t);let m=h,b=d;if(t.readBits(1)){const L=D(t),J=D(t),U=D(t),I=D(t);let j=1,Z=1;const re=u===0?c:0;re===1?(j=2,Z=2):re===2&&(j=2,Z=1),m-=(L+J)*j,b-=(U+I)*Z}const p=D(t),w=D(t);D(t);const g=t.readBits(1)?0:r;let k=0;for(let L=g;L<=r;L++)D(t),k=D(t),D(t);D(t),D(t),D(t),D(t),D(t),D(t),t.readBits(1)&&t.readBits(1)&&nc(t),t.skipBits(1),t.skipBits(1),t.readBits(1)&&(t.skipBits(4),t.skipBits(4),D(t),D(t),t.skipBits(1));const T=D(t);if(sc(t,T),t.readBits(1)){const L=D(t);for(let J=0;J{try{const t=[],r=[],n=[],s=[];for(const f of Kr(e)){const c=e.subarray(f.offset,f.offset+f.length),u=Ss(c[0]);u===tt.VPS_NUT?t.push(c):u===tt.SPS_NUT?r.push(c):u===tt.PPS_NUT?n.push(c):(u===tt.PREFIX_SEI_NUT||u===tt.SUFFIX_SEI_NUT)&&s.push(c)}if(r.length===0||n.length===0)return null;const i=tc(r[0]);if(!i)return null;let o=0;if(n.length>0){const f=n[0],c=new Te(Cn(f));c.skipBits(16),D(c),D(c),c.skipBits(1),c.skipBits(1),c.skipBits(3),c.skipBits(1),c.skipBits(1),D(c),D(c),At(c),c.skipBits(1),c.skipBits(1),c.readBits(1)&&D(c),At(c),At(c),c.skipBits(1),c.skipBits(1),c.skipBits(1),c.skipBits(1);const u=c.readBits(1),h=c.readBits(1);!u&&!h?o=0:u&&!h?o=2:!u&&h?o=3:o=0}const a=[...t.length?[{arrayCompleteness:1,nalUnitType:tt.VPS_NUT,nalUnits:t}]:[],...r.length?[{arrayCompleteness:1,nalUnitType:tt.SPS_NUT,nalUnits:r}]:[],...n.length?[{arrayCompleteness:1,nalUnitType:tt.PPS_NUT,nalUnits:n}]:[],...s.length?[{arrayCompleteness:1,nalUnitType:Ss(s[0][0]),nalUnits:s}]:[]];return{configurationVersion:1,generalProfileSpace:i.generalProfileSpace,generalTierFlag:i.generalTierFlag,generalProfileIdc:i.generalProfileIdc,generalProfileCompatibilityFlags:i.generalProfileCompatibilityFlags,generalConstraintIndicatorFlags:i.generalConstraintIndicatorFlags,generalLevelIdc:i.generalLevelIdc,minSpatialSegmentationIdc:i.minSpatialSegmentationIdc,parallelismType:o,chromaFormatIdc:i.chromaFormatIdc,bitDepthLumaMinus8:i.bitDepthLumaMinus8,bitDepthChromaMinus8:i.bitDepthChromaMinus8,avgFrameRate:0,constantFrameRate:0,numTemporalLayers:i.spsMaxSubLayersMinus1+1,temporalIdNested:i.spsTemporalIdNestingFlag,lengthSizeMinusOne:3,arrays:a}}catch(t){return ye._error("Error building HEVC Decoder Configuration Record:",t),null}},rc=(e,t)=>{const r=e.readBits(2),n=e.readBits(1),s=e.readBits(5);let i=0;for(let c=0;c<32;c++)i=i<<1|e.readBits(1);const o=new Uint8Array(6);for(let c=0;c<6;c++)o[c]=e.readBits(8);const a=e.readBits(8),l=[],f=[];for(let c=0;c0)for(let c=t;c<8;c++)e.skipBits(2);for(let c=0;c{for(let t=0;t<4;t++)for(let r=0;r<(t===3?2:6);r++)if(!e.readBits(1))D(e);else{const s=Math.min(64,1<<4+(t<<1));t>1&&At(e);for(let i=0;i{const r=[];for(let n=0;n{let s=0,i=0,o=0;if(t!==0&&(i=e.readBits(1)),i){if(t===r){const l=D(e);o=t-(l+1)}else o=t-1;e.readBits(1),D(e);const a=n[o]??0;for(let l=0;l<=a;l++)e.readBits(1)||e.readBits(1);s=n[o]}else{const a=D(e),l=D(e);for(let f=0;f{let r=2,n=2,s=2,i=0,o=0,a={num:1,den:1};if(e.readBits(1)){const l=e.readBits(8);if(l===255)a={num:e.readBits(16),den:e.readBits(16)};else{const f=Zo[l];f&&(a=f)}}return e.readBits(1)&&e.readBits(1),e.readBits(1)&&(e.readBits(3),i=e.readBits(1),e.readBits(1)&&(r=e.readBits(8),n=e.readBits(8),s=e.readBits(8))),e.readBits(1)&&(D(e),D(e)),e.readBits(1),e.readBits(1),e.readBits(1),e.readBits(1)&&(D(e),D(e),D(e),D(e)),e.readBits(1)&&(e.readBits(32),e.readBits(32),e.readBits(1)&&D(e),e.readBits(1)&&ac(e,!0,t)),e.readBits(1)&&(e.readBits(1),e.readBits(1),e.readBits(1),o=D(e),D(e),D(e),D(e),D(e)),{pixelAspectRatio:a,colourPrimaries:r,transferCharacteristics:n,matrixCoefficients:s,fullRangeFlag:i,minSpatialSegmentationIdc:o}},ac=(e,t,r)=>{let n=!1,s=!1,i=!1;n=e.readBits(1)===1,s=e.readBits(1)===1,(n||s)&&(i=e.readBits(1)===1,i&&(e.readBits(8),e.readBits(5),e.readBits(1),e.readBits(5)),e.readBits(4),e.readBits(4),i&&e.readBits(4),e.readBits(5),e.readBits(5),e.readBits(5));for(let o=0;o<=r;o++){const a=e.readBits(1)===1;let l=!0;a||(l=e.readBits(1)===1);let f=!1;l?D(e):f=e.readBits(1)===1;let c=1;f||(c=D(e)+1),n&&wi(e,c,i),s&&wi(e,c,i)}},wi=(e,t,r)=>{for(let n=0;n{const t=[];t.push(e.configurationVersion),t.push((e.generalProfileSpace&3)<<6|(e.generalTierFlag&1)<<5|e.generalProfileIdc&31),t.push(e.generalProfileCompatibilityFlags>>>24&255),t.push(e.generalProfileCompatibilityFlags>>>16&255),t.push(e.generalProfileCompatibilityFlags>>>8&255),t.push(e.generalProfileCompatibilityFlags&255),t.push(...e.generalConstraintIndicatorFlags),t.push(e.generalLevelIdc&255),t.push(240|e.minSpatialSegmentationIdc>>8&15),t.push(e.minSpatialSegmentationIdc&255),t.push(252|e.parallelismType&3),t.push(252|e.chromaFormatIdc&3),t.push(248|e.bitDepthLumaMinus8&7),t.push(248|e.bitDepthChromaMinus8&7),t.push(e.avgFrameRate>>8&255),t.push(e.avgFrameRate&255),t.push((e.constantFrameRate&3)<<6|(e.numTemporalLayers&7)<<3|(e.temporalIdNested&1)<<2|e.lengthSizeMinusOne&3),t.push(e.arrays.length&255);for(const r of e.arrays){t.push((r.arrayCompleteness&1)<<7|0|r.nalUnitType&63),t.push(r.nalUnits.length>>8&255),t.push(r.nalUnits.length&255);for(const n of r.nalUnits){t.push(n.length>>8&255),t.push(n.length&255);for(let s=0;s{const t=new Te(e);if(t.readBits(2)!==2)return null;const n=t.readBits(1),i=(t.readBits(1)<<1)+n;if(i===3&&t.skipBits(1),t.readBits(1)===1||t.readBits(1)!==0||(t.skipBits(2),t.readBits(24)!==4817730))return null;let f=8;i>=2&&(f=t.readBits(1)?12:10);const c=t.readBits(3);let u=0,h=0;if(c!==7)if(h=t.readBits(1),i===1||i===3){const S=t.readBits(1),B=t.readBits(1);u=!S&&!B?3:S&&!B?2:1,t.skipBits(1)}else u=1;else u=3,h=1;const d=t.readBits(16),m=t.readBits(16),b=d+1,p=m+1,w=b*p;let y=ze(Ht).level;for(const N of Ht)if(w<=N.maxPictureSize){y=N.level;break}const g=lc[c],k=(g==null?void 0:g.colourPrimaries)??2,T=(g==null?void 0:g.transferCharacteristics)??2,A=(g==null?void 0:g.matrixCoefficients)??2;return{profile:i,level:y,bitDepth:f,chromaSubsampling:u,videoFullRangeFlag:h,colourPrimaries:k,transferCharacteristics:T,matrixCoefficients:A}},ea=function*(e){const t=new Te(e),r=()=>{let n=0;for(let s=0;s<8;s++){const i=t.readAlignedByte();if(n+=(i&127)*2**(s*7),!(i&128))break;if(s===7&&i&128)return null}return n>2**32-1?null:n};for(;t.getBitsLeft()>=8;){t.skipBits(1);const n=t.readBits(4),s=t.readBits(1),i=t.readBits(1);t.skipBits(1),s&&t.skipBits(8);let o;if(i){const a=r();if(a===null)return;o=a}else o=Math.floor(t.getBitsLeft()/8);C(t.pos%8===0),yield{type:n,data:e.subarray(t.pos/8,t.pos/8+o)},t.skipBits(o*8)}},uc=e=>{for(const{type:t,data:r}of ea(e)){if(t!==1)continue;const n=new Te(r),s=n.readBits(3);n.readBits(1);const i=n.readBits(1);let o=0,a=0,l=0;if(i)o=n.readBits(5);else{const B=n.readBits(1);let V=0;if(B){if(n.skipBits(32),n.skipBits(32),n.readBits(1)){let U=0;for(;U<32&&!n.readBits(1);)U++;U<32&&n.skipBits(U)}V=n.readBits(1),V&&(l=n.readBits(5),n.skipBits(32),n.skipBits(5),n.skipBits(5))}const W=n.readBits(1),L=n.readBits(5);for(let J=0;J<=L;J++){n.skipBits(12);const U=n.readBits(5);if(J===0&&(o=U),U>7){const I=n.readBits(1);J===0&&(a=I)}if(V&&n.readBits(1)){const j=l+1;n.skipBits(j),n.skipBits(j),n.skipBits(1)}W&&n.readBits(1)&&n.skipBits(4)}}const f=n.readBits(4),c=n.readBits(4),u=f+1;n.skipBits(u);const h=c+1;n.skipBits(h);let d=0;if(i?d=0:d=n.readBits(1),d&&(n.skipBits(4),n.skipBits(3)),n.skipBits(1),n.skipBits(1),n.skipBits(1),!i){n.skipBits(1),n.skipBits(1),n.skipBits(1),n.skipBits(1);const B=n.readBits(1);B&&(n.skipBits(1),n.skipBits(1));const V=n.readBits(1);let W=0;V?W=2:W=n.readBits(1),W>0&&(n.readBits(1)||n.skipBits(1)),B&&n.skipBits(3)}n.skipBits(1),n.skipBits(1),n.skipBits(1);const m=n.readBits(1);let b=8;s===2&&m?b=n.readBits(1)?12:10:s<=2&&(b=m?10:8);let p=0;s!==1&&(p=n.readBits(1));let w=2,y=2,g=2;n.readBits(1)&&(w=n.readBits(8),y=n.readBits(8),g=n.readBits(8));let T=0,A=1,N=1,S=0;return p?T=n.readBits(1):w===1&&y===13&&g===0?(T=1,A=0,N=0):(T=n.readBits(1),s===0?(A=1,N=1):s===1?(A=0,N=0):b===12?(A=n.readBits(1),N=A?n.readBits(1):0):(A=1,N=0),A&&N&&(S=n.readBits(2))),{profile:s,level:o,tier:a,bitDepth:b,monochrome:p,chromaSubsamplingX:A,chromaSubsamplingY:N,chromaSamplePosition:S,videoFullRangeFlag:T,colourPrimaries:w,transferCharacteristics:y,matrixCoefficients:g}}return null},dc=e=>e.colourPrimaries!==2||e.transferCharacteristics!==2||e.matrixCoefficients!==2,hc=e=>{const t=nt(e),r=t.getUint8(9),n=t.getUint16(10,!0),s=t.getUint32(12,!0),i=t.getInt16(16,!0),o=t.getUint8(18);let a=null;return o&&(a=e.subarray(19,21+r)),{outputChannelCount:r,preSkip:n,inputSampleRate:s,outputGain:i,channelMappingFamily:o,channelMappingTable:a}},mc=(e,t,r)=>{switch(e){case"avc":{for(const n of K0(r,t)){const s=r[n.offset],i=Qo(s);if(i>=_t.NON_IDR_SLICE&&i<=_t.SLICE_DPC)return"delta";if(i===_t.IDR)return"key";if(i===_t.SEI&&!R0()){const o=r.subarray(n.offset,n.offset+n.length),a=Cn(o);let l=1;do{let f=0;for(;;){const h=a[l++];if(h===void 0||(f+=h,h<255))break}let c=0;for(;;){const h=a[l++];if(h===void 0||(c+=h,h<255))break}if(f===6){const h=new Te(a);h.pos=8*l;const d=D(h),m=h.readBits(1);if(d===0&&m===1)return"key"}l+=c}while(l{if(e.length<7||e[0]!==11||e[1]!==119)return null;const t=new Te(e);t.skipBits(16),t.skipBits(16);const r=t.readBits(2);if(r===3)return null;const n=t.readBits(6),s=t.readBits(5);if(s>8)return null;const i=t.readBits(3),o=t.readBits(3);(o&1)!==0&&o!==1&&t.skipBits(2),(o&4)!==0&&t.skipBits(2),o===2&&t.skipBits(2);const a=t.readBits(1),l=Math.floor(n/2);return{fscod:r,bsid:s,bsmod:i,acmod:o,lfeon:a,bitRateCode:l}},bc=[1,2,3,6],wc=e=>{if(e.length<6||e[0]!==11||e[1]!==119)return null;const t=new Te(e);t.skipBits(16);const r=t.readBits(2);if(t.skipBits(3),r!==0&&r!==2)return null;const n=t.readBits(11),s=t.readBits(2);let i=0,o;s===3?(i=t.readBits(2),o=3):o=t.readBits(2);const a=t.readBits(3),l=t.readBits(1),f=t.readBits(5);if(f<11||f>16)return null;const c=bc[o];let u;return s<3?u=G0[s]/1e3:u=X0[i]/1e3,{dataRate:Math.round((n+1)*u/(c*16)),substreams:[{fscod:s,fscod2:i,bsid:f,bsmod:0,acmod:a,lfeon:l,numDepSub:0,chanLoc:0}]}},yc=1683496997,gc=18,xc=10,xi=32,vc=20,kc=8,Ec=[0,8e3,16e3,32e3,0,0,11025,22050,44100,0,0,12e3,24e3,48e3,96e3,192e3],Tc=[32e3,56e3,64e3,96e3,112e3,128e3,192e3,224e3,256e3,32e4,384e3,448e3,512e3,576e3,64e4,768e3,96e4,1024e3,1152e3,128e4,1344e3,1408e3,1411200,1472e3,1536e3,192e4,2048e3,3072e3,384e4,0,0,0],Sc=[16,16,20,20,0,24,24,0],vi=[1,2,2,2,2,3,3,4,4,5,6,6,6,7,8,8],Cc=[1,2,2,2,2,3,18,19,6,7,518,323,83,519,582,535],Pc=8,Mc=[32e3,44100,48e3,0],Nc=[8e3,16e3,32e3,64e3,128e3,22050,44100,88200,176400,352800,12e3,24e3,48e3,96e3,192e3,384e3],Bc=[512,1024,2048,4096],Ac=e=>{const t=_c(e),r=nt(e);let n=t?Math.ceil(t.frameSize/4)*4:0,s=null;for(;n+4<=e.length&&r.getUint32(n)===yc;){const o=Ic(e.subarray(n));if(!o)break;s??(s=o),n+=o.frameSize}if(t)return{frameSize:s?n:t.frameSize,sampleRate:t.sampleRate,numberOfChannels:t.numberOfChannels,sampleCount:t.sampleCount,channelLayout:t.channelLayout,pcmResolution:t.pcmResolution,bitRate:t.bitRate,core:t,hasExtensions:s!==null};if(!(s!=null&&s.asset))return null;const{asset:i}=s;return{frameSize:n,sampleRate:i.sampleRate,numberOfChannels:i.numberOfChannels,sampleCount:i.sampleCount,channelLayout:i.channelLayout,pcmResolution:i.pcmResolution,bitRate:0,core:null,hasExtensions:!0}},_c=e=>{if(e.length=vi.length)return null;const o=Ec[t.readBits(4)];if(o===0)return null;const a=Tc[t.readBits(5)];if(t.readBits(1)!==0)return null;t.skipBits(4),t.skipBits(5);const l=t.readBits(2);if(l===3)return null;t.skipBits(1),r&&t.skipBits(16),t.skipBits(7);const f=Sc[t.readBits(3)];if(f===0)return null;const c=l!==0;return{frameSize:s,sampleRate:o,numberOfChannels:vi[i]+(c?1:0),sampleCount:n*xi,channelLayout:Cc[i]|(c?Pc:0),amode:i,lfePresent:c,bitRate:a,pcmResolution:f}},Ic=e=>{if(e.length2&&t.skipBits(1),b>6&&t.skipBits(1),t.readBits(1))){const w=t.readBits(2)+1<<2;p=t.readBits(w)}return l===0||t.getBitsLeft()<0?a:{frameSize:o,asset:{sampleRate:m,numberOfChannels:b,sampleCount:Math.round(f*m/l),channelLayout:p,pcmResolution:d}}},Rc=e=>{var i,o;const t=new Uint8Array(vc),r=nt(t);r.setUint32(0,e.sampleRate),r.setUint32(4,e.bitRate),r.setUint32(8,e.bitRate),t[12]=e.pcmResolution;const n=e.core&&!e.hasExtensions?1:0,s=new Te(t);return s.seekToByte(13),s.writeBits(2,Math.max(Bc.indexOf(e.sampleCount),0)),s.writeBits(5,n),s.writeBits(1,(i=e.core)!=null&&i.lfePresent?1:0),s.writeBits(6,((o=e.core)==null?void 0:o.amode)??0),s.writeBits(14,e.core?e.core.frameSize-1:0),s.writeBits(1,0),s.writeBits(3,0),s.writeBits(16,e.channelLayout),s.writeBits(1,0),s.writeBits(1,0),s.writeBits(1,0),s.writeBits(5,0),t};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Ot=["avc","hevc","vp9","av1","vp8","prores"],pt=["pcm-s16","pcm-s16be","pcm-s24","pcm-s24be","pcm-s32","pcm-s32be","pcm-f32","pcm-f32be","pcm-f64","pcm-f64be","pcm-u8","pcm-s8","ulaw","alaw"],$s=["aac","opus","mp3","vorbis","flac","ac3","eac3","dts"],sr=[...$s,...pt],Gr=["webvtt"],vn=[{maxMacroblocks:99,maxBitrate:64e3,maxDpbMbs:396,level:10},{maxMacroblocks:396,maxBitrate:192e3,maxDpbMbs:900,level:11},{maxMacroblocks:396,maxBitrate:384e3,maxDpbMbs:2376,level:12},{maxMacroblocks:396,maxBitrate:768e3,maxDpbMbs:2376,level:13},{maxMacroblocks:396,maxBitrate:2e6,maxDpbMbs:2376,level:20},{maxMacroblocks:792,maxBitrate:4e6,maxDpbMbs:4752,level:21},{maxMacroblocks:1620,maxBitrate:4e6,maxDpbMbs:8100,level:22},{maxMacroblocks:1620,maxBitrate:1e7,maxDpbMbs:8100,level:30},{maxMacroblocks:3600,maxBitrate:14e6,maxDpbMbs:18e3,level:31},{maxMacroblocks:5120,maxBitrate:2e7,maxDpbMbs:20480,level:32},{maxMacroblocks:8192,maxBitrate:2e7,maxDpbMbs:32768,level:40},{maxMacroblocks:8192,maxBitrate:5e7,maxDpbMbs:32768,level:41},{maxMacroblocks:8704,maxBitrate:5e7,maxDpbMbs:34816,level:42},{maxMacroblocks:22080,maxBitrate:135e6,maxDpbMbs:110400,level:50},{maxMacroblocks:36864,maxBitrate:24e7,maxDpbMbs:184320,level:51},{maxMacroblocks:36864,maxBitrate:24e7,maxDpbMbs:184320,level:52},{maxMacroblocks:139264,maxBitrate:24e7,maxDpbMbs:696320,level:60},{maxMacroblocks:139264,maxBitrate:48e7,maxDpbMbs:696320,level:61},{maxMacroblocks:139264,maxBitrate:8e8,maxDpbMbs:696320,level:62}],ki=[{maxPictureSize:36864,maxBitrate:128e3,tier:"L",level:30},{maxPictureSize:122880,maxBitrate:15e5,tier:"L",level:60},{maxPictureSize:245760,maxBitrate:3e6,tier:"L",level:63},{maxPictureSize:552960,maxBitrate:6e6,tier:"L",level:90},{maxPictureSize:983040,maxBitrate:1e7,tier:"L",level:93},{maxPictureSize:2228224,maxBitrate:12e6,tier:"L",level:120},{maxPictureSize:2228224,maxBitrate:3e7,tier:"H",level:120},{maxPictureSize:2228224,maxBitrate:2e7,tier:"L",level:123},{maxPictureSize:2228224,maxBitrate:5e7,tier:"H",level:123},{maxPictureSize:8912896,maxBitrate:25e6,tier:"L",level:150},{maxPictureSize:8912896,maxBitrate:1e8,tier:"H",level:150},{maxPictureSize:8912896,maxBitrate:4e7,tier:"L",level:153},{maxPictureSize:8912896,maxBitrate:16e7,tier:"H",level:153},{maxPictureSize:8912896,maxBitrate:6e7,tier:"L",level:156},{maxPictureSize:8912896,maxBitrate:24e7,tier:"H",level:156},{maxPictureSize:35651584,maxBitrate:6e7,tier:"L",level:180},{maxPictureSize:35651584,maxBitrate:24e7,tier:"H",level:180},{maxPictureSize:35651584,maxBitrate:12e7,tier:"L",level:183},{maxPictureSize:35651584,maxBitrate:48e7,tier:"H",level:183},{maxPictureSize:35651584,maxBitrate:24e7,tier:"L",level:186},{maxPictureSize:35651584,maxBitrate:8e8,tier:"H",level:186}],Ht=[{maxPictureSize:36864,maxBitrate:2e5,level:10},{maxPictureSize:73728,maxBitrate:8e5,level:11},{maxPictureSize:122880,maxBitrate:18e5,level:20},{maxPictureSize:245760,maxBitrate:36e5,level:21},{maxPictureSize:552960,maxBitrate:72e5,level:30},{maxPictureSize:983040,maxBitrate:12e6,level:31},{maxPictureSize:2228224,maxBitrate:18e6,level:40},{maxPictureSize:2228224,maxBitrate:3e7,level:41},{maxPictureSize:8912896,maxBitrate:6e7,level:50},{maxPictureSize:8912896,maxBitrate:12e7,level:51},{maxPictureSize:8912896,maxBitrate:18e7,level:52},{maxPictureSize:35651584,maxBitrate:18e7,level:60},{maxPictureSize:35651584,maxBitrate:24e7,level:61},{maxPictureSize:35651584,maxBitrate:48e7,level:62}],Ei=[{maxPictureSize:147456,maxBitrate:15e5,tier:"M",level:0},{maxPictureSize:278784,maxBitrate:3e6,tier:"M",level:1},{maxPictureSize:665856,maxBitrate:6e6,tier:"M",level:4},{maxPictureSize:1065024,maxBitrate:1e7,tier:"M",level:5},{maxPictureSize:2359296,maxBitrate:12e6,tier:"M",level:8},{maxPictureSize:2359296,maxBitrate:3e7,tier:"H",level:8},{maxPictureSize:2359296,maxBitrate:2e7,tier:"M",level:9},{maxPictureSize:2359296,maxBitrate:5e7,tier:"H",level:9},{maxPictureSize:8912896,maxBitrate:3e7,tier:"M",level:12},{maxPictureSize:8912896,maxBitrate:1e8,tier:"H",level:12},{maxPictureSize:8912896,maxBitrate:4e7,tier:"M",level:13},{maxPictureSize:8912896,maxBitrate:16e7,tier:"H",level:13},{maxPictureSize:8912896,maxBitrate:6e7,tier:"M",level:14},{maxPictureSize:8912896,maxBitrate:24e7,tier:"H",level:14},{maxPictureSize:35651584,maxBitrate:6e7,tier:"M",level:15},{maxPictureSize:35651584,maxBitrate:24e7,tier:"H",level:15},{maxPictureSize:35651584,maxBitrate:6e7,tier:"M",level:16},{maxPictureSize:35651584,maxBitrate:24e7,tier:"H",level:16},{maxPictureSize:35651584,maxBitrate:1e8,tier:"M",level:17},{maxPictureSize:35651584,maxBitrate:48e7,tier:"H",level:17},{maxPictureSize:35651584,maxBitrate:16e7,tier:"M",level:18},{maxPictureSize:35651584,maxBitrate:8e8,tier:"H",level:18},{maxPictureSize:35651584,maxBitrate:16e7,tier:"M",level:19},{maxPictureSize:35651584,maxBitrate:8e8,tier:"H",level:19}],Ti=".01.01.01.01.00",Si=".0.110.01.01.01.0",$r=["ap4x","ap4h","apch","apcn","apcs","apco"],Cs=["dtsc","dtsh","dtsl","dtse"],Oc=[{fourCc:"apco",bitrate:45e6,alpha:!1},{fourCc:"apcs",bitrate:102e6,alpha:!1},{fourCc:"apcn",bitrate:147e6,alpha:!1},{fourCc:"apch",bitrate:22e7,alpha:!1},{fourCc:"ap4h",bitrate:33e7,alpha:!0},{fourCc:"ap4x",bitrate:5e8,alpha:!0}],Fc=(e,t,r,n,s)=>{if(e==="avc"){const o=Math.ceil(t/16)*Math.ceil(r/16),a=vn.find(h=>o<=h.maxMacroblocks&&n<=h.maxBitrate)??ze(vn),l=a?a.level:0,f="64".padStart(2,"0"),c="00",u=l.toString(16).padStart(2,"0");return`avc1.${f}${c}${u}`}else if(e==="hevc"){const l=t*r,f=ki.find(u=>l<=u.maxPictureSize&&n<=u.maxBitrate)??ze(ki);return`hev1.1.6.${f.tier}${f.level}.B0`}else{if(e==="vp8")return"vp8";if(e==="vp9"){const o=t*r;return`vp09.00.${(Ht.find(f=>o<=f.maxPictureSize&&n<=f.maxBitrate)??ze(Ht)).level.toString().padStart(2,"0")}.08`}else if(e==="av1"){const o=t*r,a=Ei.find(c=>o<=c.maxPictureSize&&n<=c.maxBitrate)??ze(Ei);return`av01.0.${a.level.toString().padStart(2,"0")}${a.tier}.08`}else if(e==="prores"){const o=Math.pow(t*r/2073600,.95),a=Oc.filter(c=>c.alpha===s);let l=a[0].fourCc,f=1/0;for(const{fourCc:c,bitrate:u}of a){const h=Math.abs(u*o-n);h{const t=e.split("."),s=(1<<7)+1,i=Number(t[1]),o=t[2],a=Number(o.slice(0,-1)),l=(i<<5)+a,f=o.slice(-1)==="H"?1:0,c=Number(t[3]),u=c===8?0:1,h=c===12?1:0,d=t[4]?Number(t[4]):0,m=t[5]?Number(t[5][0]):1,b=t[5]?Number(t[5][1]):1,p=t[5]?Number(t[5][2]):0,w=(f<<7)+(u<<6)+(h<<5)+(d<<4)+(m<<3)+(b<<2)+p;return[s,l,w,0]},Dc=e=>{const{codec:t,codecDescription:r,colorSpace:n,avcCodecInfo:s,hevcCodecInfo:i,vp9CodecInfo:o,av1CodecInfo:a,proresFormat:l}=e;if(t==="avc"){if(C(e.avcType!==null),s){const f=new Uint8Array([s.avcProfileIndication,s.profileCompatibility,s.avcLevelIndication]);return`avc${e.avcType}.${ai(f)}`}if(!r||r.byteLength<4)throw new TypeError("AVC decoder description is not provided or is not at least 4 bytes long.");return`avc${e.avcType}.${ai(r.subarray(1,4))}`}else if(t==="hevc"){let f,c,u,h,d,m;if(i)f=i.generalProfileSpace,c=i.generalProfileIdc,u=ci(i.generalProfileCompatibilityFlags),h=i.generalTierFlag,d=i.generalLevelIdc,m=[...i.generalConstraintIndicatorFlags];else{if(!r||r.byteLength<23)throw new TypeError("HEVC decoder description is not provided or is not at least 23 bytes long.");const p=nt(r),w=p.getUint8(1);f=w>>6&3,c=w&31,u=ci(p.getUint32(2)),h=w>>5&1,d=p.getUint8(12),m=[];for(let y=0;y<6;y++)m.push(p.getUint8(6+y))}let b="hev1.";for(b+=["","A","B","C"][f]+c,b+=".",b+=u.toString(16).toUpperCase(),b+=".",b+=h===0?"L":"H",b+=d;m.length>0&&m[m.length-1]===0;)m.pop();return m.length>0&&(b+=".",b+=m.map(p=>p.toString(16).toUpperCase()).join(".")),b}else{if(t==="vp8")return"vp8";if(t==="vp9"){if(!o){const y=e.width*e.height;let g=ze(Ht).level;for(const k of Ht)if(y<=k.maxPictureSize){g=k.level;break}return`vp09.00.${g.toString().padStart(2,"0")}.08`}const f=o.profile.toString().padStart(2,"0"),c=o.level.toString().padStart(2,"0"),u=o.bitDepth.toString().padStart(2,"0"),h=o.chromaSubsampling.toString().padStart(2,"0"),d=o.colourPrimaries.toString().padStart(2,"0"),m=o.transferCharacteristics.toString().padStart(2,"0"),b=o.matrixCoefficients.toString().padStart(2,"0"),p=o.videoFullRangeFlag.toString().padStart(2,"0");let w=`vp09.${f}.${c}.${u}.${h}`;return w+=`.${d}.${m}.${b}.${p}`,w.endsWith(Ti)&&(w=w.slice(0,-Ti.length)),w}else if(t==="av1"){if(!a){const k=e.width*e.height;let T=ze(Ht).level;for(const A of Ht)if(k<=A.maxPictureSize){T=A.level;break}return`av01.0.${T.toString().padStart(2,"0")}M.08`}const f=a.profile,c=a.level.toString().padStart(2,"0"),u=a.tier?"H":"M",h=a.bitDepth.toString().padStart(2,"0"),d=a.monochrome?"1":"0",m=100*a.chromaSubsamplingX+10*a.chromaSubsamplingY+1*(a.chromaSubsamplingX&&a.chromaSubsamplingY?a.chromaSamplePosition:0);let b,p,w,y;dc(a)?(b=a.colourPrimaries,p=a.transferCharacteristics,w=a.matrixCoefficients,y=a.videoFullRangeFlag):(b=n!=null&&n.primaries?Er[n.primaries]:1,p=n!=null&&n.transfer?Tr[n.transfer]:1,w=n!=null&&n.matrix?Sr[n.matrix]:1,y=n!=null&&n.fullRange?1:0);let g=`av01.${f}.${c}${u}.${h}`;return g+=`.${d}.${m.toString().padStart(3,"0")}`,g+=`.${b.toString().padStart(2,"0")}`,g+=`.${p.toString().padStart(2,"0")}`,g+=`.${w.toString().padStart(2,"0")}`,g+=`.${y}`,g.endsWith(Si)&&(g=g.slice(0,-Si.length)),g}else{if(t==="prores")return l??"apch";t!==null&&Xt(t)}}throw new TypeError(`Unhandled codec '${t}'.`)},zc=(e,t,r)=>{if(e==="aac")return t>=2&&r<=24e3?"mp4a.40.29":r<=24e3?"mp4a.40.5":"mp4a.40.2";if(e==="mp3")return"mp3";if(e==="opus")return"opus";if(e==="vorbis")return"vorbis";if(e==="flac")return"flac";if(e==="ac3")return"ac-3";if(e==="eac3")return"ec-3";if(e==="dts")return"dtsc";if(pt.includes(e))return e;throw new TypeError(`Unhandled codec '${e}'.`)},ta=/^pcm-([usf])(\d+)(be)?$/,ir=e=>{if(C(pt.includes(e)),e==="ulaw")return{dataType:"ulaw",sampleSize:1,littleEndian:!0,silentValue:255};if(e==="alaw")return{dataType:"alaw",sampleSize:1,littleEndian:!0,silentValue:213};const t=ta.exec(e);C(t);let r;t[1]==="u"?r="unsigned":t[1]==="s"?r="signed":r="float";const n=Number(t[2])/8,s=t[3]!=="be",i=e==="pcm-u8"?2**7:0;return{dataType:r,sampleSize:n,littleEndian:s,silentValue:i}},Pn=e=>e.startsWith("avc1")||e.startsWith("avc3")?"avc":e.startsWith("hev1")||e.startsWith("hvc1")?"hevc":e==="vp8"?"vp8":e.startsWith("vp09")?"vp9":e.startsWith("av01")?"av1":$r.includes(e)?"prores":e==="mp3"||e==="mp4a.69"||e==="mp4a.6B"||e==="mp4a.6b"||e==="mp4a.40.34"?"mp3":e.startsWith("mp4a.40.")||e==="mp4a.67"?"aac":e==="opus"||e==="Opus"?"opus":e==="vorbis"?"vorbis":e==="flac"?"flac":e==="ac-3"||e==="ac3"?"ac3":e==="ec-3"||e==="eac3"?"eac3":Cs.includes(e)?"dts":e==="ulaw"?"ulaw":e==="alaw"?"alaw":ta.test(e)?e:e==="webvtt"?"webvtt":null,jc=e=>e==="avc"?{avc:{format:"avc"}}:e==="hevc"?{hevc:{format:"hevc"}}:{},Wc=e=>e==="aac"?{aac:{format:"aac"}}:e==="opus"?{opus:{format:"opus"}}:{},Lc=["avc1","avc3","hev1","hvc1","vp8","vp09","av01",...$r],qc=/^(avc1|avc3)\.[0-9a-fA-F]{6}$/,$c=/^(hev1|hvc1)\.(?:[ABC]?\d+)\.[0-9a-fA-F]{1,8}\.[LH]\d+(?:\.[0-9a-fA-F]{1,2}){0,6}$/,Uc=/^vp09(?:\.\d{2}){3}(?:(?:\.\d{2}){5})?$/,Hc=/^av01\.\d\.\d{2}[MH]\.\d{2}(?:\.\d\.\d{3}\.\d{2}\.\d{2}\.\d{2}\.\d)?$/,ra=(e,t)=>{if(!e)throw new TypeError("Video chunk metadata must be provided.");if(typeof e!="object")throw new TypeError("Video chunk metadata must be an object.");if(!e.decoderConfig)throw new TypeError("Video chunk metadata must include a decoder configuration.");if(typeof e.decoderConfig!="object")throw new TypeError("Video chunk metadata decoder configuration must be an object.");if(typeof e.decoderConfig.codec!="string")throw new TypeError("Video chunk metadata decoder configuration must specify a codec string.");if(!Lc.some(r=>e.decoderConfig.codec.startsWith(r)))throw new TypeError("Video chunk metadata decoder configuration codec string must be a valid video codec string as specified in the Mediabunny Codec Registry.");if(!Number.isInteger(e.decoderConfig.codedWidth)||e.decoderConfig.codedWidth<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).");if(!Number.isInteger(e.decoderConfig.codedHeight)||e.decoderConfig.codedHeight<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).");if(e.decoderConfig.displayAspectWidth!==void 0&&(!Number.isInteger(e.decoderConfig.displayAspectWidth)||e.decoderConfig.displayAspectWidth<=0))throw new TypeError("Video chunk metadata decoder configuration displayAspectWidth, when defined, must be a positive integer.");if(e.decoderConfig.displayAspectHeight!==void 0&&(!Number.isInteger(e.decoderConfig.displayAspectHeight)||e.decoderConfig.displayAspectHeight<=0))throw new TypeError("Video chunk metadata decoder configuration displayAspectHeight, when defined, must be a positive integer.");if(e.decoderConfig.displayAspectWidth!==void 0!=(e.decoderConfig.displayAspectHeight!==void 0))throw new TypeError("Video chunk metadata decoder configuration must specify both displayAspectWidth and displayAspectHeight, or neither.");if(e.decoderConfig.description!==void 0&&!Sn(e.decoderConfig.description))throw new TypeError("Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(e.decoderConfig.colorSpace!==void 0){const{colorSpace:r}=e.decoderConfig;if(typeof r!="object")throw new TypeError("Video chunk metadata decoder configuration colorSpace, when provided, must be an object.");const n=Object.keys(Er);if(r.primaries!=null&&!n.includes(r.primaries))throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${n.join(", ")}.`);const s=Object.keys(Tr);if(r.transfer!=null&&!s.includes(r.transfer))throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of ${s.join(", ")}.`);const i=Object.keys(Sr);if(r.matrix!=null&&!i.includes(r.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${i.join(", ")}.`);if(r.fullRange!=null&&typeof r.fullRange!="boolean")throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.")}if(e.decoderConfig.codec.startsWith("avc1")||e.decoderConfig.codec.startsWith("avc3")){if(!qc.test(e.decoderConfig.codec))throw new TypeError("Video chunk metadata decoder configuration codec string for AVC must be a valid AVC codec string as specified in Section 3.4 of RFC 6381.")}else if(e.decoderConfig.codec.startsWith("hev1")||e.decoderConfig.codec.startsWith("hvc1")){if(!$c.test(e.decoderConfig.codec))throw new TypeError("Video chunk metadata decoder configuration codec string for HEVC must be a valid HEVC codec string as specified in Section E.3 of ISO 14496-15.")}else if(e.decoderConfig.codec.startsWith("vp8")){if(e.decoderConfig.codec!=="vp8")throw new TypeError('Video chunk metadata decoder configuration codec string for VP8 must be "vp8".')}else if(e.decoderConfig.codec.startsWith("vp09")){if(!Uc.test(e.decoderConfig.codec))throw new TypeError('Video chunk metadata decoder configuration codec string for VP9 must be a valid VP9 codec string as specified in Section "Codecs Parameter String" of https://www.webmproject.org/vp9/mp4/.')}else if(e.decoderConfig.codec.startsWith("av01")){if(!Hc.test(e.decoderConfig.codec))throw new TypeError('Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as specified in Section "Codecs Parameter String" of https://aomediacodec.github.io/av1-isobmff/.')}else if($r.some(r=>e.decoderConfig.codec.startsWith(r))&&!$r.some(r=>e.decoderConfig.codec===r))throw new TypeError(`Video chunk metadata decoder configuration codec string for ProRes must be one of the valid ProRes four-character codes: ${$r.join(", ")}.`);if(t!==null&&Pn(e.decoderConfig.codec)!==t)throw new TypeError(`Video chunk metadata decoder configuration codec string '${e.decoderConfig.codec}' does not fit to the track codec '${t}'.`)},Gc=["mp4a","mp3","opus","vorbis","flac","ulaw","alaw","pcm","ac-3","ec-3","dts"],na=(e,t)=>{if(!e)throw new TypeError("Audio chunk metadata must be provided.");if(typeof e!="object")throw new TypeError("Audio chunk metadata must be an object.");if(!e.decoderConfig)throw new TypeError("Audio chunk metadata must include a decoder configuration.");if(typeof e.decoderConfig!="object")throw new TypeError("Audio chunk metadata decoder configuration must be an object.");if(typeof e.decoderConfig.codec!="string")throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string.");if(!Gc.some(r=>e.decoderConfig.codec.startsWith(r)))throw new TypeError("Audio chunk metadata decoder configuration codec string must be a valid audio codec string as specified in the Mediabunny Codec Registry.");if(!Number.isInteger(e.decoderConfig.sampleRate)||e.decoderConfig.sampleRate<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).");if(!Number.isInteger(e.decoderConfig.numberOfChannels)||e.decoderConfig.numberOfChannels<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).");if(e.decoderConfig.description!==void 0&&!Sn(e.decoderConfig.description))throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(e.decoderConfig.codec.startsWith("mp4a")&&e.decoderConfig.codec!=="mp4a.69"&&e.decoderConfig.codec!=="mp4a.6B"&&e.decoderConfig.codec!=="mp4a.6b"){if(!["mp4a.40.2","mp4a.40.02","mp4a.40.5","mp4a.40.05","mp4a.40.29","mp4a.67"].includes(e.decoderConfig.codec))throw new TypeError("Audio chunk metadata decoder configuration codec string for AAC must be a valid AAC codec string as specified in https://www.w3.org/TR/webcodecs-aac-codec-registration/.")}else if(e.decoderConfig.codec.startsWith("mp3")||e.decoderConfig.codec.startsWith("mp4a")){if(e.decoderConfig.codec!=="mp3"&&e.decoderConfig.codec!=="mp4a.69"&&e.decoderConfig.codec!=="mp4a.6B"&&e.decoderConfig.codec!=="mp4a.6b")throw new TypeError('Audio chunk metadata decoder configuration codec string for MP3 must be "mp3", "mp4a.69" or "mp4a.6B".')}else if(e.decoderConfig.codec.startsWith("opus")){if(e.decoderConfig.codec!=="opus")throw new TypeError('Audio chunk metadata decoder configuration codec string for Opus must be "opus".');if(e.decoderConfig.description&&e.decoderConfig.description.byteLength<18)throw new TypeError("Audio chunk metadata decoder configuration description, when specified, is expected to be an Identification Header as specified in Section 5.1 of RFC 7845.")}else if(e.decoderConfig.codec.startsWith("vorbis")){if(e.decoderConfig.codec!=="vorbis")throw new TypeError('Audio chunk metadata decoder configuration codec string for Vorbis must be "vorbis".');if(!e.decoderConfig.description)throw new TypeError("Audio chunk metadata decoder configuration for Vorbis must include a description, which is expected to adhere to the format described in https://www.w3.org/TR/webcodecs-vorbis-codec-registration/.")}else if(e.decoderConfig.codec.startsWith("flac")){if(e.decoderConfig.codec!=="flac")throw new TypeError('Audio chunk metadata decoder configuration codec string for FLAC must be "flac".');if(!e.decoderConfig.description||e.decoderConfig.description.byteLength<42)throw new TypeError("Audio chunk metadata decoder configuration for FLAC must include a description, which is expected to adhere to the format described in https://www.w3.org/TR/webcodecs-flac-codec-registration/.")}else if(e.decoderConfig.codec.startsWith("ac-3")||e.decoderConfig.codec.startsWith("ac3")){if(e.decoderConfig.codec!=="ac-3")throw new TypeError('Audio chunk metadata decoder configuration codec string for AC-3 must be "ac-3".')}else if(e.decoderConfig.codec.startsWith("ec-3")||e.decoderConfig.codec.startsWith("eac3")){if(e.decoderConfig.codec!=="ec-3")throw new TypeError('Audio chunk metadata decoder configuration codec string for EC-3 must be "ec-3".')}else if(e.decoderConfig.codec.startsWith("dts")){if(!Cs.includes(e.decoderConfig.codec))throw new TypeError(`Audio chunk metadata decoder configuration codec string for DTS must be one of the following four-character codes: ${Cs.join(", ")}.`)}else if((e.decoderConfig.codec.startsWith("pcm")||e.decoderConfig.codec.startsWith("ulaw")||e.decoderConfig.codec.startsWith("alaw"))&&!pt.includes(e.decoderConfig.codec))throw new TypeError(`Audio chunk metadata decoder configuration codec string for PCM must be one of the supported PCM codecs (${pt.join(", ")}).`);if(t!==null&&Pn(e.decoderConfig.codec)!==t)throw new TypeError(`Audio chunk metadata decoder configuration codec string '${e.decoderConfig.codec}' does not fit to the track codec '${t}'.`)},Xc=e=>{if(!e)throw new TypeError("Subtitle metadata must be provided.");if(typeof e!="object")throw new TypeError("Subtitle metadata must be an object.");if(!e.config)throw new TypeError("Subtitle metadata must include a config object.");if(typeof e.config!="object")throw new TypeError("Subtitle metadata config must be an object.");if(typeof e.config.description!="string")throw new TypeError("Subtitle metadata config description must be a string.")};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Ci=new Uint8Array(0);class St{constructor(t,r,n,s,i=-1,o,a){if(this.data=t,this.type=r,this.timestamp=n,this.duration=s,this.sequenceNumber=i,t===Ci&&o===void 0)throw new Error("Internal error: byteLength must be explicitly provided when constructing metadata-only packets.");if(o===void 0&&(o=t.byteLength),!(t instanceof Uint8Array))throw new TypeError("data must be a Uint8Array.");if(r!=="key"&&r!=="delta")throw new TypeError('type must be either "key" or "delta".');if(!Number.isFinite(n))throw new TypeError("timestamp must be a number.");if(!Number.isFinite(s)||s<0)throw new TypeError("duration must be a non-negative number.");if(!Number.isFinite(i))throw new TypeError("sequenceNumber must be a number.");if(!Number.isInteger(o)||o<0)throw new TypeError("byteLength must be a non-negative integer.");if(a!==void 0&&(typeof a!="object"||!a))throw new TypeError("sideData, when provided, must be an object.");if((a==null?void 0:a.alpha)!==void 0&&!(a.alpha instanceof Uint8Array))throw new TypeError("sideData.alpha, when provided, must be a Uint8Array.");if((a==null?void 0:a.alphaByteLength)!==void 0&&(!Number.isInteger(a.alphaByteLength)||a.alphaByteLength<0))throw new TypeError("sideData.alphaByteLength, when provided, must be a non-negative integer.");this.byteLength=o,this.sideData=a??{},this.sideData.alpha&&this.sideData.alphaByteLength===void 0&&(this.sideData.alphaByteLength=this.sideData.alpha.byteLength)}get isMetadataOnly(){return this.data===Ci}get microsecondTimestamp(){return Math.trunc(Ut*this.timestamp)}get microsecondDuration(){return Math.trunc(Ut*this.duration)}toEncodedVideoChunk(){if(this.isMetadataOnly)throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");if(typeof EncodedVideoChunk>"u")throw new Error("EncodedVideoChunk is not available in this environment.");return new EncodedVideoChunk({data:this.data,type:this.type,timestamp:this.microsecondTimestamp,duration:this.microsecondDuration})}alphaToEncodedVideoChunk(t=this.type){if(!this.sideData.alpha)throw new TypeError("This packet does not contain alpha side data.");if(this.isMetadataOnly)throw new TypeError("Metadata-only packets cannot be converted to a video chunk.");if(typeof EncodedVideoChunk>"u")throw new Error("EncodedVideoChunk is not available in this environment.");return new EncodedVideoChunk({data:this.sideData.alpha,type:t,timestamp:this.microsecondTimestamp,duration:this.microsecondDuration})}toEncodedAudioChunk(){if(this.isMetadataOnly)throw new TypeError("Metadata-only packets cannot be converted to an audio chunk.");if(typeof EncodedAudioChunk>"u")throw new Error("EncodedAudioChunk is not available in this environment.");return new EncodedAudioChunk({data:this.data,type:this.type,timestamp:this.microsecondTimestamp,duration:this.microsecondDuration})}static fromEncodedChunk(t,r){if(!(t instanceof EncodedVideoChunk||t instanceof EncodedAudioChunk))throw new TypeError("chunk must be an EncodedVideoChunk or EncodedAudioChunk.");const n=new Uint8Array(t.byteLength);return t.copyTo(n),new St(n,t.type,t.timestamp/1e6,(t.duration??0)/1e6,void 0,void 0,r)}clone(t){if(t!==void 0&&(typeof t!="object"||t===null))throw new TypeError("options, when provided, must be an object.");if((t==null?void 0:t.data)!==void 0&&!(t.data instanceof Uint8Array))throw new TypeError("options.data, when provided, must be a Uint8Array.");if((t==null?void 0:t.type)!==void 0&&t.type!=="key"&&t.type!=="delta")throw new TypeError('options.type, when provided, must be either "key" or "delta".');if((t==null?void 0:t.timestamp)!==void 0&&!Number.isFinite(t.timestamp))throw new TypeError("options.timestamp, when provided, must be a number.");if((t==null?void 0:t.duration)!==void 0&&!Number.isFinite(t.duration))throw new TypeError("options.duration, when provided, must be a number.");if((t==null?void 0:t.sequenceNumber)!==void 0&&!Number.isFinite(t.sequenceNumber))throw new TypeError("options.sequenceNumber, when provided, must be a number.");if((t==null?void 0:t.sideData)!==void 0&&(typeof t.sideData!="object"||t.sideData===null))throw new TypeError("options.sideData, when provided, must be an object.");return new St((t==null?void 0:t.data)??this.data,(t==null?void 0:t.type)??this.type,(t==null?void 0:t.timestamp)??this.timestamp,(t==null?void 0:t.duration)??this.duration,(t==null?void 0:t.sequenceNumber)??this.sequenceNumber,this.byteLength,(t==null?void 0:t.sideData)??this.sideData)}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Qc=e=>{let r=(e.hasVideo?"video/":e.hasAudio?"audio/":"application/")+(e.isQuickTime?"quicktime":"mp4");if(e.codecStrings.length>0){const n=[...new Set(e.codecStrings)];r+=`; codecs="${n.join(", ")}"`}return r};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const $n=8,Pi=16,Un=e=>{let t=0;for(let r=0;r<4;r++){t<<=7;const n=er(e);if(t|=n&127,(n&128)===0)break}return t},Kc=e=>{if(er(e)!==3)throw new Error("Invalid esds: expected ES_Descriptor.");Un(e),e.skip(2);const r=er(e),n=(r&128)!==0,s=(r&64)!==0,i=(r&32)!==0;if(n&&e.skip(2),s){const u=er(e);e.skip(u)}if(i&&e.skip(2),er(e)!==4)throw new Error("Invalid esds: expected DecoderConfigDescriptor.");const a=Un(e),l=e.filePos,f=er(e);e.skip(12);let c=null;if(a>e.filePos-l){if(er(e)!==5)throw new Error("Invalid esds: expected DecoderSpecificInfo.");const h=Un(e);c=ma(e,h)}return{objectTypeIndication:f,decoderSpecificInfo:c}};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Yc=7,Zc=9,Mi=e=>{const t=e.filePos,r=ma(e,9),n=new Te(r);if(n.readBits(12)!==4095||(n.skipBits(1),n.readBits(2)!==0))return null;const o=n.readBits(1),a=n.readBits(2)+1,l=n.readBits(4);if(l===15)return null;n.skipBits(1);const f=n.readBits(3);if(f===0)throw new Error("ADTS frames with channel configuration 0 are not supported.");n.skipBits(1),n.skipBits(1),n.skipBits(1),n.skipBits(1);const c=n.readBits(13);n.skipBits(11);const u=n.readBits(2)+1;if(u!==1)throw new Error("ADTS frames with more than one AAC frame are not supported.");let h=null;return o===1?e.filePos-=2:h=n.readBits(16),{objectType:a,samplingFrequencyIndex:l,channelConfiguration:f,frameLength:c,numberOfAacFrames:u,crcCheck:h,startPos:t}};new Proxy({},{get(e,t){throw new Error(`Module "" has been externalized for browser compatibility. Cannot access ".${t}" in client code. See https://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.`)}});/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */var Jc=function(e,t,r){if(t!=null){if(typeof t!="object"&&typeof t!="function")throw new TypeError("Object expected.");var n,s;if(r){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");n=t[Symbol.asyncDispose]}if(n===void 0){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");n=t[Symbol.dispose],r&&(s=n)}if(typeof n!="function")throw new TypeError("Object not disposable.");s&&(n=function(){try{s.call(this)}catch(i){return Promise.reject(i)}}),e.stack.push({value:t,dispose:n,async:r})}else r&&e.stack.push({async:!0});return t},el=(function(e){return function(t){function r(o){t.error=t.hasError?new e(o,t.error,"An error was suppressed during disposal."):o,t.hasError=!0}var n,s=0;function i(){for(;n=t.stack.pop();)try{if(!n.async&&s===1)return s=0,t.stack.push(n),Promise.resolve().then(i);if(n.dispose){var o=n.dispose.call(n.value);if(n.async)return s|=2,Promise.resolve(o).then(i,function(a){return r(a),i()})}else s|=1}catch(a){r(a)}if(s===1)return t.hasError?Promise.reject(t.error):Promise.resolve();if(t.hasError)throw t.error}return i()}})(typeof SuppressedError=="function"?SuppressedError:function(e,t,r){var n=new Error(r);return n.name="SuppressedError",n.error=e,n.suppressed=t,n});D0();let Ni=-1/0,Bi=-1/0,Ue=null;typeof FinalizationRegistry<"u"&&(Ue=new FinalizationRegistry(e=>{const t=performance.now();e.type==="video"?(t-Ni>=1e3&&(ye._error("A VideoSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your VideoSamples as soon as you're done using them."),Ni=t),typeof VideoFrame<"u"&&e.data instanceof VideoFrame&&e.data.close()):(t-Bi>=1e3&&(ye._error("An AudioSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your AudioSamples as soon as you're done using them."),Bi=t),typeof AudioData<"u"&&e.data instanceof AudioData&&e.data.close())}));class Qt{constructor(){this._referenceCount=0,this._lastAllocationBuffer=null}}const Ps=["I420","I420P10","I420P12","I420A","I420AP10","I420AP12","I422","I422P10","I422P12","I422A","I422AP10","I422AP12","I444","I444P10","I444P12","I444A","I444AP10","I444AP12","NV12","RGBA","RGBX","BGRA","BGRX"],tl=new Set(Ps);class $e{get codedWidth(){return this.visibleRect.width}get codedHeight(){return this.visibleRect.height}get displayWidth(){return this.rotation%180===0?this.squarePixelWidth:this.squarePixelHeight}get displayHeight(){return this.rotation%180===0?this.squarePixelHeight:this.squarePixelWidth}get microsecondTimestamp(){return Math.trunc(Ut*this.timestamp)}get microsecondDuration(){return Math.trunc(Ut*this.duration)}get hasAlpha(){return this.format&&this.format.includes("A")}constructor(t,r){var n,s,i,o,a,l,f,c;if(this._closed=!1,t instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&t instanceof SharedArrayBuffer||ArrayBuffer.isView(t)){if(!r||typeof r!="object")throw new TypeError("init must be an object.");if(r.format===void 0||!tl.has(r.format))throw new TypeError("init.format must be one of: "+Ps.join(", "));if(!Number.isInteger(r.codedWidth)||r.codedWidth<=0)throw new TypeError("init.codedWidth must be a positive integer.");if(!Number.isInteger(r.codedHeight)||r.codedHeight<=0)throw new TypeError("init.codedHeight must be a positive integer.");if(r.rotation!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");if(r.flip!==void 0&&typeof r.flip!="boolean")throw new TypeError("init.flip, when provided, must be a boolean.");if(!Number.isFinite(r.timestamp))throw new TypeError("init.timestamp must be a number.");if(r.duration!==void 0&&(!Number.isFinite(r.duration)||r.duration<0))throw new TypeError("init.duration, when provided, must be a non-negative number.");if(r.layout!==void 0){if(!Array.isArray(r.layout))throw new TypeError("init.layout, when provided, must be an array.");for(const d of r.layout){if(!d||typeof d!="object"||Array.isArray(d))throw new TypeError("Each entry in init.layout must be an object.");if(!Number.isInteger(d.offset)||d.offset<0)throw new TypeError("plane.offset must be a non-negative integer.");if(!Number.isInteger(d.stride)||d.stride<0)throw new TypeError("plane.stride must be a non-negative integer.")}}if(r.visibleRect!==void 0&&jn(r.visibleRect,"init.visibleRect"),r.displayWidth!==void 0&&(!Number.isInteger(r.displayWidth)||r.displayWidth<=0))throw new TypeError("init.displayWidth, when provided, must be a positive integer.");if(r.displayHeight!==void 0&&(!Number.isInteger(r.displayHeight)||r.displayHeight<=0))throw new TypeError("init.displayHeight, when provided, must be a positive integer.");if(r.displayWidth!==void 0!=(r.displayHeight!==void 0))throw new TypeError("init.displayWidth and init.displayHeight must be either both provided or both omitted.");this.format=r.format,this.rotation=r.rotation??0,this.flip=r.flip??!1,this.timestamp=r.timestamp,this.duration=r.duration??0;const u=r.layout??sl(r.format,r.codedWidth,r.codedHeight);let h=r.colorSpace??null;h===null&&(this.format==="RGBA"||this.format==="RGBX"||this.format==="BGRA"||this.format==="BGRX"?h={primaries:"bt709",transfer:"iec61966-2-1",matrix:"rgb",fullRange:!0}:h={primaries:"bt709",transfer:"bt709",matrix:"bt709",fullRange:!1}),this.visibleRect={left:((n=r.visibleRect)==null?void 0:n.left)??0,top:((s=r.visibleRect)==null?void 0:s.top)??0,width:((i=r.visibleRect)==null?void 0:i.width)??r.codedWidth,height:((o=r.visibleRect)==null?void 0:o.height)??r.codedHeight},r.displayWidth!==void 0?(this.squarePixelWidth=this.rotation%180===0?r.displayWidth:r.displayHeight,this.squarePixelHeight=this.rotation%180===0?r.displayHeight:r.displayWidth):(this.squarePixelWidth=this.visibleRect.width,this.squarePixelHeight=this.visibleRect.height),this._data=r._doNotCopy?Ie(t):Ie(t).slice(),this._layout=u,this.colorSpace=new Hn(h)}else if(typeof VideoFrame<"u"&&t instanceof VideoFrame){if((r==null?void 0:r.rotation)!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");if((r==null?void 0:r.flip)!==void 0&&typeof r.flip!="boolean")throw new TypeError("init.flip, when provided, must be a boolean.");if((r==null?void 0:r.timestamp)!==void 0&&!Number.isFinite(r==null?void 0:r.timestamp))throw new TypeError("init.timestamp, when provided, must be a number.");if((r==null?void 0:r.duration)!==void 0&&(!Number.isFinite(r.duration)||r.duration<0))throw new TypeError("init.duration, when provided, must be a non-negative number.");(r==null?void 0:r.visibleRect)!==void 0&&jn(r.visibleRect,"init.visibleRect"),this._data=t,this._layout=null,this.format=t.format,this.visibleRect={left:((a=t.visibleRect)==null?void 0:a.x)??0,top:((l=t.visibleRect)==null?void 0:l.y)??0,width:((f=t.visibleRect)==null?void 0:f.width)??t.codedWidth,height:((c=t.visibleRect)==null?void 0:c.height)??t.codedHeight},this.rotation=(r==null?void 0:r.rotation)??0,this.flip=(r==null?void 0:r.flip)??!1,this.squarePixelWidth=t.displayWidth,this.squarePixelHeight=t.displayHeight,this.timestamp=(r==null?void 0:r.timestamp)??t.timestamp/1e6,this.duration=(r==null?void 0:r.duration)??(t.duration??0)/1e6;const{primaries:u,transfer:h,matrix:d,fullRange:m}=t.colorSpace;this.colorSpace=new Hn({primaries:u!==null&&u in Er?u:null,transfer:h!==null&&h in Tr?h:null,matrix:d!==null&&d in Sr?d:null,fullRange:m})}else if(typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof SVGImageElement<"u"&&t instanceof SVGImageElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap||typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas){if(!r||typeof r!="object")throw new TypeError("init must be an object.");if(r.rotation!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");if(r.flip!==void 0&&typeof r.flip!="boolean")throw new TypeError("init.flip, when provided, must be a boolean.");if(!Number.isFinite(r.timestamp))throw new TypeError("init.timestamp must be a number.");if(r.duration!==void 0&&(!Number.isFinite(r.duration)||r.duration<0))throw new TypeError("init.duration, when provided, must be a non-negative number.");if(r.visibleRect!==void 0&&jn(r.visibleRect,"init.visibleRect"),typeof VideoFrame<"u")return new $e(new VideoFrame(t,{timestamp:Math.trunc(r.timestamp*Ut),duration:Math.trunc((r.duration??0)*Ut)||void 0,visibleRect:r.visibleRect&&{x:r.visibleRect.left,y:r.visibleRect.top,width:r.visibleRect.width,height:r.visibleRect.height}}),r);let u=0,h=0;if("naturalWidth"in t?(u=t.naturalWidth,h=t.naturalHeight):"videoWidth"in t?(u=t.videoWidth,h=t.videoHeight):"width"in t&&(u=Number(t.width),h=Number(t.height)),!u||!h)throw new TypeError("Could not determine dimensions.");const d=r.visibleRect??{left:0,top:0,width:u,height:h},m=new OffscreenCanvas(d.width,d.height),b=m.getContext("2d",{alpha:gn(),willReadFrequently:!0});if(!b)throw new Error("OffscreenCanvas must have support for the '2d' context in order to create a VideoSample from this data.");b.drawImage(t,-d.left,-d.top),this._data=m,this._layout=null,this.format="RGBX",this.visibleRect={left:0,top:0,width:d.width,height:d.height},this.squarePixelWidth=d.width,this.squarePixelHeight=d.height,this.rotation=r.rotation??0,this.flip=r.flip??!1,this.timestamp=r.timestamp,this.duration=r.duration??0,this.colorSpace=new Hn({matrix:"rgb",primaries:"bt709",transfer:"iec61966-2-1",fullRange:!0})}else if(t instanceof Qt){if(!r||typeof r!="object")throw new TypeError("init must be an object.");if(r.rotation!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");if(r.flip!==void 0&&typeof r.flip!="boolean")throw new TypeError("init.flip, when provided, must be a boolean.");if(!Number.isFinite(r.timestamp))throw new TypeError("init.timestamp must be a number.");if(r.duration!==void 0&&(!Number.isFinite(r.duration)||r.duration<0))throw new TypeError("init.duration, when provided, must be a non-negative number.");if(this._data=t,t._referenceCount++,this.format=t.getFormat(),this.format!==null&&!Ps.includes(this.format))throw new TypeError("getFormat() must return a VideoSamplePixelFormat or null.");if(this.visibleRect={left:0,top:0,width:t.getCodedWidth(),height:t.getCodedHeight()},!Number.isInteger(this.visibleRect.width)||this.visibleRect.width<=0)throw new TypeError("getCodedWidth() must return a positive integer.");if(!Number.isInteger(this.visibleRect.height)||this.visibleRect.height<=0)throw new TypeError("getCodedHeight() must return a positive integer.");if(this.squarePixelWidth=t.getSquarePixelWidth(),!Number.isInteger(this.squarePixelWidth)||this.squarePixelWidth<=0)throw new TypeError("getSquarePixelWidth() must return a positive integer.");if(this.squarePixelHeight=t.getSquarePixelHeight(),!Number.isInteger(this.squarePixelHeight)||this.squarePixelHeight<=0)throw new TypeError("getSquarePixelHeight() must return a positive integer.");this.rotation=r.rotation??0,this.flip=r.flip??!1,this.timestamp=r.timestamp,this.duration=r.duration??0,this.colorSpace=t.getColorSpace()}else throw new TypeError("Invalid data type: Must be a BufferSource, CanvasImageSource, or VideoSampleResource.");this.encodeOptions=(r==null?void 0:r.encodeOptions)??{},this.pixelAspectRatio=Uo({num:this.squarePixelWidth*this.codedHeight,den:this.squarePixelHeight*this.codedWidth}),Ue==null||Ue.register(this,{type:"video",data:this._data},this)}clone(){if(this._closed)throw new Error("VideoSample is closed.");return C(this._data!==null),this._data instanceof Qt?new $e(this._data,{timestamp:this.timestamp,duration:this.duration,rotation:this.rotation,flip:this.flip,encodeOptions:this.encodeOptions}):Nr(this._data)?new $e(this._data.clone(),{timestamp:this.timestamp,duration:this.duration,rotation:this.rotation,flip:this.flip,encodeOptions:this.encodeOptions}):this._data instanceof Uint8Array?(C(this._layout),new $e(this._data,{format:this.format,layout:this._layout,codedWidth:this.codedWidth,codedHeight:this.codedHeight,timestamp:this.timestamp,duration:this.duration,colorSpace:this.colorSpace,rotation:this.rotation,flip:this.flip,visibleRect:this.visibleRect,displayWidth:this.displayWidth,displayHeight:this.displayHeight,encodeOptions:this.encodeOptions,_doNotCopy:!0})):new $e(this._data,{format:this.format,codedWidth:this.codedWidth,codedHeight:this.codedHeight,timestamp:this.timestamp,duration:this.duration,colorSpace:this.colorSpace,rotation:this.rotation,flip:this.flip,visibleRect:this.visibleRect,displayWidth:this.displayWidth,displayHeight:this.displayHeight,encodeOptions:this.encodeOptions})}close(){this._closed||(Ue==null||Ue.unregister(this),this._data instanceof Qt?(this._data._referenceCount--,this._data._referenceCount===0&&this._data.close()):Nr(this._data)?this._data.close():this._data=null,this._closed=!0)}allocationSize(t={}){if(Ri(t),this._closed)throw new Error("VideoSample is closed.");if((t.format??this.format)==null)throw new Error("Cannot get allocation size when format is null.");return Nr(this._data)?this._data.allocationSize(t):Oi(this,t).allocationSize}async copyTo(t,r={}){if(!Sn(t))throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");if(Ri(r),this._closed)throw new Error("VideoSample is closed.");if((r.format??this.format)==null)throw new Error("Cannot copy video sample data when format is null.");if(C(this._data!==null),Nr(this._data))return this._data.copyTo(t,r);if(r.format&&!["RGBA","RGBX","BGRA","BGRX"].includes(this.format)&&["RGBA","RGBX","BGRA","BGRX"].includes(r.format))if(this._data instanceof Qt){const f={stack:[],error:void 0,hasError:!1};try{const c=Jc(f,await this._data.toRgbSample({timestamp:this.timestamp,duration:this.duration,rotation:this.rotation,flip:this.flip},r.colorSpace??"srgb"),!1);if(!(c instanceof $e))throw new TypeError("toRgbSample() must return a VideoSample.");if(!["RGBA","RGBX","BGRA","BGRX"].includes(c.format))throw new Error(`Sample returned by toRgbSample was expected to have an RGB format, got '${c.format}' instead.`);return await c.copyTo(t,r)}catch(c){f.error=c,f.hasError=!0}finally{el(f)}}else{if(typeof VideoFrame>"u")throw new Error("For this sample, converting from a non-RGB to an RGB format requires VideoFrame to be defined.");const f=this.toVideoFrame(),c=await f.copyTo(t,r);return f.close(),c}const n=Oi(this,r);C(this.format);const s=Ie(t);if(s.byteLength!(c.data instanceof Uint8Array)||!Number.isInteger(c.stride)||c.stride<0))throw new TypeError('getDataPlanes() must return an array of objects with a Uint8Array "data" property and a non-negative integer "stride" property.');o=f}else if(this._data instanceof Uint8Array)C(this._layout),C(this._layout.length===i.length),o=this._layout.map((f,c)=>{const u=Math.ceil(this.codedHeight/i[c].heightDivisor);return{data:this._data.subarray(f.offset,f.offset+f.stride*u),stride:f.stride}});else{const c=this._data.getContext("2d");C(c);const u=c.getImageData(0,0,this.codedWidth,this.codedHeight);o=[{data:Ie(u.data),stride:4*this.codedWidth}]}const a=[],l=i.length;for(let f=0;fh.byteLength)throw new Error("Source buffer OOB read.");if(m+b>s.byteLength)throw new Error("Destination buffer OOB write.");const y=h.subarray(d,d+b);s.set(y,m),d+=u,m+=c.destinationStride}a.push(p)}if(r.format!==void 0){const f=this.format.startsWith("RGB")!==r.format.startsWith("RGB"),c=this.format.includes("X")&&r.format.includes("A");if(f||c)for(let u=0;uo+a.data.byteLength,0),n=new Uint8Array(r);let s=0;const i=[];for(const o of t)n.set(o.data,s),i.push(s),s+=o.data.byteLength;return new VideoFrame(n,{format:this.format,layout:t.map((o,a)=>({offset:i[a],stride:o.stride})),codedWidth:this.codedWidth,codedHeight:this.codedHeight,timestamp:this.microsecondTimestamp,duration:this.microsecondDuration,colorSpace:this.colorSpace,visibleRect:this.visibleRect,displayWidth:this.squarePixelWidth,displayHeight:this.squarePixelHeight})}else return Nr(this._data)?new VideoFrame(this._data,{timestamp:this.microsecondTimestamp,duration:this.microsecondDuration||void 0}):this._data instanceof Uint8Array?(C(this._layout),new VideoFrame(this._data,{format:this.format,codedWidth:this.codedWidth,codedHeight:this.codedHeight,layout:this._layout,timestamp:this.microsecondTimestamp,duration:this.microsecondDuration||void 0,colorSpace:this.colorSpace,visibleRect:this.visibleRect,displayWidth:this.squarePixelWidth,displayHeight:this.squarePixelHeight})):new VideoFrame(this._data,{timestamp:this.microsecondTimestamp,duration:this.microsecondDuration||void 0})}draw(t,r,n,s,i,o,a,l,f){let c=0,u=0,h=this.displayWidth,d=this.displayHeight,m=0,b=0,p=this.displayWidth,w=this.displayHeight;if(o!==void 0?(c=r,u=n,h=s,d=i,m=o,b=a,l!==void 0?(p=l,w=f):(p=h,w=d)):(m=r,b=n,s!==void 0&&(p=s,w=i)),!(typeof CanvasRenderingContext2D<"u"&&t instanceof CanvasRenderingContext2D||typeof OffscreenCanvasRenderingContext2D<"u"&&t instanceof OffscreenCanvasRenderingContext2D))throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.");if(!Number.isFinite(c))throw new TypeError("sx must be a number.");if(!Number.isFinite(u))throw new TypeError("sy must be a number.");if(!Number.isFinite(h)||h<0)throw new TypeError("sWidth must be a non-negative number.");if(!Number.isFinite(d)||d<0)throw new TypeError("sHeight must be a non-negative number.");if(!Number.isFinite(m))throw new TypeError("dx must be a number.");if(!Number.isFinite(b))throw new TypeError("dy must be a number.");if(!Number.isFinite(p)||p<0)throw new TypeError("dWidth must be a non-negative number.");if(!Number.isFinite(w)||w<0)throw new TypeError("dHeight must be a non-negative number.");if(this._closed)throw new Error("VideoSample is closed.");({sx:c,sy:u,sWidth:h,sHeight:d}=this._unmapSourceRegion(c,u,h,d,this.rotation,this.flip));const y=this.toCanvasImageSource();t.save();const g=m+p/2,k=b+w/2;t.translate(g,k),this.flip&&t.scale(-1,1),t.rotate(this.rotation*Math.PI/180);const T=this.rotation%180===0?1:p/w;t.scale(1/T,T),t.drawImage(y,c,u,h,d,-p/2,-w/2,p,w),t.restore()}drawWithFit(t,r){var g,k,T,A;if(!(typeof CanvasRenderingContext2D<"u"&&t instanceof CanvasRenderingContext2D||typeof OffscreenCanvasRenderingContext2D<"u"&&t instanceof OffscreenCanvasRenderingContext2D))throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.");if(!r||typeof r!="object")throw new TypeError("options must be an object.");if(!["fill","contain","cover"].includes(r.fit))throw new TypeError("options.fit must be 'fill', 'contain', or 'cover'.");if(r.rotation!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError("options.rotation, when provided, must be 0, 90, 180, or 270.");if(r.flip!==void 0&&typeof r.flip!="boolean")throw new TypeError("options.flip, when provided, must be a boolean.");r.crop!==void 0&&Ms(r.crop,"options.");const n=t.canvas.width,s=t.canvas.height,i=r.rotation??this.rotation,o=r.flip??this.flip,[a,l]=i%180===0?[this.squarePixelWidth,this.squarePixelHeight]:[this.squarePixelHeight,this.squarePixelWidth];let f=r.crop;f&&(f=Ii(f,a,l));let c,u,h,d;const{sx:m,sy:b,sWidth:p,sHeight:w}=this._unmapSourceRegion(((g=r.crop)==null?void 0:g.left)??0,((k=r.crop)==null?void 0:k.top)??0,((T=r.crop)==null?void 0:T.width)??a,((A=r.crop)==null?void 0:A.height)??l,i,o);if(r.fit==="fill")c=0,u=0,h=n,d=s;else{const[N,S]=r.crop?[r.crop.width,r.crop.height]:[a,l],B=r.fit==="contain"?Math.min(n/N,s/S):Math.max(n/N,s/S);h=N*B,d=S*B,c=(n-h)/2,u=(s-d)/2}t.save();const y=i%180===0?1:h/d;t.translate(n/2,s/2),o&&t.scale(-1,1),t.rotate(i*Math.PI/180),t.scale(1/y,y),t.translate(-n/2,-s/2),t.drawImage(this.toCanvasImageSource(),m,b,p,w,c,u,h,d),t.restore()}_unmapSourceRegion(t,r,n,s,i,o){return o&&(t=(i%180===0?this.squarePixelWidth:this.squarePixelHeight)-t-n),i===90?[t,r,n,s]=[r,this.squarePixelHeight-t-n,s,n]:i===180?[t,r]=[this.squarePixelWidth-t-n,this.squarePixelHeight-r-s]:i===270&&([t,r,n,s]=[this.squarePixelWidth-r-s,t,s,n]),{sx:t,sy:r,sWidth:n,sHeight:s}}_drawWithFitAndMipmapping(t,r,n){const s=t.width,i=t.height,[o,a]=n.rotation%180===0?[this.squarePixelWidth,this.squarePixelHeight]:[this.squarePixelHeight,this.squarePixelWidth],l=n.crop?n.crop.width:o,f=n.crop?n.crop.height:a;let c=0;2*s0?_i(u,h):{canvas:t,context:r,isNew:n.targetIsFresh};m.imageSmoothingQuality="high",n.fillBlack?(m.fillStyle="black",m.fillRect(0,0,u,h)):b||m.clearRect(0,0,u,h),this.drawWithFit(m,{fit:n.fit,rotation:n.rotation,flip:n.flip,crop:n.crop}),m.globalCompositeOperation="copy";for(let p=c;p>1;p--){const w=s*2**p,y=i*2**p;m.drawImage(d,0,0,w,y,0,0,w/2,y/2)}m.globalCompositeOperation="source-over",c>0&&(r.imageSmoothingQuality="high",r.globalCompositeOperation="copy",r.drawImage(d,0,0,2*s,2*i,0,0,s,i),r.globalCompositeOperation="source-over")}toCanvasImageSource(){if(this._closed)throw new Error("VideoSample is closed.");if(C(this._data!==null),this._data instanceof Qt||this._data instanceof Uint8Array){const t=this.toVideoFrame();return queueMicrotask(()=>t.close()),t}else return this._data}async transform(t){if(!t||typeof t!="object")throw new TypeError("options must be an object.");if(t.width!==void 0&&(!Number.isInteger(t.width)||t.width<=0))throw new TypeError("options.width, when provided, must be a positive integer.");if(t.height!==void 0&&(!Number.isInteger(t.height)||t.height<=0))throw new TypeError("options.height, when provided, must be a positive integer.");if(t.roundDimensionsTo!==void 0&&(!Number.isInteger(t.roundDimensionsTo)||t.roundDimensionsTo<=0))throw new TypeError("options.roundDimensionsTo, when provided, must be a positive integer.");if(t.fit!==void 0&&!["fill","contain","cover"].includes(t.fit))throw new TypeError('options.fit, when provided, must be one of "fill", "contain", or "cover".');if(t.width!==void 0&&t.height!==void 0&&t.fit===void 0)throw new TypeError("When both options.width and options.height are provided, options.fit must also be provided.");if(t.rotate!==void 0&&![0,90,180,270].includes(t.rotate))throw new TypeError("options.rotate, when provided, must be 0, 90, 180 or 270.");if(t.flip!==void 0&&typeof t.flip!="boolean")throw new TypeError("options.flip, when provided, must be a boolean.");if(t.crop!==void 0&&Ms(t.crop,"options."),t.alpha!==void 0&&!["keep","discard"].includes(t.alpha))throw new TypeError("options.alpha, when provided, must be 'keep' or 'discard'.");const{rotation:r,flip:n}=v0(this.rotation,this.flip,t.rotate??0,t.flip??!1),[s,i]=r%180===0?[this.squarePixelWidth,this.squarePixelHeight]:[this.squarePixelHeight,this.squarePixelWidth];let o=t.crop;o&&(o=Ii(o,s,i));const a=o?o.width:s,l=o?o.height:i,f=a/l;let c,u;t.width!==void 0&&t.height===void 0?(c=t.width,u=c/f):t.width===void 0&&t.height!==void 0?(u=t.height,c=u*f):t.width!==void 0&&t.height!==void 0?(c=t.width,u=t.height):(c=a,u=l),c=fi(c,t.roundDimensionsTo??1),u=fi(u,t.roundDimensionsTo??1);const h={width:c,height:u,fit:t.fit??"fill",rotation:r,flip:n,crop:o??{left:0,top:0,width:s,height:i},alpha:t.alpha??"keep"};for(const p of rl){let w=p(this,h);if(Gt(w)&&(w=await w),w!==null)return w}const{canvas:d,context:m,isNew:b}=_i(h.width,h.height);return this._drawWithFitAndMipmapping(d,m,{fit:h.fit,rotation:h.rotation,flip:h.flip,crop:h.crop,targetIsFresh:b,fillBlack:h.alpha==="discard"}),new $e(d,{timestamp:this.timestamp,duration:this.duration,rotation:0,flip:!1})}setRotation(t){if(![0,90,180,270].includes(t))throw new TypeError("newRotation must be 0, 90, 180, or 270.");this.rotation=t}setFlip(t){if(typeof t!="boolean")throw new TypeError("newFlip must be a boolean.");this.flip=t}setTimestamp(t){if(!Number.isFinite(t))throw new TypeError("newTimestamp must be a number.");this.timestamp=t}setDuration(t){if(!Number.isFinite(t)||t<0)throw new TypeError("newDuration must be a non-negative number.");this.duration=t}setEncodeOptions(t){if(!t||typeof t!="object")throw new TypeError("newEncodeOptions must be an object.");this.encodeOptions=t}[Symbol.dispose](){this.close()}}const rl=[],nl=3,Mr=[];let Ai=0;const _i=(e,t)=>{for(const s of Mr)if(s.canvas.width===e&&s.canvas.height===t)return s.age=Ai++,{canvas:s.canvas,context:s.context,isNew:!1};let r;if(typeof OffscreenCanvas<"u")r=new OffscreenCanvas(e,t);else{if(typeof window>"u"||typeof document>"u")throw new Error("Cannot transform VideoSamples in this environment. Either run in an environment with OffscreenCanvas or HTMLCanvasElement, or supply a custom VideoSample transformer using registerVideoSampleTransformer().");r=document.createElement("canvas"),r.width=e,r.height=t}const n=r.getContext("2d",{alpha:!0,willReadFrequently:!1});if(!n)throw new Error("The '2d' canvas context is required to transform VideoSamples. Register a custom transformer using registerVideoSampleTransformer to work around this limitation.");return Mr.length>=nl&&Mr.splice(j0(Mr,s=>s.age),1),Mr.push({canvas:r,context:n,age:Ai++}),{canvas:r,context:n,isNew:!0}};class Hn{constructor(t){if(t!==void 0){if(!t||typeof t!="object")throw new TypeError("init.colorSpace, when provided, must be an object.");const r=Object.keys(Er);if(t.primaries!=null&&!r.includes(t.primaries))throw new TypeError(`init.colorSpace.primaries, when provided, must be one of ${r.join(", ")}.`);const n=Object.keys(Tr);if(t.transfer!=null&&!n.includes(t.transfer))throw new TypeError(`init.colorSpace.transfer, when provided, must be one of ${n.join(", ")}.`);const s=Object.keys(Sr);if(t.matrix!=null&&!s.includes(t.matrix))throw new TypeError(`init.colorSpace.matrix, when provided, must be one of ${s.join(", ")}.`);if(t.fullRange!=null&&typeof t.fullRange!="boolean")throw new TypeError("init.colorSpace.fullRange, when provided, must be a boolean.")}this.primaries=(t==null?void 0:t.primaries)??null,this.transfer=(t==null?void 0:t.transfer)??null,this.matrix=(t==null?void 0:t.matrix)??null,this.fullRange=(t==null?void 0:t.fullRange)??null}toJSON(){return{primaries:this.primaries,transfer:this.transfer,matrix:this.matrix,fullRange:this.fullRange}}}const Nr=e=>typeof VideoFrame<"u"&&e instanceof VideoFrame,Ii=(e,t,r)=>{const n=Math.min(e.left,t),s=Math.min(e.top,r),i=Math.min(e.width,t-n),o=Math.min(e.height,r-s);return C(i>=0),C(o>=0),{left:n,top:s,width:i,height:o}},Ms=(e,t)=>{if(!e||typeof e!="object")throw new TypeError(t+"crop, when provided, must be an object.");if(!Number.isInteger(e.left)||e.left<0)throw new TypeError(t+"crop.left must be a non-negative integer.");if(!Number.isInteger(e.top)||e.top<0)throw new TypeError(t+"crop.top must be a non-negative integer.");if(!Number.isInteger(e.width)||e.width<0)throw new TypeError(t+"crop.width must be a non-negative integer.");if(!Number.isInteger(e.height)||e.height<0)throw new TypeError(t+"crop.height must be a non-negative integer.")},Ri=e=>{if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.colorSpace!==void 0&&!["display-p3","srgb"].includes(e.colorSpace))throw new TypeError("options.colorSpace, when provided, must be 'display-p3' or 'srgb'.");if(e.format!==void 0&&typeof e.format!="string")throw new TypeError("options.format, when provided, must be a string.");if(e.layout!==void 0){if(!Array.isArray(e.layout))throw new TypeError("options.layout, when provided, must be an array.");for(const t of e.layout){if(!t||typeof t!="object")throw new TypeError("Each entry in options.layout must be an object.");if(!Number.isInteger(t.offset)||t.offset<0)throw new TypeError("plane.offset must be a non-negative integer.");if(!Number.isInteger(t.stride)||t.stride<0)throw new TypeError("plane.stride must be a non-negative integer.")}}if(e.rect!==void 0){if(!e.rect||typeof e.rect!="object")throw new TypeError("options.rect, when provided, must be an object.");if(e.rect.x!==void 0&&(!Number.isInteger(e.rect.x)||e.rect.x<0))throw new TypeError("options.rect.x, when provided, must be a non-negative integer.");if(e.rect.y!==void 0&&(!Number.isInteger(e.rect.y)||e.rect.y<0))throw new TypeError("options.rect.y, when provided, must be a non-negative integer.");if(e.rect.width!==void 0&&(!Number.isInteger(e.rect.width)||e.rect.width<0))throw new TypeError("options.rect.width, when provided, must be a non-negative integer.");if(e.rect.height!==void 0&&(!Number.isInteger(e.rect.height)||e.rect.height<0))throw new TypeError("options.rect.height, when provided, must be a non-negative integer.")}},sl=(e,t,r)=>{const n=Mn(e),s=[];let i=0;for(const o of n){const a=Math.ceil(t/o.widthDivisor),l=Math.ceil(r/o.heightDivisor),f=a*o.sampleBytes,c=f*l;s.push({offset:i,stride:f}),i+=c}return s},Mn=e=>{const t=(r,n,s,i,o)=>{const a=[{sampleBytes:r,widthDivisor:1,heightDivisor:1},{sampleBytes:n,widthDivisor:s,heightDivisor:i},{sampleBytes:n,widthDivisor:s,heightDivisor:i}];return o&&a.push({sampleBytes:r,widthDivisor:1,heightDivisor:1}),a};switch(e){case"I420":return t(1,1,2,2,!1);case"I420P10":case"I420P12":return t(2,2,2,2,!1);case"I420A":return t(1,1,2,2,!0);case"I420AP10":case"I420AP12":return t(2,2,2,2,!0);case"I422":return t(1,1,2,1,!1);case"I422P10":case"I422P12":return t(2,2,2,1,!1);case"I422A":return t(1,1,2,1,!0);case"I422AP10":case"I422AP12":return t(2,2,2,1,!0);case"I444":return t(1,1,1,1,!1);case"I444P10":case"I444P12":return t(2,2,1,1,!1);case"I444A":return t(1,1,1,1,!0);case"I444AP10":case"I444AP12":return t(2,2,1,1,!0);case"NV12":return[{sampleBytes:1,widthDivisor:1,heightDivisor:1},{sampleBytes:2,widthDivisor:2,heightDivisor:2}];case"RGBA":case"RGBX":case"BGRA":case"BGRX":return[{sampleBytes:4,widthDivisor:1,heightDivisor:1}];default:Xt(e),C(!1)}},Oi=(e,t)=>{const r={left:0,top:0,width:e.codedWidth,height:e.codedHeight},n=t.rect,s=il(r,n,e.codedWidth,e.codedHeight,e.format),i=t.layout;let o;if(!t.format||t.format===e.format)o=e.format;else if(["RGBA","RGBX","BGRA","BGRX"].includes(t.format))o=t.format;else throw new Error("NotSupportedError: Invalid destination format.");return al(s,o,i)},il=(e,t,r,n,s)=>{const i={...e};if(t!==void 0){if(t.width===0||t.height===0)throw new TypeError("visibleRect dimensions cannot be zero.");if((t.x||0)+(t.width||0)>r)throw new TypeError("visibleRect exceeds codedWidth.");if((t.y||0)+(t.height||0)>n)throw new TypeError("visibleRect exceeds codedHeight.");i.x=t.x||0,i.y=t.y||0,i.width=t.width||0,i.height=t.height||0}if(!ol(s,i))throw new TypeError("visibleRect alignment is invalid for the format.");return i},ol=(e,t)=>{if(e===null)return!0;const r=Mn(e);for(let n=0;n{const n=Mn(t),s=n.length;if(r!==void 0&&r.length!==s)throw new TypeError(`Layout must have ${s} planes.`);let i=0;const o=[],a=[];for(let l=0;l4294967295)throw new TypeError("Allocation size exceeds limit.");a.push(b),i=Math.max(i,b);for(let p=0;p 0.");if(!Number.isInteger(t.numberOfChannels)||t.numberOfChannels===0)throw new TypeError("Invalid AudioDataInit: numberOfChannels must be an integer > 0.");if(!Number.isFinite(t==null?void 0:t.timestamp))throw new TypeError("init.timestamp must be a number.");const r=t.data.byteLength/(Lt(t.format)*t.numberOfChannels);if(!Number.isInteger(r))throw new TypeError("Invalid AudioDataInit: data size is not a multiple of frame size.");this.format=t.format,this.sampleRate=t.sampleRate,this.numberOfFrames=r,this.numberOfChannels=t.numberOfChannels,this.timestamp=t.timestamp,this.duration=r/t.sampleRate;let n;if(t.data instanceof ArrayBuffer)n=new Uint8Array(t.data);else if(ArrayBuffer.isView(t.data))n=new Uint8Array(t.data.buffer,t.data.byteOffset,t.data.byteLength);else throw new TypeError("Invalid AudioDataInit: data is not a BufferSource.");const s=this.numberOfFrames*this.numberOfChannels*Lt(this.format);if(n.byteLength=this.numberOfFrames)throw new RangeError("frameOffset out of range");const s=t.frameCount!==void 0?t.frameCount:this.numberOfFrames-n;if(s>this.numberOfFrames-n)throw new RangeError("frameCount out of range");const i=Lt(r),o=tr(r);if(o&&t.planeIndex>=this.numberOfChannels)throw new RangeError("planeIndex out of range");if(!o&&t.planeIndex!==0)throw new RangeError("planeIndex out of range");return(o?s:s*this.numberOfChannels)*i}copyTo(t,r){if(!Sn(t))throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");if(!r||typeof r!="object")throw new TypeError("options must be an object.");if(!Number.isInteger(r.planeIndex)||r.planeIndex<0)throw new TypeError("planeIndex must be a non-negative integer.");if(r.format!==void 0&&!Jr.has(r.format))throw new TypeError("Invalid format.");if(r.frameOffset!==void 0&&(!Number.isInteger(r.frameOffset)||r.frameOffset<0))throw new TypeError("frameOffset must be a non-negative integer.");if(r.frameCount!==void 0&&(!Number.isInteger(r.frameCount)||r.frameCount<0))throw new TypeError("frameCount must be a non-negative integer.");if(this._closed)throw new Error("AudioSample is closed.");const{format:n,frameCount:s,frameOffset:i}=r;let{planeIndex:o}=r,a=this.format;const l=n??this.format;if(!l)throw new Error("Destination format not determined");const f=this.numberOfFrames,c=this.numberOfChannels,u=i??0;if(u>=f)throw new RangeError("frameOffset out of range");const h=s!==void 0?s:f-u;if(h>f-u)throw new RangeError("frameCount out of range");const d=Lt(l),m=tr(l);if(m&&o>=c)throw new RangeError("planeIndex out of range");if(!m&&o!==0)throw new RangeError("planeIndex out of range");const p=(m?h:h*c)*d;if(t.byteLength2&&l!==a){ll(this._data,w,a,l,c,o,u,h);return}else try{this._data.copyTo(t,{planeIndex:o,frameOffset:u,frameCount:h,format:l});return}catch(S){if(l==="f32-planar")throw S;a="f32-planar"}const g=sa(a),k=Lt(a),T=tr(a);let A;if(this._data instanceof Br){const S=B=>{const V=this._data.getDataPlane(B);if(!(V instanceof Uint8Array))throw new TypeError("getDataPlane() must return a Uint8Array.");const W=f*k*(T?1:c);if(V.byteLength!==W)throw new TypeError(`Data plane ${B} has invalid size. Expected exactly ${W} bytes, got ${V.byteLength} bytes.`);return V};if(T)if(m)A=S(o),o=0;else{A=new Uint8Array(f*k*c);for(let B=0;Bthis.numberOfFrames)throw new RangeError("startFrame out of range.");if(r>this.numberOfFrames)throw new RangeError("endFrame out of range.");if(r0)for(let a=0;a0&&this.copyTo(i,{planeIndex:0,format:this.format,frameOffset:t,frameCount:n});return new He({data:i,format:this.format,sampleRate:this.sampleRate,numberOfChannels:this.numberOfChannels,timestamp:this.timestamp+t/this.sampleRate})}close(){this._closed||(Ue==null||Ue.unregister(this),this._data instanceof Br?(this._data._referenceCount--,this._data._referenceCount===0&&this._data.close()):Ar(this._data)?this._data.close():this._data=new Uint8Array(0),this._closed=!0)}toAudioData(){if(this._closed)throw new Error("AudioSample is closed.");return this._data instanceof Br?this._createAudioDataFromData():Ar(this._data)?this._data.timestamp===this.microsecondTimestamp?this._data.clone():this._createAudioDataFromData():new AudioData({format:this.format,sampleRate:this.sampleRate,numberOfFrames:this.numberOfFrames,numberOfChannels:this.numberOfChannels,timestamp:this.microsecondTimestamp,data:this._data.buffer instanceof ArrayBuffer?this._data.buffer:this._data.slice()})}_createAudioDataFromData(){if(tr(this.format)){const t=this.allocationSize({planeIndex:0,format:this.format}),r=new ArrayBuffer(t*this.numberOfChannels);for(let n=0;n0;){const c=Math.min(a,f),u=new Float32Array(s*c);for(let h=0;h0;){const u=Math.min(a,f),h=new Float32Array(s*u);for(let m=0;m{switch(e){case"u8":case"u8-planar":return 1;case"s16":case"s16-planar":return 2;case"s32":case"s32-planar":return 4;case"f32":case"f32-planar":return 4;default:throw new Error("Unknown AudioSampleFormat")}},tr=e=>{switch(e){case"u8-planar":case"s16-planar":case"s32-planar":case"f32-planar":return!0;default:return!1}},sa=e=>{switch(e){case"u8":case"u8-planar":return(t,r)=>(t.getUint8(r)-128)/128;case"s16":case"s16-planar":return(t,r)=>t.getInt16(r,!0)/32768;case"s32":case"s32-planar":return(t,r)=>t.getInt32(r,!0)/2147483648;case"f32":case"f32-planar":return(t,r)=>t.getFloat32(r,!0)}},ia=e=>{switch(e){case"u8":case"u8-planar":return(t,r,n)=>t.setUint8(r,_e(Math.round(n*128)+128,0,255));case"s16":case"s16-planar":return(t,r,n)=>t.setInt16(r,_e(Math.round(n*32768),-32768,32767),!0);case"s32":case"s32-planar":return(t,r,n)=>t.setInt32(r,_e(Math.round(n*2147483648),-2147483648,2147483647),!0);case"f32":case"f32-planar":return(t,r,n)=>t.setFloat32(r,n,!0)}},Ar=e=>typeof AudioData<"u"&&e instanceof AudioData,cl=e=>{switch(e){case"u8-planar":return"u8";case"s16-planar":return"s16";case"s32-planar":return"s32";case"f32-planar":return"f32";default:return e}},ll=(e,t,r,n,s,i,o,a)=>{const l=sa(r),f=ia(n),c=Lt(r),u=Lt(n),h=tr(r);if(tr(n))if(h){const m=new ArrayBuffer(a*c),b=nt(m);e.copyTo(m,{planeIndex:i,frameOffset:o,frameCount:a,format:r});for(let p=0;p{const r=e.allocationSize({format:t,planeIndex:0}),n=new ArrayBuffer(r);return e.copyTo(n,{format:t,planeIndex:0}),new He({data:n,format:t,numberOfChannels:e.numberOfChannels,sampleRate:e.sampleRate,timestamp:e.timestamp,duration:e.duration})};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Fi=new Map,Vi=new Map,ul=e=>{if(!e||typeof e!="object")throw new TypeError("Encoding config must be an object.");if(!Ot.includes(e.codec))throw new TypeError(`Invalid video codec '${e.codec}'. Must be one of: ${Ot.join(", ")}.`);const t=e.bitrate;if(e.quality===void 0&&t===void 0)throw new TypeError("config.quality must be provided.");if(e.quality!==void 0&&t!==void 0)throw new TypeError("config.quality and config.bitrate cannot both be provided.");if(e.quality!==void 0&&!(e.quality instanceof Ke))throw new TypeError("config.quality, when provided, must be a Quality.");if(t!==void 0&&!(t instanceof Ke)&&(!Number.isInteger(t)||t<=0))throw new TypeError("config.bitrate, when provided, must be a positive integer or a quality.");if(e.keyFrameInterval!==void 0&&(!Number.isFinite(e.keyFrameInterval)||e.keyFrameInterval<0))throw new TypeError("config.keyFrameInterval, when provided, must be a non-negative number.");if(e.sizeChangeBehavior!==void 0&&!["deny","passThrough","fill","contain","cover"].includes(e.sizeChangeBehavior))throw new TypeError("config.sizeChangeBehavior, when provided, must be 'deny', 'passThrough', 'fill', 'contain' or 'cover'.");if(e.transform!==void 0){if(typeof e.transform!="object"||!e.transform)throw new TypeError("config.transform, when provided, must be an object.");if(e.transform.width!==void 0&&(!Number.isInteger(e.transform.width)||e.transform.width<=0))throw new TypeError("config.transform.width, when provided, must be a positive integer.");if(e.transform.height!==void 0&&(!Number.isInteger(e.transform.height)||e.transform.height<=0))throw new TypeError("config.transform.height, when provided, must be a positive integer.");if(e.transform.fit!==void 0&&!["fill","contain","cover"].includes(e.transform.fit))throw new TypeError('config.transform.fit, when provided, must be one of "fill", "contain", or "cover".');if(e.transform.width!==void 0&&e.transform.height!==void 0&&e.transform.fit===void 0&&!["fill","contain","cover"].includes(e.sizeChangeBehavior))throw new TypeError("When both config.transform.width and config.transform.height are provided, config.transform.fit must also be provided.");if(e.transform.fit!==void 0&&["fill","contain","cover"].includes(e.sizeChangeBehavior)&&e.transform.fit!==e.sizeChangeBehavior)throw new TypeError("config.transform.fit, when provided, cannot differ from config.sizeChangeBehavior when config.sizeChangeBehavior is 'fill', 'contain' or 'cover', as sizeChangeBehavior already determines the fitting algorithm.");if(e.transform.rotate!==void 0&&![0,90,180,270].includes(e.transform.rotate))throw new TypeError("config.transform.rotate, when provided, must be 0, 90, 180 or 270.");if(e.transform.flip!==void 0&&typeof e.transform.flip!="boolean")throw new TypeError("config.transform.flip, when provided, must be a boolean.");if(e.transform.crop!==void 0&&Ms(e.transform.crop,"config.transform."),e.transform.process!==void 0&&typeof e.transform.process!="function")throw new TypeError("config.transform.process, when provided, must be a function.");if(e.transform.frameRate!==void 0&&(!Number.isFinite(e.transform.frameRate)||e.transform.frameRate<=0))throw new TypeError("config.transform.frameRate, when provided, must be a finite positive number.");if(e.transform.force!==void 0&&typeof e.transform.force!="boolean")throw new TypeError("config.transform.force, when provided, must be a boolean.")}if(e.onEncodedPacket!==void 0&&typeof e.onEncodedPacket!="function")throw new TypeError("config.onEncodedPacket, when provided, must be a function.");if(e.onEncoderConfig!==void 0&&typeof e.onEncoderConfig!="function")throw new TypeError("config.onEncoderConfig, when provided, must be a function.");if(e.onEncodedSample!==void 0&&typeof e.onEncodedSample!="function")throw new TypeError("config.onEncodedSample, when provided, must be a function.");oa(e.codec,e)},oa=(e,t)=>{if(!t||typeof t!="object")throw new TypeError("Encoding options must be an object.");if(t.alpha!==void 0&&!["discard","keep"].includes(t.alpha))throw new TypeError("options.alpha, when provided, must be 'discard' or 'keep'.");const r=t.bitrateMode;if(r!==void 0&&!["constant","variable"].includes(r))throw new TypeError("bitrateMode, when provided, must be 'constant' or 'variable'.");if(t.latencyMode!==void 0&&!["quality","realtime"].includes(t.latencyMode))throw new TypeError("latencyMode, when provided, must be 'quality' or 'realtime'.");if(t.fullCodecString!==void 0&&typeof t.fullCodecString!="string")throw new TypeError("fullCodecString, when provided, must be a string.");if(t.fullCodecString!==void 0&&Pn(t.fullCodecString)!==e)throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${e}).`);if(t.hardwareAcceleration!==void 0&&!["no-preference","prefer-hardware","prefer-software"].includes(t.hardwareAcceleration))throw new TypeError("hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'.");if(t.scalabilityMode!==void 0&&typeof t.scalabilityMode!="string")throw new TypeError("scalabilityMode, when provided, must be a string.");if(t.contentHint!==void 0&&typeof t.contentHint!="string")throw new TypeError("contentHint, when provided, must be a string.")},aa=e=>{const t=e.bitrateMode,r=e.quality._toVideoRateControl(e.codec,e.width,e.height,t),n=(i,o,a)=>({codec:e.fullCodecString??Fc(e.codec,e.width,e.height,a,e.alpha==="keep"),width:e.width,height:e.height,displayWidth:e.squarePixelWidth,displayHeight:e.squarePixelHeight,bitrate:i,bitrateMode:o,alpha:e.alpha??"discard",framerate:e.framerate,latencyMode:e.latencyMode,hardwareAcceleration:e.hardwareAcceleration,scalabilityMode:e.scalabilityMode,contentHint:e.contentHint,...jc(e.codec)}),s=[];return r.quantizer!==null&&s.push({config:n(void 0,"quantizer",r.bitrate),quantizer:r.quantizer}),r.bitrateMode!=="quantizer"&&s.push({config:n(r.bitrate,r.bitrateMode,r.bitrate),quantizer:null}),C(s.length>0),s},dl=e=>{if(!e||typeof e!="object")throw new TypeError("Encoding config must be an object.");if(!sr.includes(e.codec))throw new TypeError(`Invalid audio codec '${e.codec}'. Must be one of: ${sr.join(", ")}.`);const t=e.bitrate;if(e.quality===void 0&&t===void 0&&!(pt.includes(e.codec)||e.codec==="flac"))throw new TypeError("config.quality must be provided for compressed audio codecs.");if(e.quality!==void 0&&t!==void 0)throw new TypeError("config.quality and config.bitrate cannot both be provided.");if(e.quality!==void 0&&!(e.quality instanceof Ke))throw new TypeError("config.quality, when provided, must be a Quality.");if(t!==void 0&&!(t instanceof Ke)&&(!Number.isInteger(t)||t<=0))throw new TypeError("config.bitrate, when provided, must be a positive integer or a quality.");if(e.transform!==void 0){if(typeof e.transform!="object"||!e.transform)throw new TypeError("config.transform, when provided, must be an object.");if(e.transform.numberOfChannels!==void 0&&(!Number.isInteger(e.transform.numberOfChannels)||e.transform.numberOfChannels<=0))throw new TypeError("config.transform.numberOfChannels, when provided, must be a positive integer.");if(e.transform.sampleRate!==void 0&&(!Number.isInteger(e.transform.sampleRate)||e.transform.sampleRate<=0))throw new TypeError("config.transform.sampleRate, when provided, must be a positive integer.");if(e.transform.sampleFormat!==void 0&&!["u8","s16","s32","f32"].includes(e.transform.sampleFormat))throw new TypeError("config.transform.sampleFormat, when provided, must be one of: u8, s16, s32, f32.");if(e.transform.process!==void 0&&typeof e.transform.process!="function")throw new TypeError("config.transform.process, when provided, must be a function.")}if(e.onEncodedPacket!==void 0&&typeof e.onEncodedPacket!="function")throw new TypeError("config.onEncodedPacket, when provided, must be a function.");if(e.onEncoderConfig!==void 0&&typeof e.onEncoderConfig!="function")throw new TypeError("config.onEncoderConfig, when provided, must be a function.");if(e.onEncodedSample!==void 0&&typeof e.onEncodedSample!="function")throw new TypeError("config.onEncodedSample, when provided, must be a function.");ca(e.codec,e)},ca=(e,t)=>{if(!t||typeof t!="object")throw new TypeError("Encoding options must be an object.");const r=t.bitrateMode;if(r!==void 0&&!["constant","variable"].includes(r))throw new TypeError("bitrateMode, when provided, must be 'constant' or 'variable'.");if(t.fullCodecString!==void 0&&typeof t.fullCodecString!="string")throw new TypeError("fullCodecString, when provided, must be a string.");if(t.fullCodecString!==void 0&&Pn(t.fullCodecString)!==e)throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${e}).`)},la=e=>{var r,n;const t=e.bitrateMode;return{codec:e.fullCodecString??zc(e.codec,e.numberOfChannels,e.sampleRate),numberOfChannels:e.numberOfChannels,sampleRate:e.sampleRate,bitrate:(r=e.quality)==null?void 0:r._toAudioBitrate(e.codec),bitrateMode:((n=e.quality)==null?void 0:n._bitrateMode)??t,...Wc(e.codec)}};class Ke{constructor(t){if((typeof t=="number"||typeof t=="string")&&(t={quality:t}),!t||typeof t!="object")throw new TypeError("options must be an object.");if(t.bitrateMode!==void 0&&!["constant","variable"].includes(t.bitrateMode))throw new TypeError("options.bitrateMode, when provided, must be 'constant' or 'variable'.");if("quality"in t){if(typeof t.quality=="string"?!(t.quality in Di):typeof t.quality!="number"||Number.isNaN(t.quality))throw new TypeError("options.quality must be a number, or one of 'very-low', 'low', 'medium', 'high' or 'very-high'.");if(t.preferBitrate!==void 0&&typeof t.preferBitrate!="boolean")throw new TypeError("options.preferBitrate, when provided, must be a boolean.");if("bitrate"in t||"quantizer"in t)throw new TypeError("options.quality cannot be combined with options.bitrate or options.quantizer.");this._quality=typeof t.quality=="string"?Di[t.quality]:t.quality,this._preferBitrate=t.preferBitrate??!1,this._bitrate=void 0,this._quantizer=void 0}else{if(t.bitrate!==void 0&&(!Number.isInteger(t.bitrate)||t.bitrate<=0))throw new TypeError("options.bitrate, when provided, must be a positive integer.");if(t.quantizer!==void 0&&(!Number.isInteger(t.quantizer)||t.quantizer<0))throw new TypeError("options.quantizer, when provided, must be a non-negative integer.");if(t.bitrate===void 0&&t.quantizer===void 0)throw new TypeError("At least one of options.bitrate or options.quantizer must be set.");if("preferBitrate"in t)throw new TypeError("options.preferBitrate can only be combined with options.quality.");this._quality=void 0,this._preferBitrate=!1,this._bitrate=t.bitrate,this._quantizer=t.quantizer}this._bitrateMode=t.bitrateMode}_toVideoRateControl(t,r,n,s){const i=hl[t];let o=null,a=this._bitrateMode??s??"variable";if(this._quantizer!==void 0){if(i)if(this._quantizeri.max){if(this._bitrate===void 0)throw new Error(`Quantizer ${this._quantizer} is out of range for codec '${t}'; must be between ${i.min} and ${i.max}.`)}else o=this._quantizer,this._bitrate===void 0&&(a="quantizer");else if(this._bitrate===void 0)throw new Error(`Codec '${t}' does not support quantizer-based encoding. Provide a bitrate in the Quality to define a fallback.`)}else this._bitrate===void 0&&i&&!this._preferBitrate&&(C(this._quality!==void 0),o=_e(Math.round(P0(i.worst,i.best,this._quality)),i.min,i.max));let l;if(this._bitrate!==void 0)l=this._bitrate;else{let f=this._quality;f===void 0&&(C(o!==null&&i),f=_e((o-i.worst)/(i.best-i.worst),0,1)),l=zi(t,r,n,Gn(f))}return{quantizer:o,bitrate:l,bitrateMode:a}}_toVideoBitrate(t,r,n){return this._bitrate!==void 0?this._bitrate:(C(this._quality!==void 0),zi(t,r,n,Gn(this._quality)))}_toAudioBitrate(t){if(pt.includes(t)||t==="flac")return;if(this._bitrate!==void 0)return this._bitrate;if(this._quality===void 0)throw new Error("This Quality defines neither a quality level nor a bitrate and therefore cannot be used for audio encoding.");const r=Gn(this._quality),s={aac:128e3,opus:64e3,mp3:16e4,vorbis:64e3,ac3:384e3,eac3:192e3,dts:768e3}[t];if(!s)throw new Error(`Unhandled codec: ${t}`);let i=s*r;return t==="aac"?i=[96e3,128e3,16e4,192e3].reduce((a,l)=>Math.abs(l-i)Math.abs(l-i).3*Math.exp(2.5538*e),zi=(e,t,r,n)=>{const s=t*r,i=1920*1080,o=3e6,a=Math.pow(s/i,.95),l=o*a,f={avc:1,hevc:.6,vp9:.6,av1:.4,vp8:1.2,prores:22e7/o},u=l*f[e]*n;return Math.ceil(u/1e3)*1e3},fa=(e,t)=>{if(e==="avc")return{avc:{quantizer:t}};if(e==="hevc")return{hevc:{quantizer:t}};if(e==="vp9")return{vp9:{quantizer:t}};if(e==="av1")return{av1:{quantizer:t}};C(!1)},ml=new Ke("high"),pl=async(e,t={})=>{const{width:r=1280,height:n=720,quality:s,bitrate:i,frameRate:o,...a}=t;if(!Ot.includes(e))return!1;if(!Number.isInteger(r)||r<=0)throw new TypeError("width must be a positive integer.");if(!Number.isInteger(n)||n<=0)throw new TypeError("height must be a positive integer.");if(s!==void 0&&!(s instanceof Ke))throw new TypeError("quality, when provided, must be a Quality.");if(s!==void 0&&i!==void 0)throw new TypeError("quality and bitrate cannot both be provided.");if(i!==void 0&&!(i instanceof Ke)&&(!Number.isInteger(i)||i<=0))throw new TypeError("bitrate must be a positive integer or a quality.");if(o!==void 0&&(!Number.isFinite(o)||o<=0))throw new TypeError("frameRate, when provided, must be a finite positive number.");oa(e,a);const l=Nn(s,i)??new Ke("medium");let f;try{f=aa({codec:e,width:r,height:n,quality:l,framerate:o,...a,alpha:"discard"})}catch{return!1}const c=JSON.stringify(f),u=Fi.get(c);if(u)return u;const h=(async()=>{for(const{config:m}of f)if(ua.some(b=>b.supports(e,m)))return!0;if(typeof VideoEncoder>"u"||(r%2===1||n%2===1)&&(e==="avc"||e==="hevc"))return!1;for(const{config:m,quantizer:b}of f){try{if(!(await VideoEncoder.isConfigSupported(m)).supported)continue}catch{continue}if(!gn()||await new Promise(async w=>{try{const y=new VideoEncoder({output:()=>{},error:()=>w(!1)});y.configure(m);const g=new Uint8Array(r*n*4),k=new VideoFrame(g,{format:"RGBA",codedWidth:r,codedHeight:n,timestamp:0});y.encode(k,b!==null?fa(e,b):void 0),k.close(),await y.flush(),w(!0)}catch{w(!1)}}))return!0}return!1})();return Fi.set(c,h),h},bl=async(e,t={})=>{const{numberOfChannels:r=2,sampleRate:n=48e3,quality:s,bitrate:i,...o}=t;if(!sr.includes(e))return!1;if(!Number.isInteger(r)||r<=0)throw new TypeError("numberOfChannels must be a positive integer.");if(!Number.isInteger(n)||n<=0)throw new TypeError("sampleRate must be a positive integer.");if(s!==void 0&&!(s instanceof Ke))throw new TypeError("quality, when provided, must be a Quality.");if(s!==void 0&&i!==void 0)throw new TypeError("quality and bitrate cannot both be provided.");if(i!==void 0&&!(i instanceof Ke)&&(!Number.isInteger(i)||i<=0))throw new TypeError("bitrate must be a positive integer.");ca(e,o);const a=Nn(s,i)??new Ke("medium"),l=la({codec:e,numberOfChannels:r,sampleRate:n,quality:a,...o}),f=JSON.stringify(l),c=Vi.get(f);if(c)return c;const u=(async()=>{if(da.some(h=>h.supports(e,l))||pt.includes(e))return!0;if(typeof AudioEncoder>"u")return!1;try{return(await AudioEncoder.isConfigSupported(l)).supported===!0}catch{return!1}})();return Vi.set(f,u),u},Nn=(e,t)=>{if(e!==void 0)return e;if(t!==void 0)return t instanceof Ke?t:new Ke({bitrate:t})},wl=async(e,t)=>{for(const r of e)if(await pl(r,t))return r;return null},yl=async(e,t)=>{for(const r of e)if(await bl(r,t))return r;return null};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const ua=[],da=[];/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const gl=e=>{let n=e>>2,s=4096,i=0,o=12,a=0;for(n<0&&(n=-n,i=128),n+=33,n>8191&&(n=8191);(n&s)!==s&&o>=5;)s>>=1,o--;return a=n>>o-4&15,~(i|o-5<<4|a)&255},xl=e=>{let r=2048,n=0,s=11,i=0,o=e>>3;for(o<0?o=-o-1:n=128,o>4095&&(o=4095);(o&r)!==r&&s>=5;)r>>=1,s--;return i=o>>(s===4?1:s-4)&15,(n|s-4<<4|i)^85};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class vr{constructor(t,r,n,s,i){this.bytes=t,this.view=r,this.offset=n,this.start=s,this.end=i,this.bufferPos=s-n}static tempFromBytes(t){return new vr(t,nt(t),0,0,t.length)}get length(){return this.end-this.start}get filePos(){return this.offset+this.bufferPos}set filePos(t){this.bufferPos=t-this.offset}get remainingLength(){return Math.max(this.end-this.filePos,0)}skip(t){this.bufferPos+=t}slice(t,r=this.end-t){if(tthis.end)throw new RangeError("Slicing outside of original slice.");return new vr(this.bytes,this.view,this.offset,t,t+r)}}const ha=(e,t)=>{if(e.filePose.end)throw new RangeError(`Tried reading [${e.filePos}, ${e.filePos+t}), but slice is [${e.start}, ${e.end}). This is likely an internal error, please report it alongside the file that caused it.`)},ma=(e,t)=>{ha(e,t);const r=e.bytes.subarray(e.bufferPos,e.bufferPos+t);return e.bufferPos+=t,r},er=e=>(ha(e,1),e.view.getUint8(e.bufferPos++));/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class vl{constructor(t){this.mutex=new jo,this.trackTimestampInfo=new WeakMap,this.output=t}onTrackClose(t){}validateTimestamp(t,r,n){let s=this.trackTimestampInfo.get(t);if(s){if(n&&(s.maxTimestampBeforeLastKeyPacket=s.maxTimestamp),s.maxTimestampBeforeLastKeyPacket!==null&&r/g,kl=e=>{const t=Math.floor(e/36e5),r=Math.floor(e%(3600*1e3)/(60*1e3)),n=Math.floor(e%(60*1e3)/1e3),s=e%1e3;return t.toString().padStart(2,"0")+":"+r.toString().padStart(2,"0")+":"+n.toString().padStart(2,"0")+"."+s.toString().padStart(3,"0")};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class en{constructor(t){this.writer=t,this.helper=new Uint8Array(8),this.helperView=new DataView(this.helper.buffer),this.offsets=new WeakMap}writeU32(t){this.helperView.setUint32(0,t,!1),this.writer.write(this.helper.subarray(0,4))}writeU64(t){this.helperView.setUint32(0,Math.floor(t/2**32),!1),this.helperView.setUint32(4,t,!1),this.writer.write(this.helper.subarray(0,8))}writeAscii(t){for(let r=0;r[(e%256+256)%256],Y=e=>(it.setUint16(0,e,!1),[se[0],se[1]]),Us=e=>(it.setInt16(0,e,!1),[se[0],se[1]]),pa=e=>(it.setUint32(0,e,!1),[se[1],se[2],se[3]]),_=e=>(it.setUint32(0,e,!1),[se[0],se[1],se[2],se[3]]),Et=e=>(it.setInt32(0,e,!1),[se[0],se[1],se[2],se[3]]),bt=e=>(it.setUint32(0,Math.floor(e/2**32),!1),it.setUint32(4,e,!1),[se[0],se[1],se[2],se[3],se[4],se[5],se[6],se[7]]),Wi=e=>(it.setInt32(0,Math.floor(e/2**32),!1),it.setUint32(4,e,!1),[se[0],se[1],se[2],se[3],se[4],se[5],se[6],se[7]]),ba=e=>(it.setInt16(0,2**8*e,!1),[se[0],se[1]]),rt=e=>(it.setInt32(0,2**16*e,!1),[se[0],se[1],se[2],se[3]]),Xn=e=>(it.setInt32(0,2**30*e,!1),[se[0],se[1],se[2],se[3]]),Qn=(e,t)=>{const r=[];let n=e;do{let s=n&127;n>>=7,r.length>0&&(s|=128),r.push(s)}while(n>0||t);return r.reverse()},he=(e,t=!1)=>{const r=Array(e.length).fill(null).map((n,s)=>e.charCodeAt(s));return t&&r.push(0),r},wa=e=>[rt(e[0]),rt(e[1]),Xn(e[2]),rt(e[3]),rt(e[4]),Xn(e[5]),rt(e[6]),rt(e[7]),Xn(e[8])],X=(e,t,r)=>({type:e,contents:t&&new Uint8Array(t.flat(10)),children:r}),ce=(e,t,r,n,s)=>X(e,[xe(t),pa(r),n??[]],s),El=e=>e.isQuickTime?X("ftyp",[he("qt "),_(512),he("qt ")]):e.fragmented?e.cmaf?X("ftyp",[he("iso5"),_(512),he("iso5"),he("iso6"),he("mp41"),he("cmfc"),he("dash")]):X("ftyp",[he("iso5"),_(512),he("iso5"),he("iso6"),he("mp41")]):X("ftyp",[he("isom"),_(512),he("isom"),e.holdsAvc?he("avc1"):[],he("mp41")]),Li=()=>X("styp",[he("iso5"),_(0),he("iso5"),he("iso6"),he("mp41"),he("cmfc"),he("dash")]),qi=(e,t)=>{const r=Math.max(0,e.minWrittenTimestamp);let n=Math.max(0,e.maxWrittenEndTimestamp-r);return Number.isFinite(n)||(n=0),ce("sidx",1,0,[_(1),_(Xe),bt(pe(r,Xe)),bt(0),Y(0),Y(1),_(t&2147483647),_(pe(n,Xe)),_(0)])},tn=e=>({type:"mdat",largeSize:e}),Tl=e=>({type:"free",size:e}),_r=e=>X("moov",void 0,[Sl(e.creationTime,e.trackDatas),...e.trackDatas.map(t=>Cl(t,e.creationTime)),e.isFragmented?hf(e.trackDatas):null,Cf(e)]),Sl=(e,t)=>{const r=Math.max(0,...t.map(o=>Math.max(0,pe(kr(o),Xe)+pe(o.startTimestampOffset??0,Xe)))),n=Math.max(0,...t.map(o=>o.track.id))+1,s=!It(e)||!It(r),i=s?bt:_;return ce("mvhd",+s,0,[i(e),i(e),_(Xe),i(r),rt(1),ba(1),Array(10).fill(0),wa(zo),Array(24).fill(0),_(n)])},Cl=(e,t)=>{const r=Ff(e),n=e.startTimestampOffset!==null&&e.startTimestampOffset!==0;return X("trak",void 0,[Pl(e,t),n?Ml(e):null,Nl(e,t),r.name!==void 0?X("udta",void 0,[X("name",[...Tt.encode(r.name)])]):null])},Pl=(e,t)=>{var l;const r=Math.max(0,pe(kr(e),Xe)+pe(e.startTimestampOffset??0,Xe)),n=!It(t)||!It(r),s=n?bt:_;let i;if(e.type==="video"&&e.track.metadata.transformationMatrix)i=e.track.metadata.transformationMatrix;else if(e.type==="video"){const{rotation:f,flip:c}=e.track.metadata,u=Es(g0(f??0),x0(c?-1:1,1));i=y0(u,e.info.width,e.info.height)}else i=zo;let o=2;((l=e.track.metadata.disposition)==null?void 0:l.default)!==!1&&(o|=1);const a=e.type==="video"?0:e.type==="audio"?1:e.type==="subtitle"?2:Xt(e);return ce("tkhd",+n,o,[s(t),s(t),_(e.track.id),_(0),s(r),Array(8).fill(0),Y(0),Y(a),ba(e.type==="audio"?1:0),Y(0),wa(i),rt(e.type==="video"?e.info.width:0),rt(e.type==="video"?e.info.height:0)])},Ml=e=>{const t=e.startTimestampOffset;if(C(t!==null),t>0){const r=pe(t,Xe),n=pe(kr(e),Xe),s=!It(r)||!It(n),i=s?bt:_,o=s?Wi:Et;return X("edts",void 0,[ce("elst",s?1:0,0,[_(2),i(r),o(-1),rt(1),i(n),o(0),rt(1)])])}else{const r=pe(-t,e.timescale),n=Math.max(0,pe(kr(e),Xe)+pe(t,Xe)),s=!k0(r)||!It(n),i=s?bt:_,o=s?Wi:Et;return X("edts",void 0,[ce("elst",s?1:0,0,[_(1),i(n),o(r),rt(1)])])}},Nl=(e,t)=>X("mdia",void 0,[Bl(e,t),Hs(!0,Al[e.type],_l[e.type]),Il(e)]),Bl=(e,t)=>{const r=pe(kr(e),e.timescale),n=!It(t)||!It(r),s=n?bt:_;return ce("mdhd",+n,0,[s(t),s(t),_(e.timescale),s(r),Y(va(e.track.metadata.languageCode??M0)),Y(0)])},Al={video:"vide",audio:"soun",subtitle:"text"},_l={video:"MediabunnyVideoHandler",audio:"MediabunnySoundHandler",subtitle:"MediabunnyTextHandler"},Hs=(e,t,r,n="\0\0\0\0")=>ce("hdlr",0,0,[e?he("mhlr"):_(0),he(t),he(n),_(0),_(0),he(r,!0)]),Il=e=>X("minf",void 0,[Vl[e.type](),Dl(),Wl(e)]),Rl=()=>ce("vmhd",0,1,[Y(0),Y(0),Y(0),Y(0)]),Ol=()=>ce("smhd",0,0,[Y(0),Y(0)]),Fl=()=>ce("nmhd",0,0),Vl={video:Rl,audio:Ol,subtitle:Fl},Dl=()=>X("dinf",void 0,[zl()]),zl=()=>ce("dref",0,0,[_(1)],[jl()]),jl=()=>ce("url ",0,1),Wl=e=>{const t=e.compositionTimeOffsetTable.length>1||e.compositionTimeOffsetTable.some(r=>r.sampleCompositionTimeOffset!==0);return X("stbl",void 0,[Ll(e),of(e),t?uf(e):null,t?df(e):null,cf(e),lf(e),ff(e),af(e)])},Ll=e=>{let t;if(e.type==="video")t=ql(Bf(e.track.source._codec,e.info.decoderConfig.codec),e);else if(e.type==="audio"){const r=xa(e.track.source._codec,e.info.decoderConfig.codec,e.muxer.isQuickTime);C(r),t=Ql(r,e)}else e.type==="subtitle"&&(t=nf(_f[e.track.source._codec],e));return C(t),ce("stsd",0,0,[_(1)],[t])},ql=(e,t)=>{var r;return X(e,[Array(6).fill(0),Y(1),Y(0),Y(0),Array(12).fill(0),Y(t.info.width),Y(t.info.height),_(4718592),_(4718592),_(0),Y(1),xe(qn.length),he(qn),Array(31-qn.length).fill(0),Y(t.info.hasAlphaChannel?32:24),Us(65535)],[((r=Yn[t.track.source._codec])==null?void 0:r.call(Yn,t))??null,$l(t),T0(t.info.decoderConfig.colorSpace)?null:Ul(t),Gs(t)])},Gs=e=>e.avgBitrate===0&&e.maxBitrate===0?null:X("btrt",[_(0),_(e.maxBitrate),_(e.avgBitrate)]),$l=e=>e.info.pixelAspectRatio.num===e.info.pixelAspectRatio.den?null:X("pasp",[_(e.info.pixelAspectRatio.num),_(e.info.pixelAspectRatio.den)]),Ul=e=>{const t=e.info.decoderConfig.colorSpace;return X("colr",[he(e.muxer.isQuickTime?"nclc":"nclx"),Y((t==null?void 0:t.primaries)!=null?Er[t.primaries]:2),Y((t==null?void 0:t.transfer)!=null?Tr[t.transfer]:2),Y((t==null?void 0:t.matrix)!=null?Sr[t.matrix]:2),e.muxer.isQuickTime?[]:xe((t!=null&&t.fullRange?1:0)<<7)])},Hl=e=>e.info.decoderConfig&&X("avcC",[...Ie(e.info.decoderConfig.description)]),Gl=e=>e.info.decoderConfig&&X("hvcC",[...Ie(e.info.decoderConfig.description)]),$i=e=>{var h,d,m,b;if(!e.info.decoderConfig)return null;const t=e.info.decoderConfig,r=t.codec.split("."),n=Number(r[1]),s=Number(r[2]),i=Number(r[3]),o=r[4]?Number(r[4]):1,a=r[8]?Number(r[8]):Number(((h=t.colorSpace)==null?void 0:h.fullRange)??0),l=(i<<4)+(o<<1)+a,f=r[5]?Number(r[5]):(d=t.colorSpace)!=null&&d.primaries?Er[t.colorSpace.primaries]:1,c=r[6]?Number(r[6]):(m=t.colorSpace)!=null&&m.transfer?Tr[t.colorSpace.transfer]:1,u=r[7]?Number(r[7]):(b=t.colorSpace)!=null&&b.matrix?Sr[t.colorSpace.matrix]:1;return ce("vpcC",1,0,[xe(n),xe(s),xe(l),xe(f),xe(c),xe(u),Y(0)])},Xl=e=>X("av1C",Vc(e.info.decoderConfig.codec)),Ql=(e,t)=>{var o;let r=0,n,s=16;const i=pt.includes(t.track.source._codec);if(i){const a=t.track.source._codec,{sampleSize:l}=ir(a);s=8*l,s>16&&(r=1)}if(t.muxer.isQuickTime&&(r=1),r===0)n=[Array(6).fill(0),Y(1),Y(r),Y(0),_(0),Y(t.info.numberOfChannels),Y(s),Y(0),Y(0),Y(t.info.sampleRate<2**16?t.info.sampleRate:0),Y(0)];else{const a=i?0:-2;n=[Array(6).fill(0),Y(1),Y(r),Y(0),_(0),Y(t.info.numberOfChannels),Y(Math.min(s,16)),Us(a),Y(0),Y(t.info.sampleRate<2**16?t.info.sampleRate:0),Y(0),i?[_(1),_(s/8),_(t.info.numberOfChannels*s/8)]:[_(0),_(0),_(0)],_(2)]}return X(e,n,[((o=Af(t.track.source._codec,t.muxer.isQuickTime))==null?void 0:o(t))??null,Gs(t)])},Kn=e=>{let t;switch(e.track.source._codec){case"aac":t=64;break;case"mp3":t=107;break;case"vorbis":t=221;break;default:throw new Error(`Unhandled audio codec: ${e.track.source._codec}`)}let r=[...xe(t),...xe(21),...pa(0),..._(e.maxBitrate),..._(e.avgBitrate)];if(e.info.decoderConfig.description){const n=Ie(e.info.decoderConfig.description);r=[...r,...xe(5),...Qn(n.byteLength),...n]}return r=[...Y(1),...xe(0),...xe(4),...Qn(r.length),...r,...xe(6),...xe(1),...xe(2)],r=[...xe(3),...Qn(r.length),...r],ce("esds",0,0,r)},Ft=e=>X("wave",void 0,[Kl(e),Yl(e),X("\0\0\0\0")]),Kl=e=>X("frma",[he(xa(e.track.source._codec,e.info.decoderConfig.codec,e.muxer.isQuickTime))]),Yl=e=>{const{littleEndian:t}=ir(e.track.source._codec);return X("enda",[Y(+t)])},Zl=e=>{var l;let t=e.info.numberOfChannels,r=3840,n=e.info.sampleRate,s=0,i=0,o=new Uint8Array(0);const a=(l=e.info.decoderConfig)==null?void 0:l.description;if(a){C(a.byteLength>=18);const f=Ie(a),c=hc(f);t=c.outputChannelCount,r=c.preSkip,n=c.inputSampleRate,s=c.outputGain,i=c.channelMappingFamily,c.channelMappingTable&&(o=c.channelMappingTable)}return X("dOps",[xe(0),xe(t),Y(r),_(n),Us(s),xe(i),...o])},Jl=e=>{var n;const t=(n=e.info.decoderConfig)==null?void 0:n.description;C(t);const r=Ie(t);return ce("dfLa",0,0,[...r.subarray(4)])},wt=e=>{const{littleEndian:t,sampleSize:r}=ir(e.track.source._codec),n=+t;return ce("pcmC",0,0,[xe(n),xe(8*r)])},ef=e=>{C(e.info.primingPacket);const t=pc(e.info.primingPacket.data);if(!t)throw new Error("Couldn't extract AC-3 frame info from the audio packet. Ensure the packets contain valid AC-3 sync frames (as specified in ETSI TS 102 366).");const r=new Uint8Array(3),n=new Te(r);return n.writeBits(2,t.fscod),n.writeBits(5,t.bsid),n.writeBits(3,t.bsmod),n.writeBits(3,t.acmod),n.writeBits(1,t.lfeon),n.writeBits(5,t.bitRateCode),n.writeBits(5,0),X("dac3",[...r])},tf=e=>{C(e.info.primingPacket);const t=wc(e.info.primingPacket.data);if(!t)throw new Error("Couldn't extract E-AC-3 frame info from the audio packet. Ensure the packets contain valid E-AC-3 sync frames (as specified in ETSI TS 102 366).");let r=16;for(const o of t.substreams)r+=23,o.numDepSub>0?r+=9:r+=1;const n=Math.ceil(r/8),s=new Uint8Array(n),i=new Te(s);i.writeBits(13,t.dataRate),i.writeBits(3,t.substreams.length-1);for(const o of t.substreams)i.writeBits(2,o.fscod),i.writeBits(5,o.bsid),i.writeBits(1,0),i.writeBits(1,0),i.writeBits(3,o.bsmod),i.writeBits(3,o.acmod),i.writeBits(1,o.lfeon),i.writeBits(3,0),i.writeBits(4,o.numDepSub),o.numDepSub>0?i.writeBits(9,o.chanLoc):i.writeBits(1,0);return X("dec3",[...s])},rf=e=>{C(e.info.primingPacket);const t=Ac(e.info.primingPacket.data);if(!t)throw new Error("Couldn't extract DTS frame info from the audio packet. Ensure the packets contain valid DTS frames as specified in ETSI TS 102 114.");return X("ddts",[...Rc(t)])},nf=(e,t)=>X(e,[Array(6).fill(0),Y(1)],[If[t.track.source._codec](t),Gs(t)]),sf=e=>X("vttC",[...Tt.encode(e.info.config.description)]),of=e=>ce("stts",0,0,[_(e.timeToSampleTable.length),e.timeToSampleTable.map(t=>[_(t.sampleCount),_(t.sampleDelta)])]),af=e=>{if(e.samples.every(r=>r.type==="key"))return null;const t=[...e.samples.entries()].filter(([,r])=>r.type==="key");return ce("stss",0,0,[_(t.length),t.map(([r])=>_(r+1))])},cf=e=>ce("stsc",0,0,[_(e.compactlyCodedChunkTable.length),e.compactlyCodedChunkTable.map(t=>[_(t.firstChunk),_(t.samplesPerChunk),_(1)])]),lf=e=>{if(e.type==="audio"&&e.info.requiresPcmTransformation){const{sampleSize:t}=ir(e.track.source._codec);return ce("stsz",0,0,[_(t*e.info.numberOfChannels),_(e.samples.reduce((r,n)=>r+pe(n.duration,e.timescale),0))])}return ce("stsz",0,0,[_(0),_(e.samples.length),e.samples.map(t=>_(t.size))])},ff=e=>e.finalizedChunks.length>0&&ze(e.finalizedChunks).offset>=2**32?ce("co64",0,0,[_(e.finalizedChunks.length),e.finalizedChunks.map(t=>bt(t.offset))]):ce("stco",0,0,[_(e.finalizedChunks.length),e.finalizedChunks.map(t=>_(t.offset))]),uf=e=>ce("ctts",1,0,[_(e.compositionTimeOffsetTable.length),e.compositionTimeOffsetTable.map(t=>[_(t.sampleCount),Et(t.sampleCompositionTimeOffset)])]),df=e=>{let t=1/0,r=-1/0,n=1/0,s=-1/0;C(e.compositionTimeOffsetTable.length>0),C(e.samples.length>0);for(let o=0;o=2**31?null:ce("cslg",0,0,[Et(i),Et(t),Et(r),Et(n),Et(s)])},hf=e=>X("mvex",void 0,e.map(mf)),mf=e=>ce("trex",0,0,[_(e.track.id),_(1),_(0),_(0),_(0)]),Ui=(e,t)=>X("moof",void 0,[pf(e),...t.map(bf)]),pf=e=>ce("mfhd",0,0,[_(e)]),ya=e=>{let t=0,r=0;const n=0,s=0,i=e.type==="delta";return r|=+i,i?t|=1:t|=2,t<<24|r<<16|n<<8|s},bf=e=>X("traf",void 0,[wf(e),yf(e),gf(e)]),wf=e=>{C(e.currentChunk);let t=0;t|=8,t|=16,t|=32,t|=131072;const r=e.currentChunk.samples[1]??e.currentChunk.samples[0],n={duration:r.timescaleUnitsToNextSample,size:r.size,flags:ya(r)};return ce("tfhd",0,t,[_(e.track.id),_(n.duration),_(n.size),_(n.flags)])},yf=e=>(C(e.currentChunk),ce("tfdt",1,0,[bt(pe(e.currentChunk.startTimestamp,e.timescale))])),gf=e=>{C(e.currentChunk);const t=e.currentChunk.samples.map(b=>b.timescaleUnitsToNextSample),r=e.currentChunk.samples.map(b=>b.size),n=e.currentChunk.samples.map(ya),s=e.currentChunk.samples.map(b=>pe(b.timestamp-b.decodeTimestamp,e.timescale)),i=new Set(t),o=new Set(r),a=new Set(n),l=new Set(s),f=a.size===2&&n[0]!==n[1],c=i.size>1,u=o.size>1,h=!f&&a.size>1,d=l.size>1||[...l].some(b=>b!==0);let m=0;return m|=1,m|=4*+f,m|=256*+c,m|=512*+u,m|=1024*+h,m|=2048*+d,ce("trun",1,m,[_(e.currentChunk.samples.length),_(e.currentChunk.offset-e.currentChunk.moofOffset||0),f?_(n[0]):[],e.currentChunk.samples.map((b,p)=>[c?_(t[p]):[],u?_(r[p]):[],h?_(n[p]):[],d?Et(s[p]):[]])])},xf=e=>X("mfra",void 0,[...e.map(vf),kf()]),vf=e=>ce("tfra",1,0,[_(e.track.id),_(63),_(e.finalizedChunks.length),e.finalizedChunks.map(r=>[bt(pe(r.samples[0].timestamp,e.timescale)),bt(r.moofOffset),_(r.trafIndex+1),_(1),_(1)])]),kf=()=>ce("mfro",0,0,[_(0)]),Ef=()=>X("vtte"),Tf=(e,t,r,n,s)=>X("vttc",void 0,[s!==null?X("vsid",[Et(s)]):null,r!==null?X("iden",[...Tt.encode(r)]):null,t!==null?X("ctim",[...Tt.encode(kl(t))]):null,n!==null?X("sttg",[...Tt.encode(n)]):null,X("payl",[...Tt.encode(e)])]),Sf=e=>X("vtta",[...Tt.encode(e)]),Cf=e=>{const t=[],r=e.format._options.metadataFormat??"auto",n=e.output._metadataTags;if(r==="mdir"||r==="auto"&&!e.isQuickTime){const s=Mf(n);s&&t.push(s)}else if(r==="mdta"){const s=Nf(n);s&&t.push(s)}else(r==="udta"||r==="auto"&&e.isQuickTime)&&Pf(t,e.output._metadataTags);return t.length===0?null:X("udta",void 0,t)},Pf=(e,t)=>{for(const{key:r,value:n}of $o(t))switch(r){case"title":e.push(yt("©nam",n));break;case"description":e.push(yt("©des",n));break;case"artist":e.push(yt("©ART",n));break;case"album":e.push(yt("©alb",n));break;case"albumArtist":e.push(yt("albr",n));break;case"genre":e.push(yt("©gen",n));break;case"date":e.push(yt("©day",n.toISOString().slice(0,10)));break;case"comment":e.push(yt("©cmt",n));break;case"lyrics":e.push(yt("©lyr",n));break;case"raw":break;case"discNumber":case"discsTotal":case"trackNumber":case"tracksTotal":case"beatsPerMinute":case"images":break;default:Xt(r)}if(t.raw)for(const r in t.raw){const n=t.raw[r];n==null||r.length!==4||e.some(s=>s.type===r)||(typeof n=="string"?e.push(yt(r,n)):n instanceof Uint8Array&&e.push(X(r,Array.from(n))))}},yt=(e,t)=>{const r=Tt.encode(t);return X(e,[Y(r.length),Y(va("und")),Array.from(r)])},Hi={"image/jpeg":13,"image/png":14,"image/bmp":27},ga=(e,t)=>{const r=[];for(const{key:n,value:s}of $o(e))switch(n){case"title":r.push({key:t?"title":"©nam",value:ct(s)});break;case"description":r.push({key:t?"description":"©des",value:ct(s)});break;case"artist":r.push({key:t?"artist":"©ART",value:ct(s)});break;case"album":r.push({key:t?"album":"©alb",value:ct(s)});break;case"albumArtist":r.push({key:t?"album_artist":"aART",value:ct(s)});break;case"comment":r.push({key:t?"comment":"©cmt",value:ct(s)});break;case"genre":r.push({key:t?"genre":"©gen",value:ct(s)});break;case"beatsPerMinute":t||r.push({key:"tmpo",value:X("data",[_(21),_(0),Y(s)])});break;case"lyrics":r.push({key:t?"lyrics":"©lyr",value:ct(s)});break;case"date":r.push({key:t?"date":"©day",value:ct(s.toISOString().slice(0,10))});break;case"images":for(const i of s)i.kind==="coverFront"&&r.push({key:"covr",value:X("data",[_(Hi[i.mimeType]??0),_(0),Array.from(i.data)])});break;case"trackNumber":if(t){const i=e.tracksTotal!==void 0?`${s}/${e.tracksTotal}`:s.toString();r.push({key:"track",value:ct(i)})}else r.push({key:"trkn",value:X("data",[_(0),_(0),Y(0),Y(s),Y(e.tracksTotal??0),Y(0)])});break;case"discNumber":t||r.push({key:"disc",value:X("data",[_(0),_(0),Y(0),Y(s),Y(e.discsTotal??0),Y(0)])});break;case"tracksTotal":case"discsTotal":break;case"raw":break;default:Xt(n)}if(e.raw)for(const n in e.raw){const s=e.raw[n];s==null||!t&&n.length!==4||r.some(i=>i.key===n)||(typeof s=="string"?r.push({key:n,value:ct(s)}):s instanceof Uint8Array?r.push({key:n,value:X("data",[_(0),_(0),Array.from(s)])}):s instanceof Ho&&r.push({key:n,value:X("data",[_(Hi[s.mimeType]??0),_(0),Array.from(s.data)])}))}return r},Mf=e=>{const t=ga(e,!1);return t.length===0?null:ce("meta",0,0,void 0,[Hs(!1,"mdir","","appl"),X("ilst",void 0,t.map(r=>X(r.key,void 0,[r.value])))])},Nf=e=>{const t=ga(e,!0);return t.length===0?null:X("meta",void 0,[Hs(!1,"mdta",""),ce("keys",0,0,[_(t.length)],t.map(r=>X("mdta",[...Tt.encode(r.key)]))),X("ilst",void 0,t.map((r,n)=>{const s=String.fromCharCode(..._(n+1));return X(s,void 0,[r.value])}))])},ct=e=>X("data",[_(1),_(0),...Tt.encode(e)]),Bf=(e,t)=>{switch(e){case"avc":return t.startsWith("avc3")?"avc3":"avc1";case"hevc":return"hvc1";case"vp8":return"vp08";case"vp9":return"vp09";case"av1":return"av01";case"prores":return t}},Yn={avc:Hl,hevc:Gl,vp8:$i,vp9:$i,av1:Xl,prores:null},xa=(e,t,r)=>{switch(e){case"aac":return"mp4a";case"mp3":return"mp4a";case"opus":return"Opus";case"vorbis":return"mp4a";case"flac":return"fLaC";case"ulaw":return"ulaw";case"alaw":return"alaw";case"pcm-u8":return"raw ";case"pcm-s8":return"sowt";case"ac3":return"ac-3";case"eac3":return"ec-3";case"dts":return t}if(r)switch(e){case"pcm-s16":return"sowt";case"pcm-s16be":return"twos";case"pcm-s24":return"in24";case"pcm-s24be":return"in24";case"pcm-s32":return"in32";case"pcm-s32be":return"in32";case"pcm-f32":return"fl32";case"pcm-f32be":return"fl32";case"pcm-f64":return"fl64";case"pcm-f64be":return"fl64"}else switch(e){case"pcm-s16":return"ipcm";case"pcm-s16be":return"ipcm";case"pcm-s24":return"ipcm";case"pcm-s24be":return"ipcm";case"pcm-s32":return"ipcm";case"pcm-s32be":return"ipcm";case"pcm-f32":return"fpcm";case"pcm-f32be":return"fpcm";case"pcm-f64":return"fpcm";case"pcm-f64be":return"fpcm"}},Af=(e,t)=>{switch(e){case"aac":return Kn;case"mp3":return Kn;case"opus":return Zl;case"vorbis":return Kn;case"flac":return Jl;case"ac3":return ef;case"eac3":return tf;case"dts":return rf}if(t)switch(e){case"pcm-s24":return Ft;case"pcm-s24be":return Ft;case"pcm-s32":return Ft;case"pcm-s32be":return Ft;case"pcm-f32":return Ft;case"pcm-f32be":return Ft;case"pcm-f64":return Ft;case"pcm-f64be":return Ft}else switch(e){case"pcm-s16":return wt;case"pcm-s16be":return wt;case"pcm-s24":return wt;case"pcm-s24be":return wt;case"pcm-s32":return wt;case"pcm-s32be":return wt;case"pcm-f32":return wt;case"pcm-f32be":return wt;case"pcm-f64":return wt;case"pcm-f64be":return wt}return null},_f={webvtt:"wvtt"},If={webvtt:sf},va=e=>{C(e.length===3);let t=0;for(let r=0;r<3;r++)t<<=5,t+=e.charCodeAt(r)-96;return t};/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class Ns{constructor(t,r){if(this.finalized=!1,this.started=!1,this.pos=0,this.trackedWrites=null,this.trackedStart=-1,this.trackedEnd=-1,t._writerAcquired)throw new Error("Can't have multiple Writers for the same Target.");this.target=t,t._setMonotonicity(r),t._writerAcquired=!0}start(){C(!this.started),this.target._start(),this.started=!0}write(t){C(this.started&&!this.finalized),this.maybeTrackWrites(t),this.target._write(t,this.pos),this.pos+=t.byteLength}seek(t){this.pos=t}getPos(){return this.pos}async flush(){return C(this.started&&!this.finalized),this.target._flush()}async finalize(){C(this.started&&!this.finalized),await this.target._finalize(),this.finalized=!0}maybeTrackWrites(t){if(!this.trackedWrites)return;let r=this.getPos();if(rJn)throw new Error(`ArrayBuffer exceeded maximum size of ${Jn} bytes. Please consider using another target.`);if(this._supportsResize)this._buffer.resize(r);else{const n=new ArrayBuffer(r),s=new Uint8Array(n);s.set(this._bytes,0),this._buffer=n,this._bytes=s}}}_start(){}_write(t,r){this._ensureSize(r+t.byteLength),this._bytes.set(t,r),this._maxPos=Math.max(this._maxPos,r+t.byteLength),this._dispatchWrite(r,r+t.byteLength)}async _flush(){}async _finalize(){this.buffer=this._buffer.slice(0,this._maxPos),this._options.onFinalize&&await this._options.onFinalize(this.buffer),this._emit("finalized")}async _close(){}_getSlice(t,r){return this._bytes.slice(t,r)}}class Rf extends Nt{constructor(t,r){super(),this._baseTarget=t,this._offset=r}_start(){}_write(t,r){this._baseTarget._write(t,this._offset+r),this._dispatchWrite(r,r+t.byteLength)}_flush(){return this._baseTarget._flush()}async _finalize(){this._emit("finalized")}async _close(){}_setMonotonicity(t){super._setMonotonicity(t),this._baseTarget._setMonotonicity(t)}}class es{constructor(t,r){if(this.rootPath=t,this.getTarget=r,typeof t!="string")throw new TypeError("rootPath must be a string.");if(typeof r!="function")throw new TypeError("getTarget must be a function.")}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Xe=57600,Of=2082844800,Ff=e=>{const t={},r=e.track;return r.metadata.name!==void 0&&(t.name=r.metadata.name),t},pe=(e,t,r=!0)=>{const n=e*t;return r?Math.round(n):n},kr=e=>{if(e.samples.length===0)return 0;let t=1/0,r=-1/0;for(let n=0;nr&&(r=s.timestamp+s.duration)}return t===1/0?0:r-t};class Vf extends vl{constructor(t,r){super(t),this.writer=null,this.boxWriter=null,this.initWriter=null,this.initBoxWriter=null,this.auxTarget=new pn,this.auxWriter=new Ns(this.auxTarget,!1),this.auxBoxWriter=new en(this.auxWriter),this.mdat=null,this.ftypSize=null,this.trackDatas=[],this.allTracksKnown=Wo(),this.creationTime=Math.floor(Date.now()/1e3)+Of,this.finalizedChunks=[],this.wroteFragmentedHeader=!1,this.nextFragmentNumber=1,this.maxWrittenTimestamp=-1/0,this.minWrittenTimestamp=1/0,this.maxWrittenEndTimestamp=-1/0,this.segmentHeaderSize=null,this.format=r,this.formatOptions={...r._options},this.isQuickTime=r instanceof Ca,this.isCmaf=r instanceof Xi,this.minimumFragmentDuration=this.formatOptions.minimumFragmentDuration??(r instanceof Xi?1/0:1),this.auxWriter.start()}async start(){var n;const t=await this.mutex.acquire();if(this.isCmaf?(this.fastStart="fragmented",this.isFragmented=!0):(this.writer=await this.output._getRootWriter(s=>this.formatOptions.fastStart!==void 0?this.formatOptions.fastStart==="fragmented":s instanceof pn),this.boxWriter=new en(this.writer),this.fastStart=this.formatOptions.fastStart??(this.writer.target instanceof pn?"in-memory":!1),this.isFragmented=this.fastStart==="fragmented"),this.isCmaf){if(!this.output._hasInitTarget())throw new Error("CMAF outputs require the initTarget field in OutputOptions to be set; the init segment will be written to it.");const s=await this.output._getInitTarget(),i=new Ns(s,!0);i.start(),this.initWriter=i,this.initBoxWriter=new en(i)}const r=this.output.tracks.some(s=>s.isVideoTrack()&&s.source._codec==="avc");{const s=this.initBoxWriter??this.boxWriter;if(C(s),this.formatOptions.onFtyp&&s.writer.startTrackingWrites(),s.writeBox(El({isQuickTime:this.isQuickTime,holdsAvc:r,fragmented:this.isFragmented,cmaf:this.isCmaf})),this.formatOptions.onFtyp){const{data:i,start:o}=s.writer.stopTrackingWrites();this.formatOptions.onFtyp(i,o)}this.ftypSize=s.writer.getPos(),this.isCmaf&&await this.initWriter.flush()}if(this.fastStart!=="in-memory")if(this.fastStart==="reserve"){for(const s of this.output.tracks)if(s.metadata.maximumPacketCount===void 0)throw new Error("All tracks must specify maximumPacketCount in their metadata when using fastStart: 'reserve'.")}else this.isFragmented||(C(this.writer),C(this.boxWriter),this.formatOptions.onMdat&&this.writer.startTrackingWrites(),this.mdat=tn(!0),this.boxWriter.writeBox(this.mdat));await((n=this.writer)==null?void 0:n.flush());for(const s of this.output.tracks)s.isVideoTrack()&&s.metadata.decoderConfig?this.getVideoTrackData(s,s.metadata.primingPacket??null,{decoderConfig:s.metadata.decoderConfig}):s.isAudioTrack()&&s.metadata.decoderConfig&&this.getAudioTrackData(s,s.metadata.primingPacket??null,{decoderConfig:s.metadata.decoderConfig});t()}allTracksAreKnown(){for(const t of this.output.tracks)if(!t.source._closed&&!this.trackDatas.some(r=>r.track===t))return!1;return!0}async getMimeType(){await this.allTracksKnown.promise;const t=this.trackDatas.map(r=>r.type==="video"||r.type==="audio"?r.info.decoderConfig.codec:{webvtt:"wvtt"}[r.track.source._codec]);return Qc({isQuickTime:this.isQuickTime,hasVideo:this.trackDatas.some(r=>r.type==="video"),hasAudio:this.trackDatas.some(r=>r.type==="audio"),codecStrings:t})}getVideoTrackData(t,r,n){const s=this.trackDatas.find(d=>d.track===t);if(s)return s;ra(n,t.source._codec),C(n),C(n.decoderConfig);const i={...n.decoderConfig};C(i.codedWidth!==void 0),C(i.codedHeight!==void 0);let o=!1;if(t.source._codec==="avc"&&!i.description){if(!r)throw new Error("No AVC description provided; you must therefore provide a priming packet.");const d=Ko(r.data);if(!d)throw new Error("Couldn't extract an AVCDecoderConfigurationRecord from the AVC packet. Make sure the packets are in Annex B format (as specified in ITU-T-REC-H.264) when not providing a description, or provide a description (must be an AVCDecoderConfigurationRecord as specified in ISO 14496-15) and ensure the packets are in AVCC format.");i.description=Yo(d),o=!0}else if(t.source._codec==="hevc"&&!i.description){if(!r)throw new Error("No HEVC description provided; you must therefore provide a priming packet.");const d=Jo(r.data);if(!d)throw new Error("Couldn't extract an HEVCDecoderConfigurationRecord from the HEVC packet. Make sure the packets are in Annex B format (as specified in ITU-T-REC-H.265) when not providing a description, or provide a description (must be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15) and ensure the packets are in HEVC format.");i.description=cc(d),o=!0}const a=_0(1/(t.metadata.frameRate??Xe),1e6).den,l=i.displayAspectWidth,f=i.displayAspectHeight,c=l===void 0||f===void 0?{num:1,den:1}:Uo({num:l*i.codedHeight,den:f*i.codedWidth}),u=i.codec==="ap4h"||i.codec==="ap4x",h={muxer:this,track:t,type:"video",info:{width:i.codedWidth,height:i.codedHeight,pixelAspectRatio:c,decoderConfig:i,requiresAnnexBTransformation:o,hasAlphaChannel:u},timescale:a,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,startTimestampOffset:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],closed:!1,avgBitrate:t.source._nominalBitrate??t.metadata.averageBitrate??0,maxBitrate:t.source._nominalBitrate??t.metadata.bitrate??0};return this.trackDatas.push(h),this.trackDatas.sort((d,m)=>d.track.id-m.track.id),this.allTracksAreKnown()&&this.allTracksKnown.resolve(),h}getAudioTrackData(t,r,n){const s=this.trackDatas.find(l=>l.track===t);if(s)return s;na(n,t.source._codec),C(n),C(n.decoderConfig);const i={...n.decoderConfig};let o=!1;if(t.source._codec==="aac"&&!i.description){if(!r)throw new Error("No AAC description provided; you must therefore provide a priming packet.");const l=Mi(vr.tempFromBytes(r.data));if(!l)throw new Error("Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format (as specified in ISO 13818-7) when not providing a description, or provide a description (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets are raw AAC data.");const f=xn[l.samplingFrequencyIndex],c=qs[l.channelConfiguration];if(f===void 0||c===void 0)throw new Error("Invalid ADTS frame header.");i.description=Go({objectType:l.objectType,outputSampleRate:f,outputNumberOfChannels:c}),o=!0}if(!r){if(t.source._codec==="ac3"||t.source._codec==="eac3")throw new Error("AC-3/E-AC-3 require a priming packet.");if(t.source._codec==="dts")throw new Error("DTS requires a priming packet.")}const a={muxer:this,track:t,type:"audio",info:{numberOfChannels:n.decoderConfig.numberOfChannels,sampleRate:n.decoderConfig.sampleRate,decoderConfig:i,requiresPcmTransformation:!this.isFragmented&&pt.includes(t.source._codec),expectedNextPcmPacketTimestamp:null,requiresAdtsStripping:o,primingPacket:r},timescale:i.sampleRate,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,startTimestampOffset:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],closed:!1,avgBitrate:t.source._nominalBitrate??t.metadata.averageBitrate??0,maxBitrate:t.source._nominalBitrate??t.metadata.bitrate??0};return this.trackDatas.push(a),this.trackDatas.sort((l,f)=>l.track.id-f.track.id),this.allTracksAreKnown()&&this.allTracksKnown.resolve(),a}getSubtitleTrackData(t,r){const n=this.trackDatas.find(i=>i.track===t);if(n)return n;Xc(r),C(r),C(r.config);const s={muxer:this,track:t,type:"subtitle",info:{config:r.config},timescale:1e3,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,startTimestampOffset:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],closed:!1,avgBitrate:t.source._nominalBitrate??t.metadata.averageBitrate??0,maxBitrate:t.source._nominalBitrate??t.metadata.bitrate??0,lastCueEndTimestamp:null,cueQueue:[],nextSourceId:0,cueToSourceId:new WeakMap};return this.trackDatas.push(s),this.trackDatas.sort((i,o)=>i.track.id-o.track.id),this.allTracksAreKnown()&&this.allTracksKnown.resolve(),s}async addEncodedVideoPacket(t,r,n){const s=await this.mutex.acquire();try{const i=this.getVideoTrackData(t,r,n);let o=r.data;if(i.info.requiresAnnexBTransformation){const l=[...Kr(o)].map(f=>o.subarray(f.offset,f.offset+f.length));if(l.length===0)throw new Error("Failed to transform packet data. Make sure all packets are provided in Annex B format, as specified in ITU-T-REC-H.264 and ITU-T-REC-H.265.");o=Y0(l,4)}this.validateTimestamp(i.track,r.timestamp,r.type==="key");const a=this.createSampleForTrack(i,o,r.timestamp,r.duration,r.type);await this.registerSample(i,a)}finally{s()}}async addEncodedAudioPacket(t,r,n){const s=await this.mutex.acquire();try{const i=this.getAudioTrackData(t,r,n);let o=r.data;if(i.info.requiresAdtsStripping){const c=Mi(vr.tempFromBytes(o));if(!c)throw new Error("Expected ADTS frame, didn't get one.");const u=c.crcCheck===null?Yc:Zc;o=o.subarray(u)}this.validateTimestamp(i.track,r.timestamp,r.type==="key");let a=r.timestamp,l=r.duration;if(i.info.requiresPcmTransformation){const u=ir(i.info.decoderConfig.codec).sampleSize*i.info.numberOfChannels;if(l=o.byteLength/u/i.info.sampleRate,i.info.expectedNextPcmPacketTimestamp!==null){const h=a-i.info.expectedNextPcmPacketTimestamp;if(h<.01)a=i.info.expectedNextPcmPacketTimestamp;else{const d=await this.padWithSilence(i,i.info.expectedNextPcmPacketTimestamp,h);a=i.info.expectedNextPcmPacketTimestamp+d}}i.info.expectedNextPcmPacketTimestamp=a+l}const f=this.createSampleForTrack(i,o,a,l,r.type);await this.registerSample(i,f)}finally{s()}}async padWithSilence(t,r,n){const s=pe(n,t.timescale);if(n=s/t.timescale,s>0){const{sampleSize:i,silentValue:o}=ir(t.info.decoderConfig.codec),a=s*t.info.numberOfChannels,l=new Uint8Array(i*a).fill(o),f=this.createSampleForTrack(t,new Uint8Array(l.buffer),r,n,"key");await this.registerSample(t,f)}return n}async addSubtitleCue(t,r,n){const s=await this.mutex.acquire();try{const i=this.getSubtitleTrackData(t,n);this.validateTimestamp(i.track,r.timestamp,!0),t.source._codec==="webvtt"&&(i.cueQueue.push(r),await this.processWebVTTCues(i,r.timestamp))}finally{s()}}async processWebVTTCues(t,r){for(;t.cueQueue.length>0;){t.lastCueEndTimestamp??(t.lastCueEndTimestamp=Math.min(0,t.cueQueue[0].timestamp));const n=new Set([]);for(const f of t.cueQueue)C(f.timestamp<=r),C(t.lastCueEndTimestamp<=f.timestamp+f.duration),n.add(Math.max(f.timestamp,t.lastCueEndTimestamp)),n.add(f.timestamp+f.duration);const s=[...n].sort((f,c)=>f-c),i=s[0],o=s[1]??i;if(r=o)break;ji.lastIndex=0;const u=ji.test(c.text),h=c.timestamp+c.duration;let d=t.cueToSourceId.get(c);if(d===void 0&&os.timestamp).sort((s,i)=>s-i);this.isFragmented?t.startTimestampOffset??(t.startTimestampOffset=Math.min(n[0],0)):t.startTimestampOffset??(t.startTimestampOffset=n[0]);for(let s=0;s=0),t.lastTimescaleUnits+=f,t.lastSample.timescaleUnitsToNextSample=f,!this.isFragmented){let c=ze(t.timeToSampleTable);if(C(c),c.sampleCount===1){c.sampleDelta=f;const h=t.timeToSampleTable[t.timeToSampleTable.length-2];h&&h.sampleDelta===f&&(h.sampleCount++,t.timeToSampleTable.pop(),c=h)}else c.sampleDelta!==f&&(c.sampleCount--,t.timeToSampleTable.push(c={sampleCount:1,sampleDelta:f}));c.sampleDelta===a?c.sampleCount++:t.timeToSampleTable.push({sampleCount:1,sampleDelta:a});const u=ze(t.compositionTimeOffsetTable);C(u),u.sampleCompositionTimeOffset===o?u.sampleCount++:t.compositionTimeOffsetTable.push({sampleCount:1,sampleCompositionTimeOffset:o})}}else t.lastTimescaleUnits=pe(i.decodeTimestamp,t.timescale,!1),this.isFragmented||(t.timeToSampleTable.push({sampleCount:1,sampleDelta:a}),t.compositionTimeOffsetTable.push({sampleCount:1,sampleCompositionTimeOffset:o}));t.lastSample=i}if(t.timestampProcessingQueue.length=0,C(t.lastSample),C(t.lastTimescaleUnits!==null),r!==void 0&&t.lastSample.timescaleUnitsToNextSample===0){C(r.type==="key");const s=pe(r.timestamp,t.timescale,!1),i=Math.round(s-t.lastTimescaleUnits);t.lastSample.timescaleUnitsToNextSample=i}}async registerSample(t,r){r.type==="key"&&this.processTimestamps(t,r),t.timestampProcessingQueue.push(r),this.isFragmented?(t.sampleQueue.push(r),await this.interleaveSamples()):this.fastStart==="reserve"?await this.registerSampleFastStartReserve(t,r):await this.addSampleToTrack(t,r)}async addSampleToTrack(t,r){if(!this.isFragmented&&(t.samples.push(r),this.fastStart==="reserve")){const s=t.track.metadata.maximumPacketCount;if(C(s!==void 0),t.samples.length>s)throw new Error(`Track #${t.track.id} has already reached the maximum packet count (${s}). Either add less packets or increase the maximum packet count.`)}let n=!1;if(!t.currentChunk)n=!0;else{t.currentChunk.startTimestamp=Math.min(t.currentChunk.startTimestamp,r.timestamp);const s=r.timestamp-t.currentChunk.startTimestamp;if(this.isFragmented){const i=this.trackDatas.every(o=>{if(t===o)return r.type==="key";const a=o.sampleQueue[0];return a?a.type==="key":o.closed});s>=this.minimumFragmentDuration&&i&&r.timestamp>this.maxWrittenTimestamp&&(n=!0,await this.finalizeFragment())}else n=s>=.5}n&&(t.currentChunk&&await this.finalizeCurrentChunk(t),t.currentChunk={startTimestamp:r.timestamp,samples:[],offset:null,moofOffset:null,trafIndex:null}),C(t.currentChunk),t.currentChunk.samples.push(r),this.isFragmented&&(this.maxWrittenTimestamp=Math.max(this.maxWrittenTimestamp,r.timestamp),this.maxWrittenEndTimestamp=Math.max(this.maxWrittenEndTimestamp,r.timestamp+r.duration),this.minWrittenTimestamp=Math.min(this.minWrittenTimestamp,r.timestamp))}async finalizeCurrentChunk(t){if(C(!this.isFragmented),C(this.writer),!t.currentChunk)return;t.finalizedChunks.push(t.currentChunk),this.finalizedChunks.push(t.currentChunk);let r=t.currentChunk.samples.length;if(t.type==="audio"&&t.info.requiresPcmTransformation&&(r=t.currentChunk.samples.reduce((n,s)=>n+pe(s.duration,t.timescale),0)),(t.compactlyCodedChunkTable.length===0||ze(t.compactlyCodedChunkTable).samplesPerChunk!==r)&&t.compactlyCodedChunkTable.push({firstChunk:t.finalizedChunks.length,samplesPerChunk:r}),this.fastStart==="in-memory"){t.currentChunk.offset=0;return}t.currentChunk.offset=this.writer.getPos();for(const n of t.currentChunk.samples)C(n.data),this.writer.write(n.data),n.data=null;await this.writer.flush()}async interleaveSamples(t=!1){if(C(this.isFragmented),!(!t&&!this.allTracksAreKnown()))e:for(;;){let r=null,n=1/0;for(const i of this.trackDatas){if(!t&&i.sampleQueue.length===0&&!i.closed)break e;i.sampleQueue.length>0&&i.sampleQueue[0].timestampd.currentChunk);if(r.length===0){t&&await this.writer.flush();return}const n=this.nextFragmentNumber++,s=Ui(n,r),i=this.writer.getPos(),o=i+this.boxWriter.measureBox(s);let a=o+$n,l=1/0;for(let d=0;d=2**32;if(c)for(const d of r)d.currentChunk.offset+=Pi-$n;this.formatOptions.onMoof&&this.writer.startTrackingWrites();const u=Ui(n,r);if(this.boxWriter.writeBox(u),this.formatOptions.onMoof){const{data:d,start:m}=this.writer.stopTrackingWrites();this.formatOptions.onMoof(d,m,l)}C(this.writer.getPos()===o),this.formatOptions.onMdat&&this.writer.startTrackingWrites();const h=tn(c);h.size=f,this.boxWriter.writeBox(h),this.writer.seek(o+(c?Pi:$n));for(const d of r)for(const m of d.currentChunk.samples)this.writer.write(m.data),m.data=null;if(this.formatOptions.onMdat){const{data:d,start:m}=this.writer.stopTrackingWrites();this.formatOptions.onMdat(d,m)}for(const d of r)d.finalizedChunks.push(d.currentChunk),this.finalizedChunks.push(d.currentChunk),d.currentChunk=null;t&&await this.writer.flush()}async registerSampleFastStartReserve(t,r){this.allTracksAreKnown()?(this.mdat||await this.createFastStartReserveMdat(),await this.addSampleToTrack(t,r)):t.sampleQueue.push(r)}async createFastStartReserveMdat(){C(this.writer),C(this.boxWriter),this.ensureOneEnabledTrack();const t=_r(this),n=this.boxWriter.measureBox(t)+this.computeSampleTableSizeUpperBound()+4096;C(this.ftypSize!==null),this.writer.seek(this.ftypSize+n),this.formatOptions.onMdat&&this.writer.startTrackingWrites(),this.mdat=tn(!0),this.boxWriter.writeBox(this.mdat);for(const s of this.trackDatas){for(const i of s.sampleQueue)await this.addSampleToTrack(s,i);s.sampleQueue.length=0}}computeSampleTableSizeUpperBound(){C(this.fastStart==="reserve");let t=0;for(const r of this.trackDatas){const n=r.track.metadata.maximumPacketCount;C(n!==void 0),t+=8*Math.ceil(2/3*n),t+=4*n,t+=8*Math.ceil(2/3*n),t+=12*Math.ceil(2/3*n),t+=4*n,t+=8*n}return t}async onTrackClose(t){const r=await this.mutex.acquire(),n=this.trackDatas.find(s=>s.track===t);n&&(n.closed=!0,n.type==="subtitle"&&t.source._codec==="webvtt"&&await this.processWebVTTCues(n,1/0),this.processTimestamps(n)),this.allTracksAreKnown()&&this.allTracksKnown.resolve(),this.isFragmented&&await this.interleaveSamples(),r()}ensureOneEnabledTrack(){for(const t of["video","audio","subtitle"]){const r=this.trackDatas.filter(s=>s.type===t);if(r.length===0)continue;if(!r.some(s=>{var i;return((i=s.track.metadata.disposition)==null?void 0:i.default)!==!1})){const s=r[0];s.track.metadata.disposition={...s.track.metadata.disposition,default:!0}}}}async forceFragmentFinalization(){C(this.isFragmented);const t=await this.mutex.acquire();try{for(const r of this.trackDatas)r.type==="subtitle"&&r.track.source._codec==="webvtt"&&await this.processWebVTTCues(r,1/0),this.processTimestamps(r);await this.interleaveSamples(!0),await this.finalizeFragment()}finally{t()}}async finalize(){const t=await this.mutex.acquire();this.allTracksKnown.resolve(),this.ensureOneEnabledTrack(),!this.mdat&&this.fastStart==="reserve"&&await this.createFastStartReserveMdat();for(const r of this.trackDatas)r.closed=!0,r.type==="subtitle"&&r.track.source._codec==="webvtt"&&await this.processWebVTTCues(r,1/0),this.processTimestamps(r);if(this.isFragmented)await this.interleaveSamples(!0),await this.finalizeFragment(!1);else for(const r of this.trackDatas){await this.finalizeCurrentChunk(r);const n=kr(r);if(n>0){let a=0;for(const l of r.samples)a+=l.size;r.avgBitrate=Math.round(8*a/n)}else r.avgBitrate=0;let s=0,i=0,o=0;for(let a=0;a=1;)i-=r.samples[s].size,s++;o=Math.max(o,i)}if(r.maxBitrate=8*o,r.startTimestampOffset!==null)for(let a=0;a=2**32&&(this.mdat.largeSize=!0)}this.formatOptions.onMoov&&this.writer.startTrackingWrites();const n=_r(this);if(this.boxWriter.writeBox(n),this.formatOptions.onMoov){const{data:s,start:i}=this.writer.stopTrackingWrites();this.formatOptions.onMoov(s,i)}this.formatOptions.onMdat&&this.writer.startTrackingWrites(),this.mdat.size=r,this.boxWriter.writeBox(this.mdat);for(const s of this.finalizedChunks)for(const i of s.samples)C(i.data),this.writer.write(i.data),i.data=null;if(this.formatOptions.onMdat){const{data:s,start:i}=this.writer.stopTrackingWrites();this.formatOptions.onMdat(s,i)}}else if(this.isFragmented)if(this.isCmaf){const r=this.segmentHeaderSize!==null?this.writer.getPos()-this.segmentHeaderSize:0;this.writer.seek(0),this.boxWriter.writeBox(Li()),this.boxWriter.writeBox(qi(this,r))}else{const r=this.writer.getPos(),n=xf(this.trackDatas);this.boxWriter.writeBox(n);const s=this.writer.getPos()-r;this.writer.seek(this.writer.getPos()-4),this.boxWriter.writeU32(s)}else{C(this.mdat);const r=this.boxWriter.offsets.get(this.mdat);C(r!==void 0);const n=this.writer.getPos()-r;if(this.mdat.size=n,this.mdat.largeSize=n>=2**32,this.boxWriter.patchBox(this.mdat),this.formatOptions.onMdat){const{data:i,start:o}=this.writer.stopTrackingWrites();this.formatOptions.onMdat(i,o)}const s=_r(this);if(this.fastStart==="reserve"){C(this.ftypSize!==null),this.writer.seek(this.ftypSize),this.formatOptions.onMoov&&this.writer.startTrackingWrites(),this.boxWriter.writeBox(s);const i=this.boxWriter.offsets.get(this.mdat)-this.writer.getPos();this.boxWriter.writeBox(Tl(i))}else this.formatOptions.onMoov&&this.writer.startTrackingWrites(),this.boxWriter.writeBox(s);if(this.formatOptions.onMoov){const{data:i,start:o}=this.writer.stopTrackingWrites();this.formatOptions.onMoov(i,o)}}t()}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class Df{constructor(t){this.sourceSampleRate=null,this.sourceNumberOfChannels=null,this.startTime=null,this.bufferStartFrame=0,this.maxWrittenFrame=null,this.targetSampleRate=t.targetSampleRate,this.targetNumberOfChannels=t.targetNumberOfChannels,this.onSample=t.onSample,this.bufferSizeInFrames=Math.floor(this.targetSampleRate*5),this.bufferSizeInSamples=this.bufferSizeInFrames*this.targetNumberOfChannels}doChannelMixerSetup(){C(this.sourceNumberOfChannels!==null);const t=this.sourceNumberOfChannels,r=this.targetNumberOfChannels;t===1&&r===2?this.channelMixer=(n,s)=>n[s*t]:t===1&&r===4?this.channelMixer=(n,s,i)=>n[s*t]*+(i<2):t===1&&r===6?this.channelMixer=(n,s,i)=>n[s*t]*+(i===2):t===2&&r===1?this.channelMixer=(n,s)=>{const i=s*t;return .5*(n[i]+n[i+1])}:t===2&&r===4?this.channelMixer=(n,s,i)=>i<2?n[s*t+i]:0:t===2&&r===6?this.channelMixer=(n,s,i)=>i<2?n[s*t+i]:0:t===4&&r===1?this.channelMixer=(n,s)=>{const i=s*t;return .25*(n[i]+n[i+1]+n[i+2]+n[i+3])}:t===4&&r===2?this.channelMixer=(n,s,i)=>{const o=s*t;return .5*(n[o+i]+n[o+i+2])}:t===4&&r===6?this.channelMixer=(n,s,i)=>{const o=s*t;return i<2?n[o+i]:i===2||i===3?0:n[o+i-2]}:t===6&&r===1?this.channelMixer=(n,s)=>{const i=s*t;return Math.SQRT1_2*(n[i]+n[i+1])+n[i+2]+.5*(n[i+4]+n[i+5])}:t===6&&r===2?this.channelMixer=(n,s,i)=>{const o=s*t;return n[o+i]+Math.SQRT1_2*(n[o+2]+n[o+i+4])}:t===6&&r===4?this.channelMixer=(n,s,i)=>{const o=s*t;return i<2?n[o+i]+Math.SQRT1_2*n[o+2]:n[o+i+2]}:this.channelMixer=(n,s,i)=>i=this.bufferStartFrame+this.bufferCapacityInFrames;)await this.finalizeCurrentBuffer(),this.bufferStartFrame+=this.bufferSizeInFrames;const c=f-this.bufferStartFrame;C(c=0&&m=0&&b=this.bufferSizeInFrames?this.maxWrittenFrame-this.bufferSizeInFrames:null}async finalize(){for(;this.maxWrittenFrame!==null;)await this.finalizeCurrentBuffer(),this.bufferStartFrame+=this.bufferSizeInFrames}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */var zf=function(e,t,r){if(t!=null){if(typeof t!="object"&&typeof t!="function")throw new TypeError("Object expected.");var n,s;if(r){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");n=t[Symbol.asyncDispose]}if(n===void 0){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");n=t[Symbol.dispose],r&&(s=n)}if(typeof n!="function")throw new TypeError("Object not disposable.");s&&(n=function(){try{s.call(this)}catch(i){return Promise.reject(i)}}),e.stack.push({value:t,dispose:n,async:r})}else r&&e.stack.push({async:!0});return t},jf=(function(e){return function(t){function r(o){t.error=t.hasError?new e(o,t.error,"An error was suppressed during disposal."):o,t.hasError=!0}var n,s=0;function i(){for(;n=t.stack.pop();)try{if(!n.async&&s===1)return s=0,t.stack.push(n),Promise.resolve().then(i);if(n.dispose){var o=n.dispose.call(n.value);if(n.async)return s|=2,Promise.resolve(o).then(i,function(a){return r(a),i()})}else s|=1}catch(a){r(a)}if(s===1)return t.hasError?Promise.reject(t.error):Promise.resolve();if(t.hasError)throw t.error}return i()}})(typeof SuppressedError=="function"?SuppressedError:function(e,t,r){var n=new Error(r);return n.name="SuppressedError",n.error=e,n.suppressed=t,n});class Xs{constructor(){this._connectedTrack=null,this._closingPromise=null,this._closed=!1,this._nominalBitrate=null}_ensureValidAdd(){if(!this._connectedTrack)throw new Error("Source is not connected to an output track.");if(this._connectedTrack.output.state==="canceled")throw new Error("Output has been canceled.");if(this._connectedTrack.output.state==="finalizing"||this._connectedTrack.output.state==="finalized")throw new Error("Output has been finalized.");if(this._connectedTrack.output.state==="pending")throw new Error("Output has not started.");if(this._closed)throw new Error("Source is closed.")}async _start(){}async _flushAndClose(t){}close(){if(this._closingPromise)return;const t=this._connectedTrack;if(!t)throw new Error("Cannot call close without connecting the source to an output track.");if(t.output.state==="pending")throw new Error("Cannot call close before output has been started.");this._closingPromise=(async()=>{await this._flushAndClose(!1),this._closed=!0,!(t.output.state==="finalizing"||t.output.state==="finalized")&&t.output._muxer.onTrackClose(t)})()}async _flushOrWaitForOngoingClose(t){return this._closingPromise??(this._closingPromise=(async()=>{await this._flushAndClose(t),this._closed=!0})())}}class ka extends Xs{constructor(t){if(super(),!Ot.includes(t))throw new TypeError(`Invalid video codec '${t}'. Must be one of: ${Ot.join(", ")}.`);this._codec=t}}const Gi=(e,t)=>{if(e.metadata.hasOnlyKeyPackets&&t.type!=="key")throw new Error("Cannot add non-key packets to a hasOnlyKeyPackets video track.")};class Wf{setError(t){this.errorSet||(this.error=t,this.errorSet=!0)}constructor(t,r){this.source=t,this.encodingConfig=r,this.ensureEncoderPromise=null,this.encoderInitialized=!1,this.encoder=null,this.encoderConfig=null,this.encodersReclaimed=!1,this.muxer=null,this.lastMultipleOfKeyFrameInterval=-1,this.emittedEncoderPackets=0,this.chunkTimestampsAreShiftedByDuration=!1,this.codedWidth=null,this.codedHeight=null,this.outputWidth=null,this.outputHeight=null,this.frameRateLastSample=null,this.frameRateLastTimestamp=null,this.frameRateLastEndTimestamp=null,this.preciseTimings=[],this.customEncoder=null,this.customEncoderCallSerializer=new Lo,this.customEncoderQueueSize=0,this.defaultEncodeOptions={},this.alphaEncoder=null,this.splitter=null,this.splitterCreationFailed=!1,this.alphaFrameQueue=[],this.error=null,this.errorSet=!1,this.lastMuxerPromise=Promise.resolve(),this.closed=!1}async add(t,r,n){var i,o,a,l,f,c,u,h,d,m,b,p,w,y;const s=t;try{this.checkForEncoderError(),this.source._ensureValidAdd();const g=this.encodingConfig,k=g.sizeChangeBehavior??"deny";let T=!1;if(this.codedWidth!==null&&this.codedHeight!==null){if((t.codedWidth!==this.codedWidth||t.codedHeight!==this.codedHeight)&&(T=!0,k==="deny"))throw new Error(`Video sample size must remain constant. Expected ${this.codedWidth}x${this.codedHeight}, got ${t.codedWidth}x${t.codedHeight}. To allow the sample size to change over time, set \`sizeChangeBehavior\` to a value other than 'deny' in the encoding options.`)}else this.codedWidth=t.codedWidth,this.codedHeight=t.codedHeight;if(((i=g.transform)==null?void 0:i.width)!==void 0||((o=g.transform)==null?void 0:o.height)!==void 0||((a=g.transform)==null?void 0:a.rotate)!==void 0||((l=g.transform)==null?void 0:l.flip)!==void 0||((f=g.transform)==null?void 0:f.crop)!==void 0||((c=g.transform)==null?void 0:c.force)===!0||T&&k!=="passThrough"){let B=(u=g.transform)==null?void 0:u.width,V=(h=g.transform)==null?void 0:h.height,W=((d=g.transform)==null?void 0:d.fit)??"fill";T&&k!=="passThrough"&&(C(this.outputWidth),C(this.outputHeight),C(k!=="deny"),B=this.outputWidth,V=this.outputHeight,W=k);const L=await t.transform({width:B,height:V,roundDimensionsTo:2,crop:(m=g.transform)==null?void 0:m.crop,rotate:(b=g.transform)==null?void 0:b.rotate,flip:(p=g.transform)==null?void 0:p.flip,fit:W,alpha:g.alpha});(this.outputWidth===null||this.outputHeight===null)&&(this.outputWidth=L.displayWidth,this.outputHeight=L.displayHeight),r&&t.close(),t=L,r=!0}else(this.outputWidth===null||this.outputHeight===null)&&(this.outputWidth=t.codedWidth,this.outputHeight=t.codedHeight);const S=(w=g.transform)==null?void 0:w.frameRate;if(S!==void 0){const B=t.timestamp+t.duration,V=di(t.timestamp,S);if(this.frameRateLastSample!==null)if(V<=this.frameRateLastTimestamp){this.frameRateLastSample.close(),this.frameRateLastSample=t.clone(),this.frameRateLastEndTimestamp=B;return}else await this.padFrameRate(V,n);t===s&&(t=t.clone(),r=!0),t.setTimestamp(V),t.setDuration(1/S),(y=this.frameRateLastSample)==null||y.close(),this.frameRateLastSample=t.clone(),this.frameRateLastTimestamp=V,this.frameRateLastEndTimestamp=B}await this.processAndEncode(t,n)}finally{r&&t.close()}}async processAndEncode(t,r){var i,o,a;const n=this.encodingConfig;let s;if((i=n.transform)!=null&&i.process){let l=n.transform.process(t);if(Gt(l)&&(l=await l),l===null)return;Array.isArray(l)||(l=[l]);const f=[];try{for(const c of l)c instanceof $e?f.push(c):typeof VideoFrame<"u"&&c instanceof VideoFrame?f.push(new $e(c)):f.push(new $e(c,{timestamp:t.timestamp,duration:t.duration}))}catch(c){for(const u of f)u!==t&&u.close();for(const u of l)(u instanceof $e&&u!==t||typeof VideoFrame<"u"&&u instanceof VideoFrame)&&u.close();throw c}s=f}else s=[t];try{for(const l of s){if(this.encoderInitialized||(this.ensureEncoderPromise||this.ensureEncoder(l),this.encoderInitialized||await this.ensureEncoderPromise),C(this.encoderInitialized),this.closed)break;this.encodersReclaimed&&this.recreateWebCodecsEncoders();const f=this.encodingConfig.keyFrameInterval??2,c=Math.floor(l.timestamp/f),u={...this.defaultEncodeOptions,...l.encodeOptions,...r},h={...u,keyFrame:u.keyFrame!==void 0?u.keyFrame:f===0||c!==this.lastMultipleOfKeyFrameInterval};if(this.lastMultipleOfKeyFrameInterval=c,(a=(o=this.encodingConfig).onEncodedSample)==null||a.call(o,l),this.customEncoder){this.customEncoderQueueSize++;const d=l.clone(),m=this.customEncoderCallSerializer.call(()=>this.customEncoder.encode(d,h)).catch(b=>this.setError(b)).finally(()=>{this.customEncoderQueueSize--,d.close()});this.customEncoderQueueSize>=4&&await m}else{C(this.encoder);const d=l.toVideoFrame(),m=li(this.preciseTimings,d.timestamp,p=>p.microsecondTimestamp),b=m!==-1?this.preciseTimings[m]:null;if(b&&b.microsecondTimestamp===d.timestamp?(b.timestamp!==l.timestamp&&(b.timestampIsValid=!1),b.duration!==l.duration&&(b.durationIsValid=!1)):(this.preciseTimings.splice(m+1,0,{microsecondTimestamp:d.timestamp,timestamp:l.timestamp,duration:l.duration,timestampIsValid:!0,durationIsValid:!0}),this.preciseTimings.length>128&&this.preciseTimings.shift()),this.alphaEncoder)if(!!d.format&&!d.format.includes("A")||this.splitterCreationFailed){this.alphaFrameQueue.push(null);try{this.encoder.encode(d,h)}finally{d.close()}}else{this.splitter||(this.splitter=new $f);const{colorFrame:w,alphaFrame:y}=await this.splitter.split(d);this.alphaFrameQueue.push(y);try{this.encoder.encode(w,h)}finally{w.close()}}else try{this.encoder.encode(d,h)}finally{d.close()}this.encoder.encodeQueueSize>=4&&await new Promise(p=>this.encoder.addEventListener("dequeue",p,{once:!0}))}await this.lastMuxerPromise}}finally{for(const l of s)l!==t&&l.close()}}async padFrameRate(t,r){const n=this.encodingConfig.transform.frameRate;C(this.frameRateLastSample);const s=Math.round((t-this.frameRateLastTimestamp)*n);for(let i=1;i{var a,l,f;const r=Nn(this.encodingConfig.quality,this.encodingConfig.bitrate);C(r!==void 0);const n=aa({...this.encodingConfig,quality:r,width:t.codedWidth,height:t.codedHeight,squarePixelWidth:t.squarePixelWidth,squarePixelHeight:t.squarePixelHeight,framerate:(a=this.source._connectedTrack)==null?void 0:a.metadata.frameRate});let s=null,i;for(const c of n){const u=c.config;if((f=(l=this.encodingConfig).onEncoderConfig)==null||f.call(l,u),i=ua.find(d=>d.supports(this.encodingConfig.codec,u)),i){s=c;break}if(typeof VideoEncoder>"u")continue;if(u.alpha="discard",this.encodingConfig.alpha==="keep"&&(u.latencyMode="quality"),(u.width%2===1||u.height%2===1)&&(this.encodingConfig.codec==="avc"||this.encodingConfig.codec==="hevc"))throw new Error(`The dimensions ${u.width}x${u.height} are not supported for codec '${this.encodingConfig.codec}'; both width and height must be even numbers. Make sure to round your dimensions to the nearest even number.`);try{if((await VideoEncoder.isConfigSupported(u)).supported){s=c;break}}catch{}}if(!s){if(typeof VideoEncoder>"u")throw new Error(qo("VideoEncoder"));const c=n[0].config,u=n.map(({config:h,quantizer:d})=>d!==null?`quantizer ${d}`:`${h.bitrate} bps`);throw new Error(`This specific encoder configuration (${c.codec}, ${u.join(" / ")}, ${c.width}x${c.height}, hardware acceleration: ${c.hardwareAcceleration??"no-preference"}) is not supported in this environment. Consider using another codec or changing your video parameters.`)}const o=s.config;s.quantizer!==null?this.defaultEncodeOptions=fa(this.encodingConfig.codec,s.quantizer):this.source._nominalBitrate=o.bitrate??null,i?(this.customEncoder=new i,this.customEncoder.codec=this.encodingConfig.codec,this.customEncoder.config=o,this.customEncoder.onPacket=(c,u)=>{var h,d;if(!(c instanceof St))throw new TypeError("The first argument passed to onPacket must be an EncodedPacket.");if(u!==void 0&&(!u||typeof u!="object"))throw new TypeError("The second argument passed to onPacket must be an object or undefined.");Gi(this.source._connectedTrack,c),(d=(h=this.encodingConfig).onEncodedPacket)==null||d.call(h,c,u),this.lastMuxerPromise=this.muxer.addEncodedVideoPacket(this.source._connectedTrack,c,u).catch(m=>{this.setError(m)})},this.customEncoder.onError=c=>{this.setError(c)},await this.customEncoder.init()):(this.encoderConfig=o,this.encodingConfig.codec==="avc"&&gn()&&(this.chunkTimestampsAreShiftedByDuration=await qf()),this.createWebCodecsEncoders()),C(this.source._connectedTrack),this.muxer=this.source._connectedTrack.output._muxer,this.encoderInitialized=!0})()}createWebCodecsEncoders(){const t=this.encoderConfig;C(t);const r=[],n=[];let s=0,i=0;const o=(l,f)=>{if(F0(l)){this.encodersReclaimed=!0;return}l.stack=f,this.setError(l)},a=new Error("Encoding error").stack;if(this.encoder=new VideoEncoder({output:(l,f)=>{if(!this.alphaEncoder){this.addPacket(l,null,f);return}const c=this.alphaFrameQueue.shift();C(c!==void 0),c?(this.alphaEncoder.encode(c,{...this.defaultEncodeOptions,keyFrame:l.type==="key"}),i++,c.close(),r.push({chunk:l,meta:f})):i===0?this.addPacket(l,null,f):(n.push(s+i),r.push({chunk:l,meta:f}))},error:l=>o(l,a)}),this.encoder.configure(t),this.encodingConfig.alpha==="keep"){const l=new Error("Encoding error").stack;this.alphaEncoder=new VideoEncoder({output:(f,c)=>{i--;const u=r.shift();for(C(u!==void 0),this.addPacket(u.chunk,f,u.meta),s++;n.length>0&&n[0]===s;){n.shift();const h=r.shift();C(h!==void 0),this.addPacket(h.chunk,null,h.meta)}},error:f=>o(f,l)}),this.alphaEncoder.configure(t)}}addPacket(t,r,n){var u,h;const s={};if(r){const d=new Uint8Array(r.byteLength);r.copyTo(d),s.alpha=d}let i=St.fromEncodedChunk(t,s);const o=this.encoderConfig;if(C(o),n!=null&&n.decoderConfig&&(n.decoderConfig.codec=Dc({width:o.width,height:o.height,codec:this.encodingConfig.codec,codecDescription:n.decoderConfig.description?Ie(n.decoderConfig.description):null,colorSpace:n.decoderConfig.colorSpace??null,avcType:o.codec.includes("avc1.")?1:o.codec.includes("avc3.")?3:null,avcCodecInfo:this.encodingConfig.codec==="avc"&&!n.decoderConfig.description?Ko(i.data):null,hevcCodecInfo:this.encodingConfig.codec==="hevc"&&!n.decoderConfig.description?Jo(i.data):null,vp9CodecInfo:this.encodingConfig.codec==="vp9"?fc(i.data):null,av1CodecInfo:this.encodingConfig.codec==="av1"?uc(i.data):null,proresFormat:null}),this.encodingConfig.codec==="avc"&&n.decoderConfig.description&&gn())){const d=Z0(Ie(n.decoderConfig.description));let m=d==null?void 0:d.sequenceParameterSets[0];if(m&&m[0]===m[1]){C(d),m=m.subarray(1);let b=d.pictureParameterSets[0];b&&(b=b.subarray(1));const p=Yo({...d,sequenceParameterSets:[m],pictureParameterSets:b?[b]:[]});n.decoderConfig.description=p}}let a=t.timestamp;this.chunkTimestampsAreShiftedByDuration&&(a-=t.duration??0,i=i.clone({timestamp:a/1e6}));const l=li(this.preciseTimings,a,d=>d.microsecondTimestamp),f=l!==-1?this.preciseTimings[l]:null;let c=null;this.emittedEncoderPackets===0&&i.type==="delta"&&(n!=null&&n.decoderConfig)&&(c=mc(this.encodingConfig.codec,n.decoderConfig,i.data)),(f&&f.microsecondTimestamp===a||c!==null)&&(i=i.clone({timestamp:f!=null&&f.timestampIsValid?f.timestamp:void 0,duration:f!=null&&f.durationIsValid?f.duration:void 0,type:c??void 0})),Gi(this.source._connectedTrack,i),(h=(u=this.encodingConfig).onEncodedPacket)==null||h.call(u,i,n),this.lastMuxerPromise=this.muxer.addEncodedVideoPacket(this.source._connectedTrack,i,n).catch(d=>{this.setError(d)}),this.emittedEncoderPackets++}recreateWebCodecsEncoders(){C(this.encoder),this.encoder.state!=="closed"&&this.encoder.close(),this.alphaEncoder&&this.alphaEncoder.state!=="closed"&&this.alphaEncoder.close(),this.alphaFrameQueue.forEach(t=>t==null?void 0:t.close()),this.alphaFrameQueue.length=0,this.encodersReclaimed=!1,this.lastMultipleOfKeyFrameInterval=-1,this.createWebCodecsEncoders()}async flushAndClose(t){var r,n,s;try{if(!t&&(this.checkForEncoderError(),this.frameRateLastSample)){const i=this.encodingConfig.transform.frameRate,o=di(this.frameRateLastEndTimestamp,i);await this.padFrameRate(o)}this.closed=!0,t||(this.customEncoder?this.customEncoderCallSerializer.call(()=>this.customEncoder.flush()):this.encoder&&!this.encodersReclaimed&&(await this.encoder.flush(),await((r=this.alphaEncoder)==null?void 0:r.flush()),await W0(25)))}finally{this.closed=!0,(n=this.frameRateLastSample)==null||n.close(),this.frameRateLastSample=null,this.customEncoder?await this.customEncoderCallSerializer.call(()=>this.customEncoder.close()).catch(i=>this.setError(i)):this.encoder&&(this.encoder.state!=="closed"&&this.encoder.close(),this.alphaEncoder&&this.alphaEncoder.state!=="closed"&&this.alphaEncoder.close(),this.alphaFrameQueue.forEach(i=>i==null?void 0:i.close()),this.alphaFrameQueue.length=0,(s=this.splitter)==null||s.close())}t||this.checkForEncoderError()}getQueueSize(){var t;return this.customEncoder?this.customEncoderQueueSize:((t=this.encoder)==null?void 0:t.encodeQueueSize)??0}checkForEncoderError(){if(this.errorSet)throw this.error}}let Lf=null;const qf=()=>Lf??(Lf=(async()=>{let e=null;const t=new VideoEncoder({output:r=>{e??(e=r.timestamp)},error:()=>{}});try{t.configure({codec:"avc1.64001f",width:1280,height:720});const r=new VideoFrame(new Uint8Array(1280*720*1.5),{format:"I420",codedWidth:1280,codedHeight:720,timestamp:0,duration:500});t.encode(r,{keyFrame:!0}),r.close(),await t.flush()}catch{return!1}finally{t.state!=="closed"&&t.close()}return e===500})());let ts=null;class $f{constructor(){this.worker=null,this.pendingRequests=new Map,this.nextRequestId=0}split(t){if(!this.worker){if(!ts){const s=new Blob([`(${Uf.toString()})()`],{type:"application/javascript"});ts=URL.createObjectURL(s)}this.worker=new Worker(ts),this.worker.addEventListener("message",s=>{const i=s.data,o=this.pendingRequests.get(i.id);o&&(this.pendingRequests.delete(i.id),"error"in i?o.reject(new Error(i.error)):o.resolve({colorFrame:i.colorFrame,alphaFrame:i.alphaFrame}))}),this.worker.addEventListener("error",s=>{const i=new Error(s.message||"Color/alpha splitter worker error.");for(const o of this.pendingRequests.values())o.reject(i);this.pendingRequests.clear()})}const r=this.nextRequestId++,n=Wo();return this.pendingRequests.set(r,n),this.worker.postMessage({id:r,sourceFrame:t},{transfer:[t]}),n.promise}close(){var r;(r=this.worker)==null||r.terminate(),this.worker=null;const t=new Error("Color/alpha splitter closed.");for(const n of this.pendingRequests.values())n.reject(t);this.pendingRequests.clear()}}const Uf=()=>{let e=null,t=Promise.resolve();self.addEventListener("message",i=>{const{id:o,sourceFrame:a}=i.data;t=t.then(async()=>{try{const{colorFrame:l,alphaFrame:f}=await r(a);self.postMessage({id:o,colorFrame:l,alphaFrame:f},{transfer:[l,f]})}catch(l){self.postMessage({id:o,error:l.message})}finally{a.close()}})});const r=async i=>{const o=i.format;if(!o)throw new Error("CPU color/alpha splitting requires a known VideoFrame format.");const a=i.allocationSize();if((!e||e.byteLength!==a)&&(e=new Uint8Array(a)),await i.copyTo(e),o==="RGBA"||o==="BGRA")return n(e,o,i);if(o==="I420A"||o==="I420AP10"||o==="I420AP12"||o==="I422A"||o==="I422AP10"||o==="I422AP12"||o==="I444A"||o==="I444AP10"||o==="I444AP12")return s(e,o,i);throw new Error(`CPU color/alpha splitting does not support format '${o}'.`)},n=(i,o,a)=>{var y,g;const l=((y=a.visibleRect)==null?void 0:y.width)??a.codedWidth,f=((g=a.visibleRect)==null?void 0:g.height)??a.codedHeight,c=l*f,u=Math.ceil(l/2),h=Math.ceil(f/2),d=c+u*h*2,m=new Uint8Array(d);for(let k=0,T=3;k{var ie,O;const l=((ie=a.visibleRect)==null?void 0:ie.width)??a.codedWidth,f=((O=a.visibleRect)==null?void 0:O.height)??a.codedHeight,c=o.includes("P10"),u=o.includes("P12"),h=c||u?2:1;let d,m;o.startsWith("I420")?(d=Math.ceil(l/2),m=Math.ceil(f/2)):o.startsWith("I422")?(d=Math.ceil(l/2),m=f):(d=l,m=f);const b=l*f,p=d*m,w=b*h,y=p*h,g=b*h,k=w+y*2,T=o.replace("A",""),A=Math.ceil(l/2),N=Math.ceil(f/2),S=A*N,B=S*h,V=g+2*B,W=new Uint8Array(V),L=k;W.set(i.subarray(L,L+g),0);const J=g,U=c?512:u?2048:128;h===1?W.fill(U,J):new Uint16Array(W.buffer,J,2*S).fill(U);const I=c?"I420P10":u?"I420P12":"I420",j=new VideoFrame(i.subarray(0,k),{format:T,codedWidth:l,codedHeight:f,timestamp:a.timestamp,duration:a.duration??void 0}),Z={format:I,codedWidth:l,codedHeight:f,timestamp:a.timestamp,duration:a.duration??void 0,colorSpace:{fullRange:!0,matrix:"bt709",primaries:"bt709",transfer:"bt709"},transfer:[W.buffer]},re=new VideoFrame(W,Z);return{colorFrame:j,alphaFrame:re}}};class Hf extends ka{constructor(t,r){if(!(typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement)&&!(typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas))throw new TypeError("canvas must be an HTMLCanvasElement or OffscreenCanvas.");ul(r),super(r.codec),this._encoder=new Wf(this,r),this._canvas=t}add(t,r=0,n){if(!Number.isFinite(t))throw new TypeError("timestamp must be a finite number.");if(!Number.isFinite(r)||r<0)throw new TypeError("duration must be a non-negative number.");const s=new $e(this._canvas,{timestamp:t,duration:r});return this._encoder.add(s,!0,n)}_flushAndClose(t){return this._encoder.flushAndClose(t)}}class Ea extends Xs{constructor(t){if(super(),!sr.includes(t))throw new TypeError(`Invalid audio codec '${t}'. Must be one of: ${sr.join(", ")}.`);this._codec=t}}class Gf{setError(t){this.errorSet||(this.error=t,this.errorSet=!0)}constructor(t,r){this.source=t,this.encodingConfig=r,this.ensureEncoderPromise=null,this.encoderInitialized=!1,this.encoder=null,this.muxer=null,this.lastNumberOfChannels=null,this.lastSampleRate=null,this.isPcmEncoder=!1,this.outputSampleSize=null,this.writeOutputValue=null,this.customEncoder=null,this.customEncoderCallSerializer=new Lo,this.customEncoderQueueSize=0,this.lastEndSampleIndex=null,this.resampler=null,this.error=null,this.errorSet=!1,this.lastMuxerPromise=Promise.resolve(),this.closed=!1}async add(t,r){var n,s;try{if(this.checkForEncoderError(),this.source._ensureValidAdd(),this.lastNumberOfChannels!==null&&this.lastSampleRate!==null){if(t.numberOfChannels!==this.lastNumberOfChannels||t.sampleRate!==this.lastSampleRate)throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${t.numberOfChannels} channels at ${t.sampleRate} Hz.`)}else this.lastNumberOfChannels=t.numberOfChannels,this.lastSampleRate=t.sampleRate;const i=this.encodingConfig;((n=i.transform)==null?void 0:n.numberOfChannels)!==void 0||((s=i.transform)==null?void 0:s.sampleRate)!==void 0?(this.resampler||(this.resampler=new Df({targetNumberOfChannels:i.transform.numberOfChannels??t.numberOfChannels,targetSampleRate:i.transform.sampleRate??t.sampleRate,onSample:async a=>{await this.processAndEncode(a,!0)}})),await this.resampler.add(t)):await this.processAndEncode(t,r)}finally{r&&t.close()}}async processAndEncode(t,r){var s,i;const n=this.encodingConfig;if(((s=n.transform)==null?void 0:s.sampleFormat)!==void 0&&cl(t.format)!==n.transform.sampleFormat){const o=fl(t,n.transform.sampleFormat);r&&t.close(),t=o,r=!0}if((i=n.transform)!=null&&i.process)try{let o=n.transform.process(t);if(Gt(o)&&(o=await o),o===null)return;Array.isArray(o)||(o=[o]);try{for(const a of o)if(!(a instanceof He))throw new TypeError("The audio process function must return an AudioSample, null, or an array of AudioSamples.");for(const a of o)await this.encodeSample(a,!0)}finally{for(const a of o)a instanceof He&&a.close()}}finally{r&&t.close()}else await this.encodeSample(t,r)}async encodeSample(t,r){var n,s;try{if(this.encoderInitialized||(this.ensureEncoderPromise||this.ensureEncoder(t),this.encoderInitialized||await this.ensureEncoderPromise),C(this.encoderInitialized),this.closed)return;{const i=Math.round(t.timestamp*t.sampleRate),o=Math.round((t.timestamp+t.duration)*t.sampleRate);if(this.lastEndSampleIndex===null)this.lastEndSampleIndex=o;else{const a=i-this.lastEndSampleIndex;if(a>=64){const l=new He({data:new Float32Array(a*t.numberOfChannels),format:"f32-planar",sampleRate:t.sampleRate,numberOfChannels:t.numberOfChannels,numberOfFrames:a,timestamp:this.lastEndSampleIndex/t.sampleRate});await this.encodeSample(l,!0)}this.lastEndSampleIndex+=t.numberOfFrames}}if((s=(n=this.encodingConfig).onEncodedSample)==null||s.call(n,t),this.customEncoder){this.customEncoderQueueSize++;const i=t.clone(),o=this.customEncoderCallSerializer.call(()=>this.customEncoder.encode(i)).catch(a=>this.setError(a)).finally(()=>{this.customEncoderQueueSize--,i.close()});this.customEncoderQueueSize>=4&&await o,await this.lastMuxerPromise}else if(this.isPcmEncoder)await this.doPcmEncoding(t,r);else{C(this.encoder);const i=t.toAudioData();this.encoder.encode(i),i.close(),r&&t.close(),this.encoder.encodeQueueSize>=4&&await new Promise(o=>this.encoder.addEventListener("dequeue",o,{once:!0})),await this.lastMuxerPromise}}finally{r&&t.close()}}async doPcmEncoding(t,r){var b,p;C(this.outputSampleSize),C(this.writeOutputValue);const{numberOfChannels:n,numberOfFrames:s,sampleRate:i,timestamp:o}=t,a=2048,l=[];for(let w=0;w{var a,l;const{numberOfChannels:r,sampleRate:n}=t,s=Nn(this.encodingConfig.quality,this.encodingConfig.bitrate),i=la({numberOfChannels:r,sampleRate:n,...this.encodingConfig,quality:s});(l=(a=this.encodingConfig).onEncoderConfig)==null||l.call(a,i),this.source._nominalBitrate=i.bitrate??null;const o=da.find(f=>f.supports(this.encodingConfig.codec,i));if(o)this.customEncoder=new o,this.customEncoder.codec=this.encodingConfig.codec,this.customEncoder.config=i,this.customEncoder.onPacket=(f,c)=>{var u,h;if(!(f instanceof St))throw new TypeError("The first argument passed to onPacket must be an EncodedPacket.");if(c!==void 0&&(!c||typeof c!="object"))throw new TypeError("The second argument passed to onPacket must be an object or undefined.");(h=(u=this.encodingConfig).onEncodedPacket)==null||h.call(u,f,c),this.lastMuxerPromise=this.muxer.addEncodedAudioPacket(this.source._connectedTrack,f,c).catch(d=>{this.setError(d)})},this.customEncoder.onError=f=>{this.setError(f)},await this.customEncoder.init();else if(pt.includes(this.encodingConfig.codec))this.initPcmEncoder();else{if(typeof AudioEncoder>"u")throw new Error(qo("AudioEncoder"));let f;try{f=(await AudioEncoder.isConfigSupported(i)).supported??!1}catch{f=!1}if(!f)throw new Error(`This specific encoder configuration (${i.codec}, ${i.bitrate} bps, ${i.numberOfChannels} channels, ${i.sampleRate} Hz) is not supported in this environment. Consider using another codec or changing your audio parameters.`);const c=new Error("Encoding error").stack;this.encoder=new AudioEncoder({output:(u,h)=>{var m,b;if(this.encodingConfig.codec==="aac"&&(h!=null&&h.decoderConfig)){if(h.decoderConfig.description&&h.decoderConfig.description.byteLength>0&&Ie(h.decoderConfig.description)[0]===3){const{decoderSpecificInfo:w}=Kc(vr.tempFromBytes(Ie(h.decoderConfig.description)));h.decoderConfig.description=w??void 0}let p=!1;if(!h.decoderConfig.description||h.decoderConfig.description.byteLength<2?p=!0:p=H0(Ie(h.decoderConfig.description)).objectType===0,p){const w=Number(ze(i.codec.split(".")));h.decoderConfig.description=Go({objectType:w,outputNumberOfChannels:h.decoderConfig.numberOfChannels,outputSampleRate:h.decoderConfig.sampleRate})}}let d=St.fromEncodedChunk(u);d=d.clone({timestamp:ui(d.timestamp,i.sampleRate),duration:u.duration!=null?ui(d.duration,i.sampleRate):void 0}),(b=(m=this.encodingConfig).onEncodedPacket)==null||b.call(m,d,h),this.lastMuxerPromise=this.muxer.addEncodedAudioPacket(this.source._connectedTrack,d,h).catch(p=>{this.setError(p)})},error:u=>{u.stack=c,this.setError(u)}}),this.encoder.configure(i)}C(this.source._connectedTrack),this.muxer=this.source._connectedTrack.output._muxer,this.encoderInitialized=!0})()}initPcmEncoder(){this.isPcmEncoder=!0;const t=this.encodingConfig.codec,{dataType:r,sampleSize:n,littleEndian:s}=ir(t);switch(this.outputSampleSize=n,n){case 1:r==="unsigned"?this.writeOutputValue=(i,o,a)=>i.setUint8(o,_e(Math.round(a*128)+128,0,255)):r==="signed"?this.writeOutputValue=(i,o,a)=>{i.setInt8(o,_e(Math.round(a*128),-128,127))}:r==="ulaw"?this.writeOutputValue=(i,o,a)=>{const l=_e(Math.round(a*32768),-32768,32767);i.setUint8(o,gl(l))}:r==="alaw"?this.writeOutputValue=(i,o,a)=>{const l=_e(Math.round(a*32768),-32768,32767);i.setUint8(o,xl(l))}:C(!1);break;case 2:r==="unsigned"?this.writeOutputValue=(i,o,a)=>i.setUint16(o,_e(Math.round(a*32768)+32768,0,65535),s):r==="signed"?this.writeOutputValue=(i,o,a)=>i.setInt16(o,_e(Math.round(a*32768),-32768,32767),s):C(!1);break;case 3:r==="unsigned"?this.writeOutputValue=(i,o,a)=>Ws(i,o,_e(Math.round(a*8388608)+8388608,0,16777215),s):r==="signed"?this.writeOutputValue=(i,o,a)=>C0(i,o,_e(Math.round(a*8388608),-8388608,8388607),s):C(!1);break;case 4:r==="unsigned"?this.writeOutputValue=(i,o,a)=>i.setUint32(o,_e(Math.round(a*2147483648)+2147483648,0,4294967295),s):r==="signed"?this.writeOutputValue=(i,o,a)=>i.setInt32(o,_e(Math.round(a*2147483648),-2147483648,2147483647),s):r==="float"?this.writeOutputValue=(i,o,a)=>i.setFloat32(o,a,s):C(!1);break;case 8:r==="float"?this.writeOutputValue=(i,o,a)=>i.setFloat64(o,a,s):C(!1);break;default:Xt(n),C(!1)}}async flushAndClose(t){try{t||(this.checkForEncoderError(),this.resampler&&await this.resampler.finalize()),this.closed=!0,t||(this.customEncoder?this.customEncoderCallSerializer.call(()=>this.customEncoder.flush()):this.encoder&&await this.encoder.flush())}finally{this.closed=!0,this.resampler=null,this.customEncoder?await this.customEncoderCallSerializer.call(()=>this.customEncoder.close()).catch(r=>this.setError(r)):this.encoder&&this.encoder.state!=="closed"&&this.encoder.close()}t||this.checkForEncoderError()}getQueueSize(){var t;return this.customEncoder?this.customEncoderQueueSize:this.isPcmEncoder?0:((t=this.encoder)==null?void 0:t.encodeQueueSize)??0}checkForEncoderError(){if(this.errorSet)throw this.error}}class Xf extends Ea{constructor(t,r={}){if(dl(t),typeof r!="object"||!r)throw new TypeError("options must be an object.");if(r.startTimestamp!==void 0&&!Number.isFinite(r.startTimestamp))throw new TypeError("options.startTimestamp, when provided, must be a finite number.");super(t.codec),this._encoder=new Gf(this,t),this._accumulatedTime=r.startTimestamp??0}async add(t){if(!(t instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");const r=He._fromAudioBuffer(t,this._accumulatedTime);this._accumulatedTime+=t.duration;for(const n of r)await this._encoder.add(n,!0)}_flushAndClose(t){return this._encoder.flushAndClose(t)}}class Qf extends Xs{constructor(t){if(super(),!Gr.includes(t))throw new TypeError(`Invalid subtitle codec '${t}'. Must be one of: ${Gr.join(", ")}.`);this._codec=t}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */class Ta{get supportsVideoRotationMetadata(){return this.supportsVideoTransformationMetadata}getSupportedVideoCodecs(){return this.getSupportedCodecs().filter(t=>Ot.includes(t))}getSupportedAudioCodecs(){return this.getSupportedCodecs().filter(t=>sr.includes(t))}getSupportedSubtitleCodecs(){return this.getSupportedCodecs().filter(t=>Gr.includes(t))}_codecUnsupportedHint(t){return""}_isFragmentedIsobmff(){return!1}}class Qs extends Ta{constructor(t={}){if(!t||typeof t!="object")throw new TypeError("options must be an object.");if(t.fastStart!==void 0&&![!1,"in-memory","reserve","fragmented"].includes(t.fastStart))throw new TypeError("options.fastStart, when provided, must be false, 'in-memory', 'reserve', or 'fragmented'.");if(t.minimumFragmentDuration!==void 0&&(!z0(t.minimumFragmentDuration)||t.minimumFragmentDuration<0))throw new TypeError("options.minimumFragmentDuration, when provided, must be a non-negative number.");if(t.onFtyp!==void 0&&typeof t.onFtyp!="function")throw new TypeError("options.onFtyp, when provided, must be a function.");if(t.onMoov!==void 0&&typeof t.onMoov!="function")throw new TypeError("options.onMoov, when provided, must be a function.");if(t.onMdat!==void 0&&typeof t.onMdat!="function")throw new TypeError("options.onMdat, when provided, must be a function.");if(t.onMoof!==void 0&&typeof t.onMoof!="function")throw new TypeError("options.onMoof, when provided, must be a function.");if(t.metadataFormat!==void 0&&!["mdir","mdta","udta","auto"].includes(t.metadataFormat))throw new TypeError("options.metadataFormat, when provided, must be either 'auto', 'mdir', 'mdta', or 'udta'.");super(),this._options=t}getSupportedTrackCounts(){return{video:{min:0,max:4294967295},audio:{min:0,max:4294967295},subtitle:{min:0,max:4294967295},total:{min:0,max:4294967295}}}get supportsVideoTransformationMetadata(){return!0}get supportsTimestampedMediaData(){return!0}get negativeTimestampSupport(){return"full"}_createMuxer(t){return new Vf(t,this)}_isFragmentedIsobmff(){return this._options.fastStart==="fragmented"}}class Sa extends Qs{constructor(t){super(t)}get _name(){return"MP4"}get fileExtension(){return".mp4"}get mimeType(){return"video/mp4"}getSupportedCodecs(){return[...Ot,...$s,"pcm-s16","pcm-s16be","pcm-s24","pcm-s24be","pcm-s32","pcm-s32be","pcm-f32","pcm-f32be","pcm-f64","pcm-f64be",...Gr]}_codecUnsupportedHint(t){return new Ca().getSupportedCodecs().includes(t)?" Switching to MOV will grant support for this codec.":""}}class Xi extends Qs{constructor(t){super(t)}get _name(){return"CMAF"}get fileExtension(){return".m4s"}get mimeType(){return"video/mp4"}getSupportedCodecs(){return[...Ot,...$s,"pcm-s16","pcm-s16be","pcm-s24","pcm-s24be","pcm-s32","pcm-s32be","pcm-f32","pcm-f32be","pcm-f64","pcm-f64be",...Gr]}}class Ca extends Qs{constructor(t){super(t)}get _name(){return"MOV"}get fileExtension(){return".mov"}get mimeType(){return"video/quicktime"}getSupportedCodecs(){return[...Ot,...sr]}_codecUnsupportedHint(t){return new Sa().getSupportedCodecs().includes(t)?" Switching to MP4 will grant support for this codec.":""}}/*! + * Copyright (c) 2026-present, Vanilagy and contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */const Qi=["video","audio","subtitle"];class Yr{constructor(t,r,n,s,i){this.id=t,this.output=r,this.type=n,this.source=s,this.metadata=i}isVideoTrack(){return this.type==="video"}isAudioTrack(){return this.type==="audio"}isSubtitleTrack(){return this.type==="subtitle"}canBePairedWith(t){if(!(t instanceof Yr))throw new TypeError("other must be an OutputTrack.");if(this===t)return!1;const r=hi(this.metadata.group),n=hi(t.metadata.group);for(const s of r)if(this.type!==t.type&&n.some(a=>s===a)||n.some(a=>s._pairedGroups.has(a)))return!0;return!1}}class Kf extends Yr{constructor(t,r,n,s){super(t,r,"video",n,s)}}class Yf extends Yr{constructor(t,r,n,s){super(t,r,"audio",n,s)}}class Zf extends Yr{constructor(t,r,n,s){super(t,r,"subtitle",n,s)}}class Xr{constructor(){this._pairedGroups=new Set}pairWith(t){if(!(t instanceof Xr))throw new TypeError("other must be an OutputTrackGroup.");if(this===t)throw new TypeError("Cannot pair a group with itself.");this._pairedGroups.add(t),t._pairedGroups.add(this)}}const rs=e=>{if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");if(e.languageCode!==void 0&&!A0(e.languageCode))throw new TypeError("metadata.languageCode, when provided, must be a three-letter, ISO 639-2/T language code.");if(e.name!==void 0&&typeof e.name!="string")throw new TypeError("metadata.name, when provided, must be a string.");if(e.disposition!==void 0&&U0(e.disposition),e.maximumPacketCount!==void 0&&(!Number.isInteger(e.maximumPacketCount)||e.maximumPacketCount<0))throw new TypeError("metadata.maximumPacketCount, when provided, must be a non-negative integer.");if(e.bitrate!==void 0&&(!Number.isFinite(e.bitrate)||e.bitrate<0))throw new TypeError("metadata.bitrate, when provided, must be a non-negative number.");if(e.averageBitrate!==void 0&&(!Number.isFinite(e.averageBitrate)||e.averageBitrate<0))throw new TypeError("metadata.averageBitrate, when provided, must be a non-negative number.");if(e.group!==void 0&&!(e.group instanceof Xr)&&(!Array.isArray(e.group)||e.group.some(t=>!(t instanceof Xr))))throw new TypeError("metadata.group, when provided, must be an OutputTrackGroup instance or an array of OutputTrackGroup instances.")};class Jf extends Ls{get target(){const t="Output.target cannot be used when using PathedTarget with an async callback. Use the 'target' event instead.";if(this._rootTargetPromise)throw new TypeError(t);const r=this._getRootTarget();if(Gt(r))throw new TypeError(t);return r}constructor(t){if(super(),this.state="pending",this.defaultTrackGroup=new Xr,this.tracks=[],this._onFinalize=null,this._unfinalizedTargets=new Set,this._rootWriterPromise=null,this._startPromise=null,this._cancelPromise=null,this._finalizePromise=null,this._mutex=new jo,this._metadataTags={},this._rootTarget=null,this._rootTargetPromise=null,this._firstMediaStreamTimestamp=null,!t||typeof t!="object")throw new TypeError("options must be an object.");if(!(t.format instanceof Ta))throw new TypeError("options.format must be an OutputFormat.");if(!(t.target instanceof Nt||t.target instanceof es))throw new TypeError("options.target must be a Target or a PathedTarget.");if(t.target instanceof Nt&&this._rememberTarget(t.target),t.initTarget!==void 0&&!(t.initTarget instanceof Nt)&&typeof t.initTarget!="function")throw new Error("options.initTarget, when provided, must be a Target or a function that returns or resolves to a Target.");if(t.onFinalize!==void 0&&typeof t.onFinalize!="function")throw new TypeError("options.onFinalize, when provided, must be a function.");this.format=t.format,this._target=t.target,this._onFinalize=t.onFinalize??null,this._initTarget=t.initTarget??null,this._initTarget instanceof Nt&&this._rememberTarget(this._initTarget),this._muxer=t.format._createMuxer(this)}_getTargetValidated(t){C(this._target instanceof es);const r=this._target.getTarget(t),n=s=>{if(!(s instanceof Nt))throw new TypeError("getTarget must return a Target.");return s};return Gt(r)?r.then(n):n(r)}async _getTarget(t){C(this._target instanceof es);const r=await this._getTargetValidated(t);return this._emit("target",{target:r,request:t,isRoot:t.isRoot}),this.state==="canceled"?await r._close():this._rememberTarget(r),r}_rememberTarget(t){this._unfinalizedTargets.add(t),t.on("finalized",()=>this._unfinalizedTargets.delete(t),{once:!0})}async _getInitTarget(){if(C(this._initTarget!==null),this._initTarget instanceof Nt)return this._initTarget;const t=await this._initTarget();return this.state==="canceled"?await t._close():this._rememberTarget(t),t}_hasInitTarget(){return this._initTarget!==null}_getRootTarget(){if(this._rootTarget)return this._rootTarget;if(this._rootTargetPromise)return this._rootTargetPromise;if(this._target instanceof Nt)return this._emit("target",{target:this._target,request:null,isRoot:!0}),this._rootTarget=this._target,this._target;const t={path:this._target.rootPath,isRoot:!0,mimeType:this.format.mimeType},r=this._getTargetValidated(t),n=s=>(this.state==="canceled"?s._close():this._rememberTarget(s),this._emit("target",{target:s,request:t,isRoot:!0}),this._rootTarget=s,s);return Gt(r)?this._rootTargetPromise=r.then(n):n(r)}_getRootWriter(t){return this._rootWriterPromise??(this._rootWriterPromise=(async()=>{const r=await this._getRootTarget(),n=new Ns(r,typeof t=="boolean"?t:t(r));return n.start(),n})())}addVideoTrack(t,r={}){if(!(t instanceof ka))throw new TypeError("source must be a VideoSource.");if(rs(r),r.rotation!==void 0&&![0,90,180,270].includes(r.rotation))throw new TypeError(`Invalid video rotation: ${r.rotation}. Has to be 0, 90, 180 or 270.`);if(r.flip!==void 0&&typeof r.flip!="boolean")throw new TypeError("metadata.flip, when provided, must be a boolean.");if(r.transformationMatrix!==void 0&&(!Array.isArray(r.transformationMatrix)||r.transformationMatrix.length!==9||!r.transformationMatrix.every(s=>Number.isFinite(s))))throw new TypeError("metadata.transformationMatrix, when provided, must be an array of 9 finite numbers.");if(r.frameRate!==void 0&&(!Number.isFinite(r.frameRate)||r.frameRate<=0))throw new TypeError(`Invalid video frame rate: ${r.frameRate}. Must be a positive number.`);if(r.hasOnlyKeyPackets!==void 0&&typeof r.hasOnlyKeyPackets!="boolean")throw new TypeError("metadata.hasOnlyKeyPackets, when provided, must be a boolean.");if(r.canBeTransparent!==void 0&&typeof r.canBeTransparent!="boolean")throw new TypeError("metadata.canBeTransparent, when provided, must be a boolean.");if(r.decoderConfig!==void 0&&ra({decoderConfig:r.decoderConfig},t._codec),r.primingPacket!==void 0){if(!(r.primingPacket instanceof St))throw new TypeError("metadata.primingPacket, when provided, must be an EncodedPacket.");if(r.decoderConfig===void 0)throw new TypeError("metadata.primingPacket can only be provided alongside metadata.decoderConfig.")}const n={...r};return n.group??(n.group=this.defaultTrackGroup),this._addTrack(new Kf(this.tracks.length+1,this,t,n))}addAudioTrack(t,r={}){if(!(t instanceof Ea))throw new TypeError("source must be an AudioSource.");if(rs(r),r.decoderConfig!==void 0&&na({decoderConfig:r.decoderConfig},t._codec),r.primingPacket!==void 0){if(!(r.primingPacket instanceof St))throw new TypeError("metadata.primingPacket, when provided, must be an EncodedPacket.");if(r.decoderConfig===void 0)throw new TypeError("metadata.primingPacket can only be provided alongside metadata.decoderConfig.")}const n={...r};return n.group??(n.group=this.defaultTrackGroup),this._addTrack(new Yf(this.tracks.length+1,this,t,n))}addSubtitleTrack(t,r={}){if(!(t instanceof Qf))throw new TypeError("source must be a SubtitleSource.");rs(r);const n={...r};return n.group??(n.group=this.defaultTrackGroup),this._addTrack(new Zf(this.tracks.length+1,this,t,n))}setMetadataTags(t){if($0(t),this.state!=="pending")throw new Error("Cannot set metadata tags after output has been started or canceled.");this._metadataTags=t}_addTrack(t){if(this.state!=="pending")throw new Error("Cannot add track after output has been started or canceled.");if(t.source._connectedTrack)throw new Error("Source is already used for a track.");const r=this.format.getSupportedTrackCounts(),n=this.tracks.reduce((o,a)=>o+(a.type===t.type?1:0),0),s=r[t.type].max;if(n===s)throw new Error(s===0?`${this.format._name} does not support ${t.type} tracks.`:`${this.format._name} does not support more than ${s} ${t.type} track${s===1?"":"s"}.`);const i=r.total.max;if(this.tracks.length===i)throw new Error(`${this.format._name} does not support more than ${i} tracks${i===1?"":"s"} in total.`);if(t.isVideoTrack()){const o=this.format.getSupportedVideoCodecs();if(o.length===0)throw new Error(`${this.format._name} does not support video tracks.`+this.format._codecUnsupportedHint(t.source._codec));if(!o.includes(t.source._codec))throw new Error(`Codec '${t.source._codec}' cannot be contained within ${this.format._name}. Supported video codecs are: ${o.map(a=>`'${a}'`).join(", ")}.`+this.format._codecUnsupportedHint(t.source._codec))}else if(t.isAudioTrack()){const o=this.format.getSupportedAudioCodecs();if(o.length===0)throw new Error(`${this.format._name} does not support audio tracks.`+this.format._codecUnsupportedHint(t.source._codec));if(!o.includes(t.source._codec))throw new Error(`Codec '${t.source._codec}' cannot be contained within ${this.format._name}. Supported audio codecs are: ${o.map(a=>`'${a}'`).join(", ")}.`+this.format._codecUnsupportedHint(t.source._codec))}else if(t.isSubtitleTrack()){const o=this.format.getSupportedSubtitleCodecs();if(o.length===0)throw new Error(`${this.format._name} does not support subtitle tracks.`+this.format._codecUnsupportedHint(t.source._codec));if(!o.includes(t.source._codec))throw new Error(`Codec '${t.source._codec}' cannot be contained within ${this.format._name}. Supported subtitle codecs are: ${o.map(a=>`'${a}'`).join(", ")}.`+this.format._codecUnsupportedHint(t.source._codec))}return this.tracks.push(t),t.source._connectedTrack=t,t}hasEnoughTracks(){const t=this.format.getSupportedTrackCounts();for(const n of Qi){const s=this.tracks.reduce((o,a)=>o+(a.type===n?1:0),0),i=t[n].min;if(so+(a.type===n?1:0),0),i=t[n].min;if(s{this.state="started";const n=this._mutex.acquire();try{await this._muxer.start();const s=this.tracks.map(i=>i.source._start());await Promise.all(s)}finally{(await n)()}})()}getMimeType(){return this._muxer.getMimeType()}async cancel(){if(this.state==="canceled")return this._cancelPromise??void 0;if(this.state==="finalizing"||this.state==="finalized"){this.state==="finalized"&&ye._warn("Output has already been finalized.");return}return this._cancelPromise=(async()=>{this.state="canceled";const t=await this._mutex.acquire();try{const r=this.tracks.map(n=>n.source._flushOrWaitForOngoingClose(!0));await Promise.all(r),await Promise.all([...this._unfinalizedTargets].map(n=>n._close())),this._unfinalizedTargets.clear()}finally{t()}})()}async finalize(){if(this.state==="pending")throw new Error("Cannot finalize before starting.");if(this.state==="canceled")throw new Error("Cannot finalize after canceling.");return this._finalizePromise?(ye._warn("Output has already been finalized."),this._finalizePromise):this._finalizePromise=(async()=>{this.state="finalizing";const t=await this._mutex.acquire();try{const r=this.tracks.map(n=>n.source._flushOrWaitForOngoingClose(!1));if(await Promise.all(r),await this._muxer.finalize(),this._rootWriterPromise){const n=await this._rootWriterPromise;n.finalized||(await n.flush(),await n.finalize())}this._onFinalize&&await this._onFinalize(),this.state="finalized"}catch(r){throw this.state="canceled",r}finally{await Promise.all([...this._unfinalizedTargets].map(r=>r._close().catch(()=>{}))),this._unfinalizedTargets.clear(),t()}})()}}const Ki="./";function eu({defaultAspect:e}){const[t,r]=te.useState(e),[n,s]=te.useState("1080"),[i,o]=te.useState(30),[a,l]=te.useState(!0),[f,c]=te.useState(!1),[u,h]=te.useState(0),[d,m]=te.useState(""),[b,p]=te.useState(null),w=te.useRef(!1),y=typeof window<"u"&&"VideoEncoder"in window,g=async()=>{c(!0),p(null),h(0),w.current=!1;const T=n==="1080"?1080:720,A=t==="16:9"?Math.round(T*16/9/2)*2:T,N=t==="16:9"?T:Math.round(T*16/9/2)*2;let S=null;try{m("准备字体与音轨…"),await Vs(Ki);const B=await Do(`${Ki}${ae.assets}voice.mp3`),V=Number(new URLSearchParams(location.search).get("exportSeconds"))||ve,W=Math.round(Math.min(ve,V)*i),L=W/i,J=new AudioBuffer({numberOfChannels:2,length:Math.round(L*B.sampleRate),sampleRate:B.sampleRate});for(let H=0;H<2;H++)J.copyToChannel(B.getChannelData(H).subarray(0,J.length),H);const U=await wl(["avc","hevc","vp9","av1"],{width:A,height:N}),I=await yl(["aac","opus"],{numberOfChannels:2,sampleRate:B.sampleRate});if(!U)throw new Error("这个浏览器不支持视频编码(请用电脑版 Chrome / Edge / Safari 17+)");const j=document.createElement("canvas");j.width=A,j.height=N;const Z=j.getContext("2d",{alpha:!1});S=new Jf({format:new Sa({fastStart:"in-memory"}),target:new pn});const re=new Hf(j,{codec:U,bitrate:ml,keyFrameInterval:2});S.addVideoTrack(re,{frameRate:i});let ie=null;I&&(ie=new Xf({codec:I,bitrate:192e3}),S.addAudioTrack(ie)),await S.start(),ie&&(m("编码音轨…"),await ie.add(J),ie.close());const O=performance.now();for(let H=0;H`rounded border px-3 py-1.5 text-sm transition ${T?"border-[#2a2420] bg-[#2a2420] text-[#f6efdf]":"border-[#d0c3a5] bg-[#fbf7ee] text-[#3a3128] hover:border-[#8a7a5a]"}`;return v.jsxDEV("div",{className:"grid gap-5 md:grid-cols-[1fr_1.1fr]",children:[v.jsxDEV("div",{className:"flex flex-col gap-4",children:[v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1.5 text-sm text-[#6b5d48]",children:"画幅"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:89,columnNumber:11},this),v.jsxDEV("div",{className:"flex flex-wrap gap-2",children:[v.jsxDEV("button",{className:k(t==="16:9"),onClick:()=>r("16:9"),children:[v.jsxDEV(Ao,{className:"mr-1 inline h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:91,columnNumber:90},this),"横屏 16:9"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:91,columnNumber:13},this),v.jsxDEV("button",{className:k(t==="9:16"),onClick:()=>r("9:16"),children:[v.jsxDEV(Io,{className:"mr-1 inline h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:92,columnNumber:90},this),"竖屏 9:16(手机)"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:92,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:90,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:88,columnNumber:9},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1.5 text-sm text-[#6b5d48]",children:"帧率"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:96,columnNumber:11},this),v.jsxDEV("div",{className:"flex gap-2",children:[24,30,60].map(T=>v.jsxDEV("button",{className:k(i===T),onClick:()=>o(T),children:[T," fps"]},T,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:97,columnNumber:75},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:97,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:95,columnNumber:9},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1.5 text-sm text-[#6b5d48]",children:"清晰度"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:100,columnNumber:11},this),v.jsxDEV("div",{className:"flex gap-2",children:[v.jsxDEV("button",{className:k(n==="1080"),onClick:()=>s("1080"),children:"1080P"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:102,columnNumber:13},this),v.jsxDEV("button",{className:k(n==="720"),onClick:()=>s("720"),children:"720P(更快)"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:103,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:101,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:99,columnNumber:9},this),v.jsxDEV("label",{className:"flex items-center gap-2 text-sm text-[#3a3128]",children:[v.jsxDEV("input",{type:"checkbox",checked:a,onChange:T=>l(T.target.checked),className:"accent-[#b8322a]"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:106,columnNumber:75},this),"烧录字幕"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:106,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:87,columnNumber:7},this),v.jsxDEV("div",{className:"flex flex-col justify-between gap-3 rounded-md border border-[#d9cdb2] bg-[#fbf7ee] p-4",children:[v.jsxDEV("div",{className:"text-sm leading-relaxed text-[#4a3f30]",children:["视频在你的浏览器里",v.jsxDEV("b",{children:"本地"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:110,columnNumber:20},this),"逐帧渲染、编码成 MP4(H.264 + AAC),不会上传任何东西。 全片 ",Math.round(ve)," 秒 ≈ ",Math.round(ve*i)," 帧;60fps 与 1080P 需要的时间更长,期间请保持页面在前台。",!y&&v.jsxDEV("div",{className:"mt-2 text-[#b8322a]",children:"当前浏览器不支持 WebCodecs,请用电脑版 Chrome / Edge 导出。"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:112,columnNumber:26},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:109,columnNumber:9},this),(f||u>0)&&v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"h-2 overflow-hidden rounded-full bg-[#e6dcc5]",children:v.jsxDEV("div",{className:"h-full bg-[#b8322a] transition-all",style:{width:`${u*100}%`}},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:116,columnNumber:76},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:116,columnNumber:13},this),v.jsxDEV("div",{className:"mt-1.5 text-xs text-[#6b5d48]",children:d},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:117,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:115,columnNumber:11},this),!f&&d&&u===0&&v.jsxDEV("div",{className:"text-xs text-[#b8322a]",children:d},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:120,columnNumber:40},this),v.jsxDEV("div",{className:"flex flex-wrap gap-2",children:[f?v.jsxDEV(v.Fragment,{children:[v.jsxDEV("span",{className:"inline-flex items-center gap-2 rounded bg-[#2a2420] px-4 py-2 text-[#f6efdf]",children:[v.jsxDEV(hn,{className:"h-4 w-4 animate-spin"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:126,columnNumber:110},this),"导出中 ",Math.round(u*100),"%"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:126,columnNumber:15},this),v.jsxDEV("button",{onClick:()=>{w.current=!0},className:"inline-flex items-center gap-1 rounded border border-[#cdbf9f] px-3 py-2 text-sm",children:[v.jsxDEV(o0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:127,columnNumber:158},this),"取消"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:127,columnNumber:15},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:125,columnNumber:13},this):v.jsxDEV("button",{onClick:g,disabled:!y,className:"inline-flex items-center gap-2 rounded bg-[#b8322a] px-4 py-2 text-[#f6efdf] shadow hover:bg-[#a02a23] disabled:opacity-50",children:[v.jsxDEV(e0,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:123,columnNumber:192},this),"开始导出 MP4"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:123,columnNumber:13},this),b&&v.jsxDEV("a",{href:b.url,download:b.name,className:"inline-flex items-center gap-2 rounded bg-[#2f5d3a] px-4 py-2 text-[#f6efdf] shadow",children:[v.jsxDEV(Fs,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:130,columnNumber:156},this),"保存 ",b.name,"(",(b.size/1048576).toFixed(1)," MB)"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:130,columnNumber:20},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:121,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:108,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/ExportPanel.tsx",lineNumber:86,columnNumber:5},this)}var Fe=Uint8Array,Qe=Uint16Array,Ks=Int32Array,Ys=new Fe([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Zs=new Fe([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Yi=new Fe([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Pa=function(e,t){for(var r=new Qe(31),n=0;n<31;++n)r[n]=t+=1<>1|(ge&21845)<<1;Vt=(Vt&52428)>>2|(Vt&13107)<<2,Vt=(Vt&61680)>>4|(Vt&3855)<<4,As[ge]=((Vt&65280)>>8|(Vt&255)<<8)>>1}var Ur=(function(e,t,r){for(var n=e.length,s=0,i=new Qe(t);s>l]=f}else for(a=new Qe(n),s=0;s>15-e[s]);return a}),or=new Fe(288);for(var ge=0;ge<144;++ge)or[ge]=8;for(var ge=144;ge<256;++ge)or[ge]=9;for(var ge=256;ge<280;++ge)or[ge]=7;for(var ge=280;ge<288;++ge)or[ge]=8;var kn=new Fe(32);for(var ge=0;ge<32;++ge)kn[ge]=5;var nu=Ur(or,9,0),su=Ur(kn,5,0),Na=function(e){return(e+7)/8|0},Ba=function(e,t,r){return(r==null||r>e.length)&&(r=e.length),new Fe(e.subarray(t,r))},iu=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],Bn=function(e,t,r){var n=new Error(t||iu[e]);if(n.code=e,Error.captureStackTrace&&Error.captureStackTrace(n,Bn),!r)throw n;return n},Ct=function(e,t,r){r<<=t&7;var n=t/8|0;e[n]|=r,e[n+1]|=r>>8},Ir=function(e,t,r){r<<=t&7;var n=t/8|0;e[n]|=r,e[n+1]|=r>>8,e[n+2]|=r>>16},ns=function(e,t){for(var r=[],n=0;nh&&(h=i[n].s);var d=new Qe(h+1),m=_s(r[c-1],d,0);if(m>t){var n=0,b=0,p=m-t,w=1<t)b+=w-(1<>=p;b>0;){var g=i[n].s;d[g]=0&&b;--n){var k=i[n].s;d[k]==t&&(--d[k],++b)}m=t}return{t:new Fe(d),l:m}},_s=function(e,t,r){return e.s==-1?Math.max(_s(e.l,t,r+1),_s(e.r,t,r+1)):t[e.s]=r},Ji=function(e){for(var t=e.length;t&&!e[--t];);for(var r=new Qe(++t),n=0,s=e[0],i=1,o=function(l){r[n++]=l},a=1;a<=t;++a)if(e[a]==s&&a!=t)++i;else{if(!s&&i>2){for(;i>138;i-=138)o(32754);i>2&&(o(i>10?i-11<<5|28690:i-3<<5|12305),i=0)}else if(i>3){for(o(s),--i;i>6;i-=6)o(8304);i>2&&(o(i-3<<5|8208),i=0)}for(;i--;)o(s);i=1,s=e[a]}return{c:r.subarray(0,n),n:t}},Rr=function(e,t){for(var r=0,n=0;n>8,e[s+2]=e[s]^255,e[s+3]=e[s+1]^255;for(var i=0;i4&&!V[Yi[L-1]];--L);var J=f+5<<3,U=Rr(s,or)+Rr(i,kn)+o,I=Rr(s,h)+Rr(i,b)+o+14+3*L+Rr(N,V)+2*N[16]+3*N[17]+7*N[18];if(l>=0&&J<=U&&J<=I)return Aa(t,c,e.subarray(l,l+f));var j,Z,re,ie;if(Ct(t,c,1+(I15&&(Ct(t,c,q[S]>>5&127),c+=q[S]>>12)}}else j=nu,Z=or,re=su,ie=kn;for(var S=0;S255){var H=K>>18&31;Ir(t,c,j[H+257]),c+=Z[H+257],H>7&&(Ct(t,c,K>>23&31),c+=Ys[H]);var be=K&31;Ir(t,c,re[be]),c+=ie[be],be>3&&(Ir(t,c,K>>5&8191),c+=Zs[be])}else Ir(t,c,j[K]),c+=Z[K]}return Ir(t,c,j[256]),c+Z[256]},ou=new Ks([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),_a=new Fe(0),au=function(e,t,r,n,s,i){var o=i.z||e.length,a=new Fe(n+o+5*(1+Math.ceil(o/7e3))+s),l=a.subarray(n,a.length-s),f=i.l,c=(i.r||0)&7;if(t){c&&(l[0]=i.r>>3);for(var u=ou[t-1],h=u>>13,d=u&8191,m=(1<7e3||V>24576)&&(j>423||!f)){c=eo(e,l,0,k,T,A,S,V,L,B-L,c),V=N=S=0,L=B;for(var Z=0;Z<286;++Z)T[Z]=0;for(var Z=0;Z<30;++Z)A[Z]=0}var re=2,ie=0,O=d,F=U-I&32767;if(j>2&&J==g(B-F))for(var Q=Math.min(h,j)-1,q=Math.min(32767,B),H=Math.min(258,j);F<=q&&--O&&U!=I;){if(e[B+re]==e[B+re-F]){for(var K=0;Kre){if(re=K,ie=F,K>Q)break;for(var be=Math.min(F,K-2),we=0,Z=0;Zwe&&(we=at,I=Be)}}}U=I,I=b[U],F+=U-I&32767}if(ie){k[V++]=268435456|Bs[re]<<18|Zi[ie];var ar=Bs[re]&31,ti=Zi[ie]&31;S+=Ys[ar]+Zs[ti],++T[257+ar],++A[ti],W=B+re,++N}else k[V++]=e[B],++T[e[B]]}}for(B=Math.max(B,W);B=o&&(l[c/8|0]=f,An=o),c=Aa(l,c+1,e.subarray(B,An))}i.i=o}return Ba(a,0,n+Na(c)+s)},cu=(function(){for(var e=new Int32Array(256),t=0;t<256;++t){for(var r=t,n=9;--n;)r=(r&1&&-306674912)^r>>>1;e[t]=r}return e})(),lu=function(){var e=-1;return{p:function(t){for(var r=e,n=0;n>>8;e=r},d:function(){return~e}}},fu=function(e,t,r,n,s){if(!s&&(s={l:1},t.dictionary)){var i=t.dictionary.subarray(-32768),o=new Fe(i.length+e.length);o.set(i),o.set(e,i.length),e=o,s.w=i.length}return au(e,t.level==null?6:t.level,t.mem==null?s.l?Math.ceil(Math.max(8,Math.min(13,Math.log(e.length)))*1.5):20:12+t.mem,r,n,s)},Ia=function(e,t){var r={};for(var n in e)r[n]=e[n];for(var n in t)r[n]=t[n];return r},Me=function(e,t,r){for(;r;++t)e[t]=r,r>>>=8};function uu(e,t){return fu(e,t||{},0,0)}var Ra=function(e,t,r,n){for(var s in e){var i=e[s],o=t+s,a=n;Array.isArray(i)&&(a=Ia(n,i[1]),i=i[0]),ArrayBuffer.isView(i)?r[o]=[i,a]:(r[o+="/"]=[new Fe(0),a],Ra(i,o,r,n))}},to=typeof TextEncoder<"u"&&new TextEncoder,du=typeof TextDecoder<"u"&&new TextDecoder,hu=0;try{du.decode(_a,{stream:!0}),hu=1}catch{}function ro(e,t){var r;if(to)return to.encode(e);for(var n=e.length,s=new Fe(e.length+(e.length>>1)),i=0,o=function(f){s[i++]=f},r=0;rs.length){var a=new Fe(i+8+(n-r<<1));a.set(s),s=a}var l=e.charCodeAt(r);l<128||t?o(l):l<2048?(o(192|l>>6),o(128|l&63)):l>55295&&l<57344?(l=65536+(l&1047552)|e.charCodeAt(++r)&1023,o(240|l>>18),o(128|l>>12&63),o(128|l>>6&63),o(128|l&63)):(o(224|l>>12),o(128|l>>6&63),o(128|l&63))}return Ba(s,0,i)}var Is=function(e){var t=0;if(e)for(var r in e){var n=e[r].length;n>65535&&Bn(9),t+=n+4}return t},no=function(e,t,r,n,s,i,o,a){var l=n.length,f=r.extra,c=a&&a.length,u=Is(f);Me(e,t,o!=null?33639248:67324752),t+=4,o!=null&&(e[t++]=20,e[t++]=r.os),e[t]=20,t+=2,e[t++]=r.flag<<1|(i<0&&8),e[t++]=s&&8,e[t++]=r.compression&255,e[t++]=r.compression>>8;var h=new Date(r.mtime==null?Date.now():r.mtime),d=h.getFullYear()-1980;if((d<0||d>119)&&Bn(10),Me(e,t,d<<25|h.getMonth()+1<<21|h.getDate()<<16|h.getHours()<<11|h.getMinutes()<<5|h.getSeconds()>>1),t+=4,i!=-1&&(Me(e,t,r.crc),Me(e,t+4,i<0?-i-2:i),Me(e,t+8,r.size)),Me(e,t+12,l),Me(e,t+14,u),t+=16,o!=null&&(Me(e,t,c),Me(e,t+6,r.attrs),Me(e,t+10,o),t+=14),e.set(n,t),t+=l,u)for(var m in f){var b=f[m],p=b.length;Me(e,t,+m),Me(e,t+2,p),e.set(b,t+4),t+=4+p}return c&&(e.set(a,t),t+=c),t},mu=function(e,t,r,n,s){Me(e,t,101010256),Me(e,t+8,r),Me(e,t+10,r),Me(e,t+12,n),Me(e,t+16,s)};function pu(e,t){t||(t={});var r={},n=[];Ra(e,"",r,t);var s=0,i=0;for(var o in r){var a=r[o],l=a[0],f=a[1],c=f.level==0?0:8,u=ro(o),h=u.length,d=f.comment,m=d&&ro(d),b=m&&m.length,p=Is(f.extra);h>65535&&Bn(11);var w=c?uu(l,f):l,y=w.length,g=lu();g.p(l),n.push(Ia(f,{size:l.length,crc:g.d(),c:w,f:u,m,u:h!=o.length||m&&d.length!=b,o:s,compression:c})),s+=30+h+p+y,i+=76+2*(h+p)+(b||0)+y}for(var k=new Fe(i+22),T=s,A=i-s,N=0;N{e.strokeText(y,c,u+g*f*1.02)}),e.fillStyle=G,l.forEach((y,g)=>e.fillText(y,c,u+g*f*1.02)),e.restore();const d=Math.max(26*a,f*.2),m=i==="left"?c+f*.85:c-f*.85;e.save(),e.font=`${d}px ${xr}`,e.textAlign="center",e.textBaseline="middle",e.lineJoin="round",e.strokeStyle=P(Ge,.92),e.lineWidth=d*.22;const b=[...s.replace(/\s/g,"")],p=u+f*.1;b.forEach((y,g)=>{const k=p+g*d*1.12,T=y===","?"︐":y==="·"?"・":y;e.strokeText(T,m,k)}),e.fillStyle=P(G,.88),b.forEach((y,g)=>{const k=p+g*d*1.12,T=y===","?"︐":y==="·"?"・":y;e.fillText(T,m,k)}),e.restore();const w=u+h-f*.15;Hr(e,c+(i==="left"?-f*.05:f*.05),w+f*.1,f*.42,o.seal??ae.seal),o.index&&(e.save(),e.font=`${d*1.1}px ${Rt}`,e.fillStyle=P("#b8322a",.9),e.textAlign="center",e.fillText(o.index,m,p-d*1.6),e.restore())}function wu(e,t,r,n){zs(e,t,r,n.t,{subs:!1,fadeEnd:!1}),bu(e,t,r,n.title,n.sub,n.side,{big:t>r?200:180});const s=Math.min(t,r)/1080;z(e,[[t*.3,r-40*s],[t*.5,r-34*s],[t*.7,r-42*s]],5*s,{alpha:.25,dry:.7,seed:3})}const yu="./",gu=420;async function ss(e,t,r=1){const n=Math.round(e.w*r/2)*2,s=Math.round(e.h*r/2)*2,i=document.createElement("canvas");i.width=n,i.height=s;const o=i.getContext("2d",{alpha:!1});wu(o,n,s,t);const a=await new Promise(l=>i.toBlob(f=>l(f),"image/jpeg",r<1?.85:.93));return i.width=i.height=1,a}const xu=(e,t)=>`${ae.title}封面_${e.id.replace(":","x")}_${t.name}_${e.w}x${e.h}.jpg`,so=e=>{const[t,r]=e.split("|");return{r:bn.find(n=>n.id===t),d:ae.covers.find(n=>n.id===r)}};function vu(){const[e,t]=te.useState([]),[r,n]=te.useState(new Set),[s,i]=te.useState("全部"),[o,a]=te.useState(!1);te.useEffect(()=>{let p=!1;return(async()=>{await Vs(yu);for(const w of bn)for(const y of ae.covers){if(p)return;await new Promise(k=>setTimeout(k,30));const g=await ss(w,y,gu/Math.max(w.w,w.h));if(p)return;t(k=>[...k,{key:`${w.id}|${y.id}`,ratio:w.id,design:y.id,name:xu(w,y),url:URL.createObjectURL(g),w:w.w,h:w.h}])}})(),()=>{p=!0}},[]);const l=bn.length*ae.covers.length,f=e.filter(p=>s==="全部"||p.ratio===s),c=p=>n(w=>{const y=new Set(w);return y.has(p)?y.delete(p):y.add(p),y}),[u,h]=te.useState(null),d=(p,w)=>{const y=URL.createObjectURL(p),g=document.createElement("a");g.href=y,g.download=w,g.click(),setTimeout(()=>URL.revokeObjectURL(y),4e3)},m=async p=>{h(p.key);const{r:w,d:y}=so(p.key);d(await ss(w,y),p.name),h(null)},b=async()=>{a(!0);const p={};for(const y of e.filter(g=>r.has(g.key))){const{r:g,d:k}=so(y.key);p[y.name]=new Uint8Array(await(await ss(g,k)).arrayBuffer()),await new Promise(T=>setTimeout(T,10))}const w=pu(p,{level:0});d(new Blob([w],{type:"application/zip"}),`${ae.title}封面_${r.size}张.zip`),a(!1)};return v.jsxDEV("div",{className:"flex flex-col gap-4",children:[v.jsxDEV("div",{className:"flex flex-wrap items-center gap-2",children:[["全部",...bn.map(p=>p.id)].map(p=>v.jsxDEV("button",{onClick:()=>i(p),className:`rounded border px-3 py-1 text-sm ${s===p?"border-[#2a2420] bg-[#2a2420] text-[#f6efdf]":"border-[#d0c3a5] bg-[#fbf7ee]"}`,children:p},p,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:68,columnNumber:11},this)),v.jsxDEV("span",{className:"text-xs text-[#8a7a5a]",children:e.lengthn(new Set(f.map(p=>p.key))),className:"rounded border border-[#d0c3a5] px-3 py-1 text-sm",children:["全选",s!=="全部"?` ${s}`:""]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:72,columnNumber:11},this),v.jsxDEV("button",{onClick:()=>n(new Set),className:"rounded border border-[#d0c3a5] px-3 py-1 text-sm",children:"清空"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:73,columnNumber:11},this),v.jsxDEV("button",{onClick:b,disabled:!r.size||o,className:"inline-flex items-center gap-1.5 rounded bg-[#b8322a] px-3 py-1 text-sm text-[#f6efdf] disabled:opacity-50",children:[o?v.jsxDEV(hn,{className:"h-4 w-4 animate-spin"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:75,columnNumber:24},this):v.jsxDEV(_o,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:75,columnNumber:71},this),"下载所选(ZIP)"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:74,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:71,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:66,columnNumber:7},this),v.jsxDEV("div",{className:"grid grid-cols-2 gap-3 md:grid-cols-4",children:f.map(p=>{var y;const w=r.has(p.key);return v.jsxDEV("div",{className:`group relative overflow-hidden rounded-md border-2 bg-[#fbf7ee] transition ${w?"border-[#b8322a]":"border-transparent"}`,children:[v.jsxDEV("button",{onClick:()=>c(p.key),className:"block w-full","aria-label":"选择封面",children:v.jsxDEV("img",{src:p.url,alt:p.name,className:"block w-full",style:{aspectRatio:`${p.w}/${p.h}`}},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:85,columnNumber:17},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:84,columnNumber:15},this),v.jsxDEV("span",{className:`absolute left-2 top-2 flex h-6 w-6 items-center justify-center rounded border-2 ${w?"border-[#b8322a] bg-[#b8322a] text-white":"border-white/90 bg-black/20"}`,children:w&&v.jsxDEV(Bo,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:87,columnNumber:205},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:87,columnNumber:15},this),v.jsxDEV("div",{className:"flex items-center justify-between px-2 py-1.5 text-xs text-[#4a3f30]",children:[v.jsxDEV("span",{children:[v.jsxDEV("b",{className:"mr-1",children:p.ratio},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:89,columnNumber:23},this),(y=ae.covers.find(g=>g.id===p.design))==null?void 0:y.name," · ",p.w,"×",p.h]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:89,columnNumber:17},this),v.jsxDEV("button",{onClick:()=>m(p),className:"rounded p-1 hover:bg-[#ece2cc]","aria-label":"下载原图",title:"下载原尺寸 JPG",children:u===p.key?v.jsxDEV(hn,{className:"h-4 w-4 animate-spin"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:90,columnNumber:149},this):v.jsxDEV(Fs,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:90,columnNumber:196},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:90,columnNumber:17},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:88,columnNumber:15},this)]},p.key,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:83,columnNumber:13},this)})},void 0,!1,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:79,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Covers.tsx",lineNumber:65,columnNumber:5},this)}const rn=e=>[...e].length;function Oa(){const[e,t]=te.useState(null);return{done:e,copy:async(n,s)=>{try{await navigator.clipboard.writeText(s)}catch{const i=document.createElement("textarea");i.value=s,document.body.appendChild(i),i.select(),document.execCommand("copy"),i.remove()}t(n),setTimeout(()=>t(i=>i===n?null:i),1400)}}}function nn({k:e,text:t,label:r,c:n}){const s=n.done===e;return v.jsxDEV("button",{onClick:()=>n.copy(e,t),className:`inline-flex shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs transition ${s?"border-[#2f5d3a] bg-[#2f5d3a] text-white":"border-[#d0c3a5] bg-[#fbf7ee] hover:border-[#8a7a5a]"}`,children:[s?v.jsxDEV(Bo,{className:"h-3.5 w-3.5"},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:21,columnNumber:13},this):v.jsxDEV(Ja,{className:"h-3.5 w-3.5"},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:21,columnNumber:49},this),s?"已复制":r]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:20,columnNumber:5},this)}function io({ok:e,children:t}){return v.jsxDEV("span",{className:`rounded px-1.5 py-0.5 text-[11px] tabular-nums ${e?"bg-[#e3ecd9] text-[#2f5d3a]":"bg-[#f5d9d5] text-[#b8322a]"}`,children:t},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:27,columnNumber:10},this)}function Fa({p:e,c:t,pre:r}){const[n,s]=te.useState(0),i=e.titles[n],o=e.desc??e.body??"",a=e.tags.map(c=>`#${c}`).join(" "),l=e.tagMax==null||e.tags.length<=e.tagMax,f=`${i} + +${o} + +${a}`;return v.jsxDEV("div",{className:"flex flex-col gap-3 rounded-md border border-[#d9cdb2] bg-[#fbf7ee] p-4",children:[v.jsxDEV("div",{className:"flex items-center gap-2",children:[v.jsxDEV("span",{className:"h-3 w-3 rounded-full",style:{background:e.color??"#b8322a"}},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:42,columnNumber:9},this),v.jsxDEV("h4",{className:"font-semibold text-[#2a2420]",children:e.name},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:43,columnNumber:9},this),v.jsxDEV("span",{className:"ml-auto",children:v.jsxDEV(nn,{k:`${r}${e.id}-all`,text:f,label:"复制全部",c:t},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:44,columnNumber:35},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:44,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:41,columnNumber:7},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1 flex items-center gap-2 text-xs text-[#6b5d48]",children:["标题 ",v.jsxDEV(io,{ok:rn(i)<=e.titleMax,children:[rn(i),"/",e.titleMax," 字"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:47,columnNumber:81},this),v.jsxDEV("span",{className:"text-[#a09070]",children:["(可选 ",e.titles.length," 个)"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:47,columnNumber:153},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:47,columnNumber:9},this),v.jsxDEV("div",{className:"flex flex-col gap-1.5",children:e.titles.map((c,u)=>v.jsxDEV("label",{className:`flex cursor-pointer items-start gap-2 rounded border px-2 py-1.5 text-sm ${u===n?"border-[#8a7a5a] bg-[#f3ead6]":"border-[#e3d9c2]"}`,children:[v.jsxDEV("input",{type:"radio",checked:u===n,onChange:()=>s(u),className:"mt-1 accent-[#b8322a]"},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:51,columnNumber:15},this),v.jsxDEV("span",{className:"flex-1 text-[#2a2420]",children:c},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:52,columnNumber:15},this),v.jsxDEV("span",{className:"text-[11px] text-[#a09070] tabular-nums",children:rn(c)},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:53,columnNumber:15},this),v.jsxDEV(nn,{k:`${r}${e.id}-t${u}`,text:c,label:"复制",c:t},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:54,columnNumber:15},this)]},u,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:50,columnNumber:13},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:48,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:46,columnNumber:7},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1 flex items-center justify-between text-xs text-[#6b5d48]",children:[v.jsxDEV("span",{children:[e.body?"正文":"描述","(",rn(o)," 字)"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:60,columnNumber:88},this),v.jsxDEV(nn,{k:`${r}${e.id}-d`,text:o,label:e.body?"复制正文":"复制描述",c:t},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:60,columnNumber:138},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:60,columnNumber:9},this),v.jsxDEV("pre",{className:"max-h-56 overflow-auto whitespace-pre-wrap rounded border border-[#e3d9c2] bg-[#f8f2e4] p-2 font-sans text-sm leading-relaxed text-[#3a3128]",children:o},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:61,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:59,columnNumber:7},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"mb-1 flex items-center justify-between text-xs text-[#6b5d48]",children:[v.jsxDEV("span",{className:"flex items-center gap-2",children:["话题 / 标签 ",v.jsxDEV(io,{ok:l,children:[e.tags.length,e.tagMax==null?" 个 · 不限":`/${e.tagMax}`]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:65,columnNumber:61},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:65,columnNumber:11},this),v.jsxDEV(nn,{k:`${r}${e.id}-tags`,text:a,label:"复制标签",c:t},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:66,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:64,columnNumber:9},this),v.jsxDEV("div",{className:"flex flex-wrap gap-1.5",children:e.tags.map(c=>v.jsxDEV("span",{className:"rounded-full bg-[#ece2cc] px-2 py-0.5 text-xs text-[#4a3f30]",children:["#",c]},c,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:68,columnNumber:68},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:68,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:63,columnNumber:7},this),v.jsxDEV("div",{className:"text-[11px] text-[#8a7a5a]",children:e.tip},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:70,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:40,columnNumber:5},this)}function ku(){const e=Oa();return v.jsxDEV("div",{className:"grid gap-4 md:grid-cols-2",children:ae.platforms.map(t=>v.jsxDEV(Fa,{p:t,c:e,pre:"v-"},t.id,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:77,columnNumber:80},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:77,columnNumber:10},this)}function Eu(){const e=Oa();return v.jsxDEV("div",{className:"grid gap-4 md:grid-cols-2",children:ae.photoPosts.map(t=>v.jsxDEV(Fa,{p:{...t,color:t.id==="xhs"?"#ff2442":"#111111"},c:e,pre:"p-"},t.id,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:82,columnNumber:81},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/PublishCopy.tsx",lineNumber:82,columnNumber:10},this)}const Tu="./",Or=e=>`${Tu}${ae.assets}live/${e}`;function Su(){return v.jsxDEV("div",{className:"flex flex-col gap-5",children:[v.jsxDEV("div",{className:"flex flex-wrap items-center gap-3 rounded-md border border-[#d9cdb2] bg-[#fbf7ee] p-4 text-sm text-[#3a3128]",children:[v.jsxDEV(Ya,{className:"h-5 w-5 shrink-0"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:12,columnNumber:9},this),v.jsxDEV("div",{className:"min-w-[240px] flex-1 leading-relaxed",children:[ae.live.length," 组苹果实况照片(每组 ",v.jsxDEV("b",{children:"JPG + MOV"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:14,columnNumber:41},this),",3:4 · 1080×1440 · 3 秒 · 30fps),按 livecanvas 流程写入 Live Photo 配对信息(共享 content identifier + still-image-time),另附 .pvt 资源包。 在 Mac 上解压后,把同名的 JPG 和 MOV 一起拖进「照片」即合成为实况图,再同步到手机发小红书 / 抖音图文。"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:13,columnNumber:9},this),v.jsxDEV("a",{href:Or("livephotos.zip"),download:`${ae.title}_水墨实况图_${ae.live.length}组.zip`,className:"inline-flex items-center gap-2 rounded bg-[#b8322a] px-4 py-2 text-[#f6efdf] shadow hover:bg-[#a02a23]",children:[v.jsxDEV(_o,{className:"h-4 w-4"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:17,columnNumber:212},this),"下载实况图压缩包"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:17,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:11,columnNumber:7},this),v.jsxDEV("div",{className:"grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-6",children:ae.live.map(e=>v.jsxDEV("figure",{className:"overflow-hidden rounded-md border border-[#d9cdb2] bg-[#fbf7ee]",children:[v.jsxDEV("video",{src:Or(`${e.id}.mp4`),poster:Or(`${e.id}.jpg`),autoPlay:!0,muted:!0,loop:!0,playsInline:!0,className:"block aspect-[3/4] w-full object-cover"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:22,columnNumber:13},this),v.jsxDEV("figcaption",{className:"flex items-center justify-between gap-1 px-2 py-1.5 text-xs text-[#4a3f30]",children:[v.jsxDEV("span",{children:[v.jsxDEV("b",{className:"text-[#b8322a]",children:e.index},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:24,columnNumber:21},this)," ",e.caption]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:24,columnNumber:15},this),v.jsxDEV("span",{className:"flex gap-1",children:[v.jsxDEV("a",{href:Or(`${e.id}.jpg`),download:!0,className:"rounded px-1 hover:bg-[#ece2cc]",title:"下载 JPG",children:"JPG"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:26,columnNumber:17},this),v.jsxDEV("a",{href:Or(`${e.id}.mov`),download:!0,className:"rounded px-1 hover:bg-[#ece2cc]",title:"下载 MOV",children:[v.jsxDEV(Fs,{className:"inline h-3.5 w-3.5"},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:27,columnNumber:112},this),"MOV"]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:27,columnNumber:17},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:25,columnNumber:15},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:23,columnNumber:13},this)]},e.id,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:21,columnNumber:11},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:19,columnNumber:7},this),v.jsxDEV(Eu,{},void 0,!1,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:33,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/LivePhotos.tsx",lineNumber:10,columnNumber:5},this)}const oo="./",Cu=e=>`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,"0")}`,is={PASS:"bg-[#e3ecd9] text-[#2f5d3a]",WARN:"bg-[#f6e7c4] text-[#8a5a10]",FAIL:"bg-[#f5d9d5] text-[#b8322a]",INFO:"bg-[#e6e0d2] text-[#5a4f3c]"};function Pu(){const e=Vo(),t=ae.qa;return v.jsxDEV("div",{className:"flex flex-col gap-6",children:[v.jsxDEV("div",{className:"grid gap-4 md:grid-cols-3",children:ae.making.map(([r,n])=>v.jsxDEV("div",{className:"rounded-md border border-[#d9cdb2] bg-[#fbf7ee] p-4",children:[v.jsxDEV("div",{className:"mb-1 font-brush text-xl",children:r},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:15,columnNumber:13},this),v.jsxDEV("p",{className:"text-sm leading-relaxed text-[#4a3f30]",children:n},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:16,columnNumber:13},this)]},r,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:14,columnNumber:11},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:12,columnNumber:7},this),v.jsxDEV("div",{className:"overflow-x-auto rounded-md border border-[#d9cdb2] bg-[#fbf7ee]",children:v.jsxDEV("table",{className:"w-full min-w-[640px] text-left text-sm",children:[v.jsxDEV("thead",{className:"bg-[#efe6d2] text-xs text-[#6b5d48]",children:v.jsxDEV("tr",{children:[v.jsxDEV("th",{className:"px-3 py-2",children:"#"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:70},this),v.jsxDEV("th",{className:"px-3 py-2",children:"时间"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:102},this),v.jsxDEV("th",{className:"px-3 py-2",children:"场景"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:135},this),v.jsxDEV("th",{className:"px-3 py-2",children:"旁白"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:168},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:66},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:23,columnNumber:11},this),v.jsxDEV("tbody",{children:ot.scenes.map((r,n)=>v.jsxDEV("tr",{className:"border-t border-[#e3d9c2] align-top",children:[v.jsxDEV("td",{className:"px-3 py-2 text-[#b8322a]",children:n+1},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:27,columnNumber:17},this),v.jsxDEV("td",{className:"px-3 py-2 tabular-nums text-[#6b5d48]",children:Cu(r.start)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:28,columnNumber:17},this),v.jsxDEV("td",{className:"whitespace-nowrap px-3 py-2 font-medium",children:e[n].label},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:29,columnNumber:17},this),v.jsxDEV("td",{className:"px-3 py-2 text-[#3a3128]",children:(r.subs??[]).map(s=>s.text).join(",")},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:30,columnNumber:17},this)]},r.id,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:26,columnNumber:15},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:24,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:22,columnNumber:9},this)},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:21,columnNumber:7},this),v.jsxDEV("div",{className:"flex flex-col gap-3",children:[v.jsxDEV("div",{className:"flex flex-wrap items-center gap-2",children:[v.jsxDEV("span",{className:"font-brush text-xl",children:"vibe qa 质检"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:39,columnNumber:11},this),t.status==="pending"?v.jsxDEV("span",{className:"text-sm text-[#8a7a5a]",children:"报告尚未生成"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:40,columnNumber:37},this):v.jsxDEV(v.Fragment,{children:[v.jsxDEV("span",{className:`rounded px-2 py-0.5 text-xs ${is[t.verdict??"INFO"]}`,children:t.verdict},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:42,columnNumber:15},this),t.meta&&v.jsxDEV("span",{className:"text-xs text-[#8a7a5a]",children:[t.meta.w,"×",t.meta.h," · ",t.meta.fps,"fps · ",t.meta.dur.toFixed(2),"s · ",t.generated]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:43,columnNumber:26},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:41,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:38,columnNumber:9},this),t.results.length>0&&v.jsxDEV("div",{className:"grid gap-1.5 md:grid-cols-2",children:t.results.map((r,n)=>v.jsxDEV("div",{className:"flex items-start gap-2 rounded border border-[#e3d9c2] bg-[#fbf7ee] px-2 py-1.5 text-xs",children:[v.jsxDEV("span",{className:`shrink-0 rounded px-1.5 py-0.5 font-mono ${is[r.level]??is.INFO}`,children:r.level},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:51,columnNumber:17},this),v.jsxDEV("span",{className:"shrink-0 font-medium text-[#3a3128]",children:r.check},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:52,columnNumber:17},this),v.jsxDEV("span",{className:"text-[#5a4f3c]",children:r.msg},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:53,columnNumber:17},this)]},n,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:50,columnNumber:15},this))},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:48,columnNumber:11},this),t.status!=="pending"&&v.jsxDEV("div",{className:"grid gap-3 md:grid-cols-2",children:[v.jsxDEV("figure",{children:[v.jsxDEV("img",{src:`${oo}${ae.assets}qa/sheet_landscape.jpg`,alt:"横屏接触表",className:"w-full rounded border border-[#d9cdb2]"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:60,columnNumber:21},this),v.jsxDEV("figcaption",{className:"mt-1 text-xs text-[#8a7a5a]",children:"横屏接触表(每 2 秒一格)"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:60,columnNumber:143},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:60,columnNumber:13},this),v.jsxDEV("figure",{children:[v.jsxDEV("img",{src:`${oo}${ae.assets}qa/sheet_portrait.jpg`,alt:"竖屏接触表",className:"w-full rounded border border-[#d9cdb2]"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:61,columnNumber:21},this),v.jsxDEV("figcaption",{className:"mt-1 text-xs text-[#8a7a5a]",children:"竖屏接触表"},void 0,!1,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:61,columnNumber:142},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:61,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:59,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:37,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/components/Making.tsx",lineNumber:11,columnNumber:5},this)}const jr=(e,t,r)=>{var n;return(((n=e.scenes.find(s=>s.id===t))==null?void 0:n.start)??0)+r},Mu="丑小鸭",Nu="ink-wash textbook illustration",Bu=1920,Au=1080,_u=30,Iu=156.3,Ru=4689,Ou="zh-CN-XiaoxiaoNeural",Fu=[{id:"title",start:0,dur:6.5,voice_start:.5,voice_dur:2.832,subs:[{t0:.5,t1:2.199,text:"安徒生童话"},{t0:2.199,t1:3.332,text:"丑小鸭"}],cue:{type:"title"},sfx:[{at:.3,type:"drop"},{at:4.6,type:"seal"}]},{id:"summer",start:6.5,dur:11.566666666666666,voice_start:7,voice_dur:10.176,subs:[{t0:7,t1:8.42,text:"夏天的乡下"},{t0:8.42,t1:9.603,text:"麦子金黄"},{t0:9.603,t1:12.68,text:"池塘边的芦苇长得比人还高"},{t0:12.68,t1:14.573,text:"鸭妈妈就在这里"},{t0:14.573,t1:17.176,text:"安安静静地孵着她的蛋"}],cue:{type:"summer"},sfx:[{at:.2,type:"breeze"}]},{id:"hatch",start:18.0667,dur:11.566666666666666,voice_start:18.5667,voice_dur:10.176,subs:[{t0:18.567,t1:19.26,text:"咔嚓"},{t0:19.26,t1:19.954,text:"咔嚓!"},{t0:19.954,t1:22.267,text:"蛋壳一个接一个裂开"},{t0:22.267,t1:25.042,text:"毛茸茸的小黄鸭钻了出来"},{t0:25.042,t1:26.43,text:"唧唧地叫着"},{t0:26.43,t1:28.743,text:"好奇地看着这个世界"}],cue:{type:"hatch"},sfx:[{at:.5,type:"crack"},{at:1.2,type:"crack"},{at:2.2,type:"peep"}]},{id:"bigegg",start:29.6333,dur:11.8,voice_start:30.1333,voice_dur:10.416,subs:[{t0:30.133,t1:32.217,text:"只剩最大的那颗蛋"},{t0:32.217,t1:33.374,text:"过了好久"},{t0:33.374,t1:34.763,text:"才慢慢裂开"},{t0:34.763,t1:36.614,text:"钻出来的小家伙"},{t0:36.614,t1:37.772,text:"又大又灰"},{t0:37.772,t1:40.549,text:"一点也不像他的哥哥姐姐"}],cue:{type:"bigegg"},sfx:[{at:2.6,type:"bigcrack"}]},{id:"bullied",start:41.4333,dur:10.366666666666667,voice_start:41.9333,voice_dur:8.952,subs:[{t0:41.933,t1:44.111,text:"大家都叫他丑小鸭"},{t0:44.111,t1:46.046,text:"哥哥姐姐笑话他"},{t0:46.046,t1:47.74,text:"公鸡追着啄他"},{t0:47.74,t1:50.885,text:"连喂鸭的小姑娘也嫌他碍事"}],cue:{type:"bullied"},sfx:[{at:.8,type:"quack"},{at:3.2,type:"flap"}]},{id:"leave",start:51.8,dur:7.533333333333333,voice_start:52.3,voice_dur:6.144,subs:[{t0:52.3,t1:54.348,text:"丑小鸭难过极了"},{t0:54.348,t1:55.116,text:"一天"},{t0:55.116,t1:57.164,text:"他悄悄钻过篱笆"},{t0:57.164,t1:58.444,text:"离开了家"}],cue:{type:"leave"},sfx:[{at:.3,type:"wind"}]},{id:"marsh",start:59.3333,dur:13.666666666666666,voice_start:59.8333,voice_dur:12.264,subs:[{t0:59.833,t1:61.921,text:"他来到一片沼泽"},{t0:61.921,t1:64.269,text:"野鸭们笑他长得怪"},{t0:64.269,t1:65.052,text:"忽然"},{t0:65.052,t1:65.574,text:"砰!"},{t0:65.574,t1:66.096,text:"砰!"},{t0:66.096,t1:68.183,text:"猎人的枪声响起"},{t0:68.183,t1:70.271,text:"他吓得躲进芦苇"},{t0:70.271,t1:72.097,text:"一动也不敢动"}],cue:{type:"marsh"},sfx:[{at:3.4,type:"shot"},{at:3.9,type:"shot"},{at:4.1,type:"scatter"}]},{id:"autumn",start:73,dur:15.8,voice_start:73.5,voice_dur:14.4,subs:[{t0:73.5,t1:74.7,text:"秋天到了"},{t0:74.7,t1:76.38,text:"树叶变黄变红"},{t0:76.38,t1:77.58,text:"一天傍晚"},{t0:77.58,t1:80.7,text:"一群洁白的大鸟从天上飞过"},{t0:80.7,t1:82.14,text:"脖子长长的"},{t0:82.14,t1:83.82,text:"翅膀又大又美"},{t0:83.82,t1:85.74,text:"丑小鸭望着他们"},{t0:85.74,t1:87.9,text:"心里说不出的向往"}],cue:{type:"autumn"},sfx:[{at:2.4,type:"call"}]},{id:"winter",start:88.8,dur:13.433333333333334,voice_start:89.3,voice_dur:12.024,subs:[{t0:89.3,t1:90.502,text:"冬天来了"},{t0:90.502,t1:92.186,text:"雪花纷纷扬扬"},{t0:92.186,t1:93.629,text:"湖面结了冰"},{t0:93.629,t1:96.033,text:"丑小鸭冻得缩成一团"},{t0:96.033,t1:96.755,text:"可是"},{t0:96.755,t1:97.957,text:"他咬着牙"},{t0:97.957,t1:101.324,text:"熬过了一个又一个寒冷的夜晚"}],cue:{type:"winter"},sfx:[{at:.2,type:"wind"},{at:3,type:"ice"}]},{id:"spring",start:102.2333,dur:12.366666666666667,voice_start:102.7333,voice_dur:10.968,subs:[{t0:102.733,t1:104.519,text:"春天终于来了!"},{t0:104.519,t1:105.794,text:"柳树发芽"},{t0:105.794,t1:107.07,text:"桃花盛开"},{t0:107.07,t1:109.365,text:"丑小鸭扇了扇翅膀"},{t0:109.365,t1:109.875,text:"咦?"},{t0:109.875,t1:112.171,text:"翅膀变得这么有力"},{t0:112.171,t1:113.701,text:"他飞起来了!"}],cue:{type:"spring"},sfx:[{at:.2,type:"chime"},{at:4.8,type:"rise"}]},{id:"reflection",start:114.6,dur:13.2,voice_start:115.1,voice_dur:11.784,subs:[{t0:115.1,t1:116.686,text:"他落到湖面上"},{t0:116.686,t1:118.952,text:"几只白天鹅向他游来"},{t0:118.952,t1:120.086,text:"他低下头"},{t0:120.086,t1:121.898,text:"看见水里的倒影"},{t0:121.898,t1:124.391,text:"那不是又丑又灰的鸭子"},{t0:124.391,t1:126.884,text:"而是一只美丽的白天鹅!"}],cue:{type:"reflection"},sfx:[{at:.6,type:"splash"},{at:5.6,type:"reveal"}]},{id:"accepted",start:127.8,dur:11.5,voice_start:128.3,voice_dur:10.104,subs:[{t0:128.3,t1:129.945,text:"天鹅们围着他"},{t0:129.945,t1:132.53,text:"用嘴轻轻梳理他的羽毛"},{t0:132.53,t1:135.114,text:"岸边的孩子们拍着手喊"},{t0:135.114,t1:135.819,text:"快看"},{t0:135.819,t1:138.404,text:"新来的那只天鹅最漂亮!"}],cue:{type:"accepted"},sfx:[{at:3.6,type:"cheer"}]},{id:"fly",start:139.3,dur:7,voice_start:139.8,voice_dur:5.472,subs:[{t0:139.8,t1:141.852,text:"他张开雪白的翅膀"},{t0:141.852,t1:143.448,text:"和伙伴们一起"},{t0:143.448,t1:145.272,text:"飞向高高的蓝天"}],cue:{type:"fly"},sfx:[{at:.4,type:"flap"},{at:1,type:"rise"}]},{id:"ending",start:146.3,dur:10,voice_start:146.8,voice_dur:8.376,subs:[{t0:146.8,t1:148.429,text:"只要心里有梦"},{t0:148.429,t1:149.592,text:"不怕风雪"},{t0:149.592,t1:150.988,text:"也不怕嘲笑"},{t0:150.988,t1:152.151,text:"总有一天"},{t0:152.151,t1:155.176,text:"你也会展开属于自己的翅膀"}],cue:{type:"ending"},sfx:[{at:6.2,type:"seal"}]}],Vu={title:Mu,style:Nu,w:Bu,h:Au,fps:_u,total:Iu,frames:Ru,voice:Ou,scenes:Fu},Du=[{id:"01-egg",scene:"bigegg",from:2.2,dur:3,cover:2.4,caption:"最大的蛋",index:"其一",xhs:"最后一颗蛋最大,好久才裂开,钻出来一只又大又灰的小家伙。"},{id:"02-laugh",scene:"bullied",from:3,dur:3,cover:1,caption:"丑小鸭",index:"其二",xhs:"哥哥姐姐笑话他,公鸡追着啄他,大家都叫他「丑小鸭」。"},{id:"03-swans",scene:"autumn",from:4.6,dur:3,cover:1.6,caption:"仰望",index:"其三",xhs:"秋天傍晚,一群洁白的大鸟飞过,他望着天空,心里说不出的向往。"},{id:"04-snow",scene:"winter",from:6.4,dur:3,cover:1.6,caption:"寒冬",index:"其四",xhs:"雪夜里湖面结了冰,他缩成一团,熬过了一个又一个寒冷的夜晚。"},{id:"05-reflect",scene:"reflection",from:5.4,dur:3,cover:2.4,caption:"倒影",index:"其五",xhs:"他低下头,看见水里的倒影——那是一只美丽的白天鹅。"},{id:"06-fly",scene:"fly",from:2.6,dur:3,cover:1.6,caption:"展翅",index:"其六",xhs:"原来,他本来就是一只天鹅。张开翅膀,飞向高高的蓝天。"}],zu="done",ju="2026-10-17",Wu={w:1080,h:1920,fps:30,dur:156.3,audio:!0},Lu="PASS",qu=[{level:"PASS",check:"竖屏 分辨率",msg:"1080×1920(时间轴 1080×1920)"},{level:"PASS",check:"竖屏 帧率",msg:"30.0 fps"},{level:"PASS",check:"竖屏 时长",msg:"156.30s(时间轴 156.30s)"},{level:"PASS",check:"竖屏 音轨",msg:"有"},{level:"PASS",check:"竖屏 响度",msg:"-14.0 LUFS(目标 −14)"},{level:"PASS",check:"竖屏 真峰值",msg:"-1.4 dBTP(应 ≤ −1)"},{level:"PASS",check:"竖屏 黑场",msg:"无"},{level:"PASS",check:"竖屏 冻结画面",msg:"无"},{level:"PASS",check:"竖屏 文字@title",msg:"OCR 读出 16 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@summer",msg:"OCR 读出 37 字;字幕命中 88%"},{level:"PASS",check:"竖屏 文字@hatch",msg:"OCR 读出 31 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@bigegg",msg:"OCR 读出 33 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@bullied",msg:"OCR 读出 35 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@leave",msg:"OCR 读出 21 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@marsh",msg:"OCR 读出 14 字;字幕命中 86%"},{level:"PASS",check:"竖屏 文字@autumn",msg:"OCR 读出 20 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@winter",msg:"OCR 读出 40 字;字幕命中 91%"},{level:"PASS",check:"竖屏 文字@spring",msg:"OCR 读出 22 字;字幕命中 100%"},{level:"INFO",check:"竖屏 文字@reflection",msg:"OCR 读出 47 字;字幕命中 53%(检查窗口同时计入了上一句字幕;当前字幕「而是一只美丽的白天鹅」已被读出)"},{level:"PASS",check:"竖屏 文字@accepted",msg:"OCR 读出 33 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@fly",msg:"OCR 读出 11 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@ending",msg:"OCR 读出 28 字;字幕命中 100%"},{level:"PASS",check:"横屏 分辨率",msg:"1920×1080(时间轴 1920×1080)"},{level:"PASS",check:"横屏 帧率",msg:"30.0 fps"},{level:"PASS",check:"横屏 时长",msg:"156.30s(时间轴 156.30s)"},{level:"PASS",check:"横屏 音轨",msg:"有"},{level:"PASS",check:"横屏 响度",msg:"-14.0 LUFS(目标 −14)"},{level:"PASS",check:"横屏 真峰值",msg:"-1.4 dBTP(应 ≤ −1)"},{level:"PASS",check:"横屏 黑场",msg:"无"},{level:"PASS",check:"横屏 冻结画面",msg:"无"},{level:"PASS",check:"横屏 字幕@summer",msg:"「安安静静地孵着她的蛋」OCR 命中 88%;墨色/纸带对比度 12.5:1"},{level:"PASS",check:"横屏 字幕@hatch",msg:"「好奇地看着这个世界」OCR 命中 100%;墨色/纸带对比度 12.4:1"},{level:"PASS",check:"横屏 字幕@bigegg",msg:"「一点也不像他的哥哥姐姐」OCR 命中 100%;墨色/纸带对比度 12.7:1"},{level:"PASS",check:"横屏 字幕@bullied",msg:"「连喂鸭的小姑娘也嫌他碍事」OCR 命中 100%;墨色/纸带对比度 13.2:1"},{level:"PASS",check:"横屏 字幕@leave",msg:"「离开了家」OCR 命中 100%;墨色/纸带对比度 3.5:1"},{level:"PASS",check:"横屏 字幕@marsh",msg:"「他吓得躲进芦苇」OCR 命中 0%;墨色/纸带对比度 11.6:1"},{level:"PASS",check:"横屏 字幕@autumn",msg:"「丑小鸭望着他们」OCR 命中 100%;墨色/纸带对比度 11.2:1"},{level:"PASS",check:"横屏 字幕@winter",msg:"「熬过了一个又一个寒冷的夜晚」OCR 命中 91%;墨色/纸带对比度 12.9:1"},{level:"PASS",check:"横屏 字幕@spring",msg:"「翅膀变得这么有力」OCR 命中 100%;墨色/纸带对比度 12.2:1"},{level:"PASS",check:"横屏 字幕@reflection",msg:"「那不是又丑又灰的鸭子而是一只美丽的白天鹅」OCR 命中 53%;墨色/纸带对比度 12.1:1"},{level:"PASS",check:"横屏 字幕@accepted",msg:"「新来的那只天鹅最漂亮」OCR 命中 90%;墨色/纸带对比度 12.9:1"},{level:"PASS",check:"横屏 字幕@fly",msg:"「飞向高高的蓝天」OCR 命中 100%;墨色/纸带对比度 10.2:1"},{level:"PASS",check:"横屏 字幕@ending",msg:"「你也会展开属于自己的翅膀」OCR 命中 100%;墨色/纸带对比度 13.1:1"},{level:"INFO",check:"说明",msg:"横屏字幕位于画面底部 ~92%,vibe qa 的 OCR 只扫 60–84% 高度带,故横屏改用「字幕带 OCR + 墨色/纸带对比度(≥7:1)」补充检查。"},{level:"PASS",check:"确定性",msg:"vibe det:同一帧乱序重渲逐像素一致"},{level:"PASS",check:"实况图",msg:"6 组 JPG+MOV:content identifier 一致、still-image-time 读回正确、MOV 完整解码、哈希校验通过(PhotoKit 本机加载:Linux 环境不可用)"}],$u={status:zu,generated:ju,meta:Wu,verdict:Lu,results:qu},Uu=[[.42,-.04],[.3,-.3],[.02,-.39],[-.3,-.31],[-.56,-.24],[-.5,.02],[-.4,.22],[-.1,.33],[.22,.3],[.41,.15]],Hu=[[.47,-.02],[.32,-.17],[.02,-.24],[-.36,-.22],[-.66,-.36],[-.6,-.08],[-.44,.12],[-.1,.22],[.26,.2],[.46,.1]],dt=(e,t,r)=>[E(e[0],t[0],r),E(e[1],t[1],r)],os=(e,t)=>[e[0]+t[0],e[1]+t[1]],cr=[[0,0],[.32,-.09],[.7,-.07],[1,.04],[.93,.16],[.84,.2],[.76,.27],[.64,.31],[.5,.36],[.3,.4],[.06,.34]];function ao(e,t,r,n,s,i,o,a,l=!1){const f=[-.16*r,-n*r],c=r*(.42+.18*Math.abs(n)),u=[-c,-.12*c*Math.sign(n||1)],h=b=>[t[0]+f[0]*b[0]+u[0]*b[1],t[1]+f[1]*b[0]+u[1]*b[1]],d=cr.map(h);ue(e,d,l?ht(s,"#9a948a",.3):s,a*.94,{edge:.6,lw:i});const m=fe(cr.slice(0,4).map(h),8);z(e,m,i*1.15,{seed:o,taperIn:.1,taperOut:.5,alpha:.9*a});for(let b=3;b<10;b++){const p=h(cr[b]),w=h([cr[b][0]-.2,cr[b][1]-.16]);z(e,[p,dt(p,w,.5),w],i*.55,{seed:o+b,taperIn:.05,taperOut:.7,alpha:.55*a})}z(e,fe(cr.slice(3).map(h),6),i*.7,{seed:o+3,alpha:.6*a,dry:.4})}function Gu(e,t,r){const n=t[0]-e[0],s=t[1]-e[1],i=Math.atan2(-s,Math.abs(n)+1e-6);let o;if(i>.35){const l=$((i-.35)/.9);o=[e[0]+n*E(.35,.02,l),t[1]+Math.abs(s)*E(.15,.32,l)]}else i>-.15?o=[e[0]+n*.5,e[1]+s*.5-.04-.04*r]:o=[e[0]+n*.45,Math.min(e[1],t[1])-E(.08,.3,r)];const a=[];for(let l=0;l<=18;l++){const f=l/18,c=1-f;a.push([c*c*e[0]+2*c*f*o[0]+f*f*t[0],c*c*e[1]+2*c*f*o[1]+f*f*t[1]])}return a}function Se(e,t){const r=t.s,n=Re(t.sw??0),s=t.face??1,i=t.t,o=t.seed??1,a=t.alpha??1;if(a<=.001)return;const l=t.body??"#e9c45a",f=t.head??l,c=t.beak??"#e0862c",u=Math.max(1.2,r*.028*(t.ink??1)),h=t.walk??0,d=i*h*Math.PI*2,m=E(.2,.26,n),b=h?Math.abs(Math.sin(d))*.035:.008*Math.sin(i*2.1+o),p=(t.shiver??0)*.012*Math.sin(i*47+o),w=E(.33,.22,n),y=t.swim?-.02:-(m+w)-b;e.save(),e.globalAlpha=a,e.translate(t.x+p*r,t.y),e.scale(s*r,r),e.rotate(t.tilt??0);const g=u/r,k=t.squash??0;e.translate(0,y),e.scale(1+k*.5,1-k);const T=Uu.map((F,Q)=>dt(F,Hu[Q],n)),A=dt([.24,-.26],[.36,-.12],n);let N=dt([.3,-.56],[.5,-1.12],n);const S=t.neckUp??0;S>0&&(N=os(N,[-.06*S,-E(.08,.22,n)*S])),S<0&&(N=dt(N,dt([.48,-.34],[.92,-.06],n),-S));const B=t.tuck??0;B>0&&(N=dt(N,[-.06,E(-.42,-.3,n)],B)),N=os(N,[.012*mt(i*.9,o),.012*mt(i*1.1,o+3)]);const V=E(.22,.105,n);if(!t.swim&&t.legs!==!1)for(let F=0;F<2;F++){const Q=F===0?1:-1,q=h?Math.sin(d+(F?Math.PI:0))*.35:0,H=[.02+.08*Q,w-.04],K=[H[0]+Math.sin(q)*m,H[1]+Math.cos(q)*m+b-(h&&Math.sin(d+(F?Math.PI:0))>0?.03:0)];z(e,[H,dt(H,K,.5),K],g*1.3,{color:"#c8731f",alpha:.95,taperOut:.1,seed:o+F}),e.fillStyle=P("#e08a2c",.9),e.beginPath(),e.moveTo(K[0]-.04,K[1]),e.lineTo(K[0]+.11,K[1]+.012),e.lineTo(K[0]+.02,K[1]-.035),e.closePath(),e.fill()}const W=t.wing??0,L=t.flap??0,J=E(.75,1.55,n),U=[.08,-w*.7],I=W*(t.flap===void 0?.85:Math.sin(L));W>.01&&I>-.2&&ao(e,os(U,[.07,-.03]),J*.95,Math.min(1,I*.92+.08),l,g,o+50,1,!0);const j=Gu(A,N,n),Z=E(.3,.1,n),re=(F,Q=1)=>j.map((q,H)=>{const K=j[Math.max(0,H-1)],be=j[Math.min(j.length-1,H+1)];let we=be[0]-K[0],Be=be[1]-K[1];const je=Math.hypot(we,Be)||1;we/=je,Be/=je;const at=H/(j.length-1),ar=Z*.5*Q*E(1.35,.9,$(at));return[q[0]-Be*ar*F,q[1]+we*ar*F]});{const F=re(1),Q=re(-1);e.fillStyle=P(f===l?l:ht(l,f,.5),.96),e.beginPath(),e.moveTo(F[0][0],F[0][1]);for(const q of F.slice(1))e.lineTo(q[0],q[1]);for(const q of Q.slice().reverse())e.lineTo(q[0],q[1]);e.closePath(),e.fill(),e.beginPath(),e.arc(A[0],A[1],Z*.68,0,Math.PI*2),e.fill()}if(ue(e,T,l,.94,{dx:.012,dy:.01,edge:1.2,lw:g}),e.save(),$t(e,fe(T,8,!0),!0),e.clip(),le(e,-.1,w*.8,.5,ht(l,"#5a4a38",.5),.22,1.2,.6),t.glow&&le(e,.05,-.1,.7,"#fffdf4",.5*t.glow),e.restore(),e.fillStyle=P(f,.96),e.beginPath(),e.arc(N[0],N[1],V,0,Math.PI*2),e.fill(),W>.01)ao(e,U,J,I,l,g,o+60,1);else{const F=[[.18,-.12],[-.05,-E(.2,.16,n)],[-.38,-E(.12,.2,n)],[-E(.42,.6,n),-E(.02,.22,n)],[-.2,E(.12,.08,n)],[.1,.06]];ue(e,F,ht(l,"#6b6255",.12),.55,{edge:.8,lw:g});const Q=fe(F,8,!0);z(e,gr([...Q,Q[0]],.05,.62),g*.85,{seed:o+9,alpha:.75,taperIn:.2});for(let q=0;q<3;q++){const H=[-.12-q*.1,-.02+q*.01],K=[-.3-q*.1,-.1-q*.02*(1-n)-n*.05];z(e,[H,dt(H,K,.5),K],g*.55,{seed:o+20+q,alpha:.5})}}if(st(e,T,g*1.2,{seed:o,alpha:.88,breaks:[.02,.33,.62,.98]}),n>.15){const F=.85*$(n*2);z(e,gr(re(1),.1,.97),g*.95,{seed:o+31,alpha:F,taperIn:.15,taperOut:.2}),z(e,gr(re(-1),.18,.92),g*.8,{seed:o+32,alpha:F*.75,taperIn:.2,taperOut:.3})}const ie=[];for(let F=0;F<12;F++){const Q=F/12*Math.PI*2;ie.push([N[0]+Math.cos(Q)*V,N[1]+Math.sin(Q)*V])}st(e,ie,g*1.05,{seed:o+40,alpha:.85,breaks:[.55,.95,1.4].map(F=>F%1).sort()});const O=t.fluff??(n<.3?.4:0);if(O>0){const F=de(o*77),Q=Math.round(6+O*10);for(let q=0;q0){const F=nr(-.1,-w-.02+.06*(1-t.snow),.26*t.snow+.02,o+5,.25,18).map(Q=>[Q[0]*1.5,Q[1]*.55-w*.45]);ue(e,F,"#ffffff",.92,{edge:.2,lw:g})}if(B<.85){const F=t.look??0;let Q=E(.12,.32,n)+F;S>0&&(Q-=.35*S),S<0&&(Q+=.9*-S);const q=E(.2,.2,n),H=E(.085,.05,n),K=N[0]+Math.cos(Q)*V*.85,be=N[1]+Math.sin(Q)*V*.85,we=(t.mouth??0)*.35;e.save(),e.translate(K,be),e.rotate(Q),e.fillStyle=P(c,.97),e.save(),e.rotate(-we*.6),e.beginPath(),e.moveTo(-.02,-H),e.quadraticCurveTo(q*.6,-H*1.1,q,-H*.05),e.lineTo(-.02,H*.2),e.closePath(),e.fill(),e.strokeStyle=P(G,.8),e.lineWidth=g*.7,e.stroke(),n>.6&&(e.fillStyle=P(G,.9*$((n-.6)*3)),e.beginPath(),e.ellipse(0,-H*.45,.035,H*.7,0,0,Math.PI*2),e.fill()),e.restore(),e.save(),e.rotate(we),e.fillStyle=P(ht(c,"#8a4a1a",.2),.97),e.beginPath(),e.moveTo(-.02,H*.15),e.quadraticCurveTo(q*.55,H*.9,q*.9,H*.1),e.lineTo(-.02,H*.8),e.closePath(),e.fill(),e.strokeStyle=P(G,.7),e.lineWidth=g*.6,e.stroke(),e.restore(),e.restore();const Be=N[0]+Math.cos(Q-.95)*V*.42,je=N[1]+Math.sin(Q-.95)*V*.42,at=E(.045,.022,n);!t.noBlink&&(i+o*.37)%3.4<.13||B>.5?(e.strokeStyle=P(G,.9),e.lineWidth=g*.9,e.beginPath(),e.arc(Be,je-at*.3,at,.2,Math.PI-.2),e.stroke()):(e.fillStyle=P(G,.95),e.beginPath(),e.arc(Be,je,at,0,Math.PI*2),e.fill(),e.fillStyle=P(Ge,.95),e.beginPath(),e.arc(Be+at*.3,je-at*.35,at*.33,0,Math.PI*2),e.fill())}e.restore()}function Xu(e,t,r,n,s,i=1,o=.6){for(let a=0;a<3;a++){const l=(s*.6+a/3+oe(i))%1,f=n*(.55+l*.9);e.strokeStyle=P(G,o*(1-l)*.7),e.lineWidth=Math.max(1,n*.012),e.beginPath(),e.ellipse(t,r+n*.01,f,f*.08,0,Math.PI*.05,Math.PI*.95),e.stroke()}z(e,[[t-n*.62,r],[t,r+n*.015],[t+n*.62,r]],n*.02,{seed:i,alpha:o*.8,jitter:.5})}function Qu(e,t,r,n,s,i,o="#5d7180"){const a=Math.ceil(r.w),l=Math.ceil(r.h);if(a<2||l<2)return;const f=typeof OffscreenCanvas<"u"?new OffscreenCanvas(a,l):Object.assign(document.createElement("canvas"),{width:a,height:l}),c=f.getContext("2d");c.translate(-r.x,-r.y),t(c),c.setTransform(1,0,0,1,0,0),c.globalCompositeOperation="source-atop",c.fillStyle=P(o,.18),c.fillRect(0,0,a,l);const u=Math.max(8,Math.round(l/6)),h=l/u;e.save();for(let d=0;d[t+l[0],r+l[1]*.38]);ue(e,a,i,o),st(e,a,Math.max(1.2,n*.03),{seed:s,alpha:.55,breaks:[.1,.45,.9]});for(let l=0;l<6;l++){const f=l/6*Math.PI*2+s;z(e,[[t,r],[t+Math.cos(f)*n*.45,r+Math.sin(f)*n*.17],[t+Math.cos(f)*n*.85,r+Math.sin(f)*n*.32]],Math.max(1,n*.02),{color:"#3e5a35",alpha:.4,seed:s+l})}}function lr(e,t,r,n,s={}){const i=s.seed??1,o=s.color??"#f4ecd6",a=s.crack??0,l=s.open??0;e.save(),e.translate(t,r),e.rotate(s.wobble??0);const f=[];for(let c=0;c<18;c++){const u=c/18*Math.PI*2,h=Math.sin(u)<0?1.28:1;f.push([Math.cos(u)*n*.82,Math.sin(u)*n*h-n*.95])}if(l<.02)ue(e,f,o,.95),le(e,-n*.25,-n*1.25,n*.4,"#ffffff",.5),st(e,f,Math.max(1.2,n*.07),{seed:i,alpha:.8});else{const u=[...f.filter(p=>p[1]>-n*1.05).sort((p,w)=>Math.atan2(p[1]+n,p[0])-Math.atan2(w[1]+n,w[0]))],h=[];for(let p=0;p<=6;p++)h.push([E(n*.82,-n*.82,p/6),-n*1.05+(p%2?-n*.16:n*.05)]);const d=[...u,...h];e.fillStyle=P(o,.95),$t(e,d,!0),e.fill(),e.strokeStyle=P(G,.8),e.lineWidth=Math.max(1.2,n*.06),$t(e,d,!0),e.stroke();const m=l;e.save(),e.translate(n*.3*m,-n*1.2-n*1.4*m),e.rotate(.9*m);const b=[...h.slice().reverse().map(p=>[p[0],p[1]+n*1.05]),[-n*.6,-n*.6],[0,-n*.95],[n*.6,-n*.6]];e.globalAlpha*=Re(1.4-m),e.fillStyle=P(o,.95),$t(e,b,!0),e.fill(),e.strokeStyle=P(G,.8),$t(e,b,!0),e.stroke(),e.restore()}if(a>0&&l<.02){const c=[];for(let h=0;h<=7;h++)c.push([E(-n*.75,n*.75,h/7),-n*1+(h%2?-n*.14:n*.08)+n*.04*mt(h,i)]);z(e,c,Math.max(1.2,n*.06),{seed:i+3,alpha:.9,grow:a,taperIn:.05,taperOut:.1})}e.restore()}function as(e,t,r,n,s){const i=de(s);le(e,t,r-n*.05,n*.6,"#7a5a34",.35,1,.35);for(let o=0;o<26;o++){const a=i()*Math.PI,l=a+.6+i()*.8,f=n*(.42+i()*.12),c=u=>[t+Math.cos(u)*f,r-n*.04+Math.sin(u)*f*.28];z(e,[c(a),c((a+l)/2),c(l)],Math.max(1.2,n*.012),{color:o%3?"#6b4a26":"#a07a40",alpha:.75,seed:s+o,dry:.3})}}function lo(e,t,r,n,s,i,o=-1,a=0){const l=de(i),f=s*.42;for(let c=t;c=0&&Math.abs(c-o)=0?(z(e,[[t,u],[(t+o-a/2)/2,u+3],[o-a/2,u]],s*.045,{color:"#5a4630",alpha:.8,seed:i+c*9,dry:.5}),z(e,[[o+a/2,u],[(r+o+a/2)/2,u-2],[r,u]],s*.045,{color:"#5a4630",alpha:.8,seed:i+c*11,dry:.5})):z(e,[[t,u],[(t+r)/2,u+4],[r,u]],s*.045,{color:"#5a4630",alpha:.8,seed:i+c*9,dry:.5})}}function Fr(e,t,r,n,s,i={}){const o=de(s),a=i.color??"#2a2420",l=i.t??0,f=(c,u,h,d,m,b)=>{var N;const p=.015*Math.sin(l*.8+b+s)*(4-b),w=h+p,y=c+Math.cos(w)*d,g=u+Math.sin(w)*d,k=(c+y)/2+(o()-.5)*d*.25,T=(u+g)/2+(o()-.5)*d*.15;if(z(e,fe([[c,u],[k,T],[y,g]],8),m,{color:a,alpha:.9,seed:s+b*13+d,taperIn:.02,taperOut:b>=(i.depth??4)?.9:.25,dry:.35,jitter:.3}),b>=(i.depth??4)){(N=i.leaves)==null||N.call(i,y,g,d);return}const A=b<2?2:2+(o()<.4?1:0);for(let S=0;S0)for(let w=6;we.u*(e.portrait?.95:1),lt=(e,t,r,n,s={})=>({z:t*(e.portrait?1.25:1),x:r,y:n,...s});function et(e,t,r,n,s,i,o){const a=e.createLinearGradient(0,0,0,r);a.addColorStop(0,P(n,s)),a.addColorStop(1,P(i,o)),e.fillStyle=a,e.fillRect(0,0,t,r)}function De(e,t,r,n,s,i,o,a=18){const l=[];for(let c=0;c<=24;c++)l.push([c/24*t,r+mt(c*.6,o)*a]);const f=e.createLinearGradient(0,r-a,0,n);f.addColorStop(0,P(s,i)),f.addColorStop(1,P(s,i*.45)),e.fillStyle=f,e.beginPath(),e.moveTo(0,n);for(const c of l)e.lineTo(c[0],c[1]);e.lineTo(t,n),e.closePath(),e.fill(),z(e,l,2.2,{color:s,alpha:Math.min(1,i*1.3),seed:o,jitter:.6,dry:.5})}function Mt(e,t,r,n,s,i,o,a,l){const f=de(a);for(let c=0;c{const d=(h-(f.length-1)/2)*s*c,m=l.vertical?r:r+d,b=l.vertical?n+d:n,p=M(i+h*o,i+h*o+o*1.2,a);p<=0||(e.save(),e.beginPath(),e.rect(m-s*.7,b-s*.7,s*1.4,s*1.4*me(p)),e.clip(),l.bleed!==0&&(e.shadowColor=P(G,.35),e.shadowBlur=s*.06),e.fillStyle=l.color??G,e.fillText(u,m,b),e.restore())}),e.restore()}function fr(e,t,r,n,s={}){return{x:e,y:t,s:r,t:n,sw:1,body:pr,beak:br,swim:!0,...s}}function Zu(e,t,r,n,s,i=G){s<=0||(e.strokeStyle=P(i,s),e.lineWidth=Math.max(1,n*.012),e.beginPath(),e.ellipse(t,r,n,n*.16,0,0,Math.PI*2),e.stroke())}function Pt(e,t,r=!0){e.save(),e.beginPath(),e.rect(t.x-t.s*3,t.y-t.s*4,t.s*6,t.s*4+t.s*.06),e.clip(),Se(e,t),e.restore(),r&&Xu(e,t.x,t.y+t.s*.06,t.s,t.t,t.seed??1,.5*(t.alpha??1))}function on(e,t,r,n,s,i,o,a,l=70){Oe(s,i,(f,c)=>{const u=f()*t*1.2-t*.1,h=f()*r,d=(.6+f()*.8)*l*o,m=(20+f()*50)*o,b=(h+n*d)%(r+60*o)-30*o,p=u+Math.sin(n*(.8+f())+c)*m+n*12*o;a((p%(t+40*o)+t+40*o)%(t+40*o)-20*o,b,n*(1+f()*2)+c,f,c)})}function qr(e){return[e.X(.08),e.gy+70*e.u]}function po(e){const{g:t,W:r,H:n,u:s,gy:i}=e;et(t,r,i,"#a9c3c9",.16,"#ffffff",0),Ee(t,r,i-90*s,120*s,"#8c9a96",.25,11,{soft:1});for(let l=0;l<26;l++){const f=l/26*r+oe(l)*40*s;ut(t,f,i-20*s,(120+oe(l*3)*110)*s,0,300+l,{color:"#7d8a76",alpha:.35,head:!0,wind:.3})}De(t,r,i-30*s,n,"#a8b878",.32,4),Mt(t,r,i,n,40,"#6f8a52",.35,9,s);const[o,a]=qr(e);le(t,o,a+30*s,380*s,"#7a8a50",.18,1.3,.35)}const Ju={title:{label:"片头",draw(e){const{g:t,W:r,H:n,u:s,t:i,portrait:o}=e,a=r/2,l=o?n*.37:n*.43;if(i<.32){const p=E(-30*s,l,yr(i/.32));t.fillStyle=P(G,.95),t.beginPath(),t.ellipse(a,p,9*s,15*s,0,0,Math.PI*2),t.fill()}const f=me(M(.3,2.4,i));if(f>0){const p=(o?420:350)*s*f;ue(t,nr(a,l,p,5,.05,32),"#a39c90",.14+.18*(1-f),{edge:1.4}),le(t,a,l,p*.85,"#8a8276",.12*f),Oe(10,77,(w,y)=>{const g=w()*Math.PI*2,k=(p*.9+60*s*w())*me(M(.3,.8,i)),T=(3+w()*8)*s;t.fillStyle=P(G,.6*(1-.6*f)),t.beginPath(),t.arc(a+Math.cos(g)*k,l+Math.sin(g)*k*.9,T,0,Math.PI*2),t.fill()})}const c=$(M(.8,2.6,i));if(c>0){t.save(),t.globalAlpha=c;const p=o?n*.9:n*.95;Dt(t,r*.1,p,150*s,3),Dt(t,r*.24,p+30*s,110*s,5,"#7fa070");for(let w=0;w<9;w++)ut(t,r*(.8+w*.025),p+20*s,(260+oe(w)*200)*s*(o?1.2:1),i,40+w);t.restore()}const u=(o?230:210)*s;mo(t,"丑小鸭",a,l,u,1.1,.45,i,{vertical:o});const h=$(M(2.9,3.6,i));if(h>0){t.save(),t.globalAlpha=h,t.font=`${46*s}px ${xr}`,t.fillStyle=P(G,.85),t.textAlign="center",t.textBaseline="middle";const p=o?l+u*1.95:l+u*.95;t.fillText("安 徒 生 童 话",a,p),z(t,[[a-170*s,p+38*s],[a,p+44*s],[a+170*s,p+36*s]],4*s,{alpha:.5*h,seed:8,dry:.6}),t.restore()}const d=M(4.55,4.85,i);if(d>0){const p=E(1.5,1,me(d)),w=o?a+u*.78:a+u*1.82,y=o?l+u*1.15:l+u*.32;t.save(),t.translate(w,y),t.scale(p,p),t.rotate(-.04),Hr(t,0,0,92*s,["童话"],{alpha:Math.min(1,d*2)}),t.restore()}const m=o?n*.74:n*.86,b=M(2,6.6,i);if(b>0){const p=E(-120*s,r*.62,b);Se(t,{x:p,y:m,s:70*s,t:i,walk:2.4,body:sn,seed:3}),Se(t,{x:p-120*s,y:m+6*s,s:92*s,t:i+.2,walk:2,body:Kt,beak:Yt,fluff:1,sw:.1,seed:9,neckUp:-.2})}},enterAt:[.5,.45]},summer:{label:"夏日乡村",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o,portrait:a}=e;et(t,r,i,"#9fbccb",.2,"#ffffff",0),rr(t,o(a?-.55:-.7),n*(a?.13:.17),58*s),Ee(t,r,i-110*s,150*s,"#7d8a8c",.32,3),Ee(t,r,i-40*s,110*s,"#56645c",.42,7),De(t,r,i-70*s,i+40*s,"#d9b45a",.42,21,14*s),Oe(260,5,h=>{const d=h()*r,m=i-60*s+h()*90*s,b=(14+h()*26)*s;z(t,[[d,m+b],[d+2*s,m+b*.5],[d+4*s,m]],2.4*s,{color:h()<.5?"#b88a2e":"#d8a940",alpha:.55,seed:Math.floor(d),taperIn:.05})}),De(t,r,i+35*s,n,"#9fb07a",.3,31);const l=o(-.15),f=i+190*s,c=Math.min(r*.45,720*s),u=120*s;ue(t,nr(l,f,1,4,.04,26).map(([h,d])=>[l+(h-l)*c,f+(d-f)*u]),"#94adb5",.4,{edge:1.5}),Mt(t,r*.9,f-u*.6,f+u*.7,18,"#4f6670",.35,41,s),Dt(t,l-c*.6,f+30*s,70*s,1),Dt(t,l-c*.35,f+70*s,55*s,2),Dt(t,l+c*.1,f+10*s,60*s,3),ls(t,l-c*.48,f-30*s,22*s,4,"#e7a0b0"),Fr(t,o(a?-.95:-.88),i+40*s,560*s,17,{color:"#3a3128",depth:4,leaves:(h,d,m)=>{le(t,h,d,m*1.4,"#5f8a4c",.32),le(t,h+m*.4,d-m*.3,m*.9,"#86a85e",.3)}})},draw(e){const{g:t,W:r,u:n,t:s,gy:i,X:o}=e,a=Ye(e);Wr(t,o(-.2)+s*14*n,i-430*n,420*n,2,.3),Wr(t,o(.5)+s*9*n,i-520*n,300*n,5,.25);const l=o(.55),f=i+95*n;for(let h=0;h<12;h++)ut(t,l-220*n+h*42*n+oe(h)*20*n,f-10*n+oe(h+3)*30*n,(260+oe(h*7)*180)*n,s,70+h);as(t,l,f+10*n,270*a,3),lr(t,l+95*a,f+4*n,30*a,{seed:2}),Se(t,{x:l,y:f-8*n,s:230*a,t:s,sw:.34,body:"#f7f2e6",swim:!0,face:-1,seed:5,neckUp:.15+.15*Math.sin(s*.8),look:.15*Math.sin(s*.5),fluff:0}),lr(t,l-100*a,f+12*n,28*a,{seed:4});for(let h=0;h<5;h++)ut(t,l-160*n+h*85*n,f+60*n,(150+oe(h*11)*90)*n,s,90+h,{alpha:.85});const c=o(.05)+280*n*Math.sin(s*.55),u=i-140*n+60*n*Math.sin(s*1.3);t.save(),t.translate(c,u),t.rotate(.15*Math.sin(s)),z(t,[[-28*n,0],[0,0],[26*n,1*n]],6*n,{color:"#b8322a",alpha:.9,taperIn:.05,taperOut:.9}),t.fillStyle=P("#8aa0a8",.35);for(const h of[-1,1])for(const d of[0,9*n])t.beginPath(),t.ellipse(2*n-d,h*(13*n+3*n*Math.sin(s*40)),7*n,16*n,h*.3,0,Math.PI*2),t.fill();t.restore();for(let h=0;h<2;h++){const d=o(-.5+h*.35)+120*n*Math.sin(s*.7+h*2),m=i-60*n+50*n*Math.sin(s*1.1+h),b=Math.abs(Math.sin(s*9+h));t.fillStyle=P(h?"#e0a040":"#f1f0ea",.85);for(const p of[-1,1])t.beginPath(),t.ellipse(d+p*9*n*b,m,9*n*b+1,12*n,p*.4,0,Math.PI*2),t.fill();t.strokeStyle=P(G,.6),t.lineWidth=1.5*n;for(const p of[-1,1])t.beginPath(),t.ellipse(d+p*9*n*b,m,9*n*b+1,12*n,p*.4,0,Math.PI*2),t.stroke()}},cam(e){const t=ne(M(0,e.D,e.t));return lt(e,E(1,1.4,t),E(e.W/2,e.X(.52),t),E(e.H/2,e.gy+20*e.u,t))},enterAt:[.5,.4]},hatch:{label:"破壳",bg:po,draw(e){const{g:t,u:r,t:n,X:s}=e,i=Ye(e),[o,a]=qr(e);Se(t,{x:s(-.6),y:a+60*r,s:270*i,t:n,sw:.34,body:"#f7f2e6",face:1,seed:5,neckUp:-.25+.1*Math.sin(n*.7),fluff:0}),as(t,o,a+10*r,470*i,8);const l=[[-150,-6],[-70,8],[110,10],[180,-6]];lr(t,o+20*i,a-2*r,62*i,{color:"#e6e3d6",seed:99,wobble:n>e.D-2.5?.04*Math.sin(n*13):0});const f=[[s(-.38),a+150*r],[s(-.2),a+175*r],[s(.32),a+165*r],[s(.5),a+140*r]];l.forEach(([c,u],h)=>{const d=o+c*i,m=a+u*r,b=.4+h*.75,p=M(b,b+.35,n),w=me(M(b+.55,b+1.05,n));if(lr(t,d,m,40*i,{crack:p,open:w,seed:10+h,wobble:p>0&&w===0?.08*Math.sin(n*30):0}),w>0){const y=ni(M(b+.55,b+1.3,n)),g=M(b+2,b+3,n),[k,T]=f[h],A=E(d,k,ne(g)),N=E(m+10*r,T,ne(g))-Math.sin(Math.PI*g)*70*r,S=(n-b)%2.3;Se(t,{x:A,y:N+(1-y)*40*r,s:88*i*E(.6,1,y),t:n,body:sn,seed:20+h,face:h<2?-1:1,walk:g>0&&g<1?3:0,mouth:S>1&&S<1.25?1:0,squash:(1-y)*.2,neckUp:.15,legs:g>0}),g>=1&&S>1&&S<1.5&&Lr(t,A+(h<2?-1:1)*40*r,N-95*r,40*r,1-(S-1)/.5,h,h<2?-1:1)}})},cam(e){const[t,r]=qr(e);return lt(e,E(1.3,1.42,ne(e.t/e.D)),t-20*e.u,r+30*e.u)},enterAt:[.55,.65]},bigegg:{label:"最大的蛋",bg:po,draw(e){const{g:t,u:r,t:n,X:s}=e,i=Ye(e),[o,a]=qr(e),l=ee.bigegg,f=$(M(l.surprise,l.surprise+.3,n))*(1-$(M(l.surprise+2.2,l.surprise+3.2,n)));Se(t,{x:s(-.62),y:a+60*r,s:270*i,t:n,sw:.34,body:"#f7f2e6",face:1,seed:5,neckUp:E(-.2,.75,f),fluff:0}),as(t,o,a+10*r,470*i,8),[[-150,-6],[-70,8],[110,10],[180,-6]].forEach(([w,y],g)=>lr(t,o+w*i,a+y*r,40*i,{open:1,seed:10+g}));const c=.12*$(M(.3,l.crackFrom,n)),u=Math.sin(n*14)*c,h=o+20*i,d=a-2*r,m=M(l.crackFrom,l.open-.05,n),b=me(M(l.open,l.open+.5,n));lr(t,h,d-(b?0:Math.abs(Math.sin(n*7))*8*r*c*6),62*i,{color:"#e6e3d6",crack:m,open:b,seed:99,wobble:b?0:u});const p=M(l.walk,l.walk+2.6,n);if(b>0){const w=ni(M(l.open+.1,l.open+1,n)),y=E(h,s(-.02),ne(p)),g=E(d+8*r,a+175*r,ne(p))-Math.sin(Math.PI*Re(p*3))*40*r;Se(t,{x:y,y:g+(1-w)*60*r,s:132*i*E(.7,1,w),t:n,sw:.12,body:Kt,beak:Yt,fluff:1.1,seed:7,face:-1,squash:(1-w)*.25,legs:p>0,walk:p>0&&p<1?1.6:0,tilt:n>l.question&&nl.question&&n0&&p<1?1.2:0}),uo(t,g+10*r,k-150*r,46*r,$(M(l.surprise,l.surprise+.15,n))*(1-$(M(l.surprise+1.4,l.surprise+1.8,n))),w)}},cam(e){const[t,r]=qr(e),n=ne(M(0,ee.bigegg.open+.6,e.t)),s=ne(M(ee.bigegg.walk-.4,ee.bigegg.walk+2.4,e.t));return lt(e,E(E(1.35,1.9,n),1.35,s),E(t+20*e.u,e.X(-.18),s),r+E(-30,70,s)*e.u)},enterAt:[.55,.62]},bullied:{label:"被嘲笑",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#c9c2b0",.18,"#ffffff",0);const a=o(.78),l=i-360*s;ue(t,[[a-260*s,l+40*s],[a+400*s,l-40*s],[a+420*s,l+70*s],[a-240*s,l+120*s]],"#4a4a4f",.6);for(let u=0;u<12;u++)z(t,[[a-240*s+u*55*s,l+115*s-u*6*s],[a-250*s+u*55*s,l+40*s-u*6*s]],4*s,{alpha:.5,seed:u});ue(t,[[a-200*s,l+115*s],[a+400*s,l+65*s],[a+400*s,i-10*s],[a-200*s,i-10*s]],"#b3a48a",.4);const f=o(-.78),c=i-10*s;ue(t,[[f-150*s,c],[f-110*s,c-150*s],[f,c-210*s],[f+110*s,c-150*s],[f+150*s,c]],"#d6b060",.55),Oe(60,12,u=>{const h=u()*Math.PI-Math.PI,d=u()*140*s,m=f+Math.cos(h)*d,b=c-20*s+Math.sin(h)*d;z(t,[[m,b],[m+18*s*(u()-.5),b-25*s]],2*s,{color:"#8a6a2a",alpha:.6})}),De(t,r,i-20*s,n,"#c9b48a",.32,51),lo(t,-20*s,r+20*s,i+10*s,160*s,5),Mt(t,r,i+20*s,n,34,"#8a7450",.3,13,s)},draw(e){const{g:t,u:r,t:n,gy:s,X:i}=e,o=Ye(e),a=ee.bullied,l=n>a.laugh1[0]&&na.laugh2[0]&&n0&&co(t,E(i(-1.15),i(-.78),me(f)),s+200*r,360*o,n,{face:1,pose:n>a.shoo?"shoo":"basket",dress:"#b8483e",seed:4});for(let p=0;p<3;p++){const w=i(-.52)+p*85*r,y=s+150*r+p%2*20*r,g=l?Math.abs(Math.sin(n*9+p)):0;Se(t,{x:w,y,s:92*o,t:n+p,body:sn,seed:40+p,face:1,mouth:l?.4+.6*g:0,squash:.06*g,tilt:l?-.15:0,neckUp:l?.3:0}),l&&Lr(t,w+50*r,y-120*r,36*r,.8,p)}const c=M(a.roosterIn,a.roosterIn+.6,n),u=a.pecks;let h=0,d=0;for(const p of u){const w=M(p-.12,p+.12,n);h=Math.max(h,Math.sin(Math.PI*w)),n>p&&(d+=1)}const m=i(.1)-d*30*r,b=n>a.shoo?.2:.08;if(Se(t,{x:m,y:s+170*r,s:140*o,t:n,sw:.16,body:Kt,beak:Yt,fluff:1.1,seed:7,face:n>a.girlIn?-1:1,neckUp:-.45,shiver:n>a.pecks[0]?1:.4,squash:b}),yn(t,m+(n>a.girlIn?-1:1)*40*r,s+60*r+n*30%30*r,9*r,$(M(1.2,1.5,n))*(1-$(M(3.4,3.7,n)))),yn(t,m+(n>a.girlIn?-1:1)*40*r,s+60*r+n*30%30*r,9*r,$(M(7.6,7.9,n))*(1-$(M(9.6,10,n)))),c>0){const p=E(i(1.3),i(.42),me(c))-(n>a.pecks[3]+.3?(n-a.pecks[3]-.3)*20*r:0);Ku(t,p,s+175*r,240*o,n,{face:-1,peck:h,flap:n>a.roosterIn+.6&&n{const y=n-p;if(!(y<0||y>2))for(let g=0;g<3;g++){const k=m+30*r+(g-1)*30*r+Math.sin(y*4+g)*20*r,T=s+40*r-80*r*me(y*2)+y*y*40*r;Wt(t,k,T,10*r,y*3+g+w,"#9a968e",.8*(1-y/2))}})},cam(e){return lt(e,E(1.28,1.36,ne(e.t/e.D)),e.X(-.08),e.gy+110*e.u)},enterAt:[.3,.6]},leave:{label:"离家",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#b9a48a",.2,"#ffffff",0),Ee(t,r,i-70*s,140*s,"#8a8478",.3,23),Ee(t,r,i-20*s,90*s,"#6a665e",.35,29),De(t,r,i-20*s,n,"#b8ad8a",.28,61);const a=[[o(-.75),n+20*s],[o(-.42),i+170*s],[o(-.05),i+100*s],[o(.35),i+30*s],[o(.68),i-25*s]],l=fe(a,10);for(let f=0;fle(t,f,c,u*.8,"#8a7a50",.25)}),lo(t,-20*s,o(-.12),i+120*s,170*s,8,o(-.42),120*s),Mt(t,r,i+30*s,n,30,"#7a6a48",.28,15,s)},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a,D:l}=e,f=Math.pow(M(ee.leave.walk,l+1.2,i),1.15),c=fe([[a(-.62),o+200*s],[a(-.42),o+175*s],[a(-.05),o+100*s],[a(.35),o+32*s],[a(.62),o-20*s]],12),u=Math.min(c.length-1,Math.floor(f*(c.length-1))),[h,d]=c[u],m=E(150,52,f)*Ye(e);Se(t,{x:h,y:d,s:m,t:i,sw:.18,body:Kt,beak:Yt,fluff:1,seed:7,face:1,walk:i>ee.leave.walk?1.7:0,neckUp:-.35,shiver:iWt(t,b,p*.8,(8+y()*6)*s,w,y()<.5?"#b08a4a":"#8a7a50",.75),50)},cam(e){return lt(e,E(1.08,1.16,e.t/e.D),e.W/2,e.gy+60*e.u)},enterAt:[.25,.7]},marsh:{label:"沼泽",bg(e){const{g:t,W:r,H:n,u:s,gy:i}=e;et(t,r,i,"#7e878a",.25,"#ffffff",.02),Ee(t,r,i-50*s,100*s,"#6e7672",.3,37,{soft:1});for(let o=0;o<46;o++)ut(t,o/46*r+oe(o)*30*s,i-10*s,(90+oe(o*3)*90)*s,0,500+o,{color:"#5d6658",alpha:.4,wind:.2});De(t,r,i-15*s,n,"#8fa3a6",.32,71,6*s),Oe(8,19,o=>le(t,o()*r,i+o()*(n-i),(80+o()*160)*s,"#5d6a62",.12,2.2,.4)),Mt(t,r,i+20*s,n,36,"#3f4c52",.35,17,s)},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a}=e,l=Ye(e),f=ee.marsh;fo(t,0,r,o+10*s,n-o,i,3,{alpha:.3,lines:12});for(let m=0;m<12;m++)ut(t,a(.62)+m*38*s,o+40*s+oe(m)*30*s,(280+oe(m*5)*160)*s,i,120+m,{wind:i>f.shots[0]?1+2*Math.exp(-(i-f.shots[0])*2):1});for(const[m,b,p]of[[f.shots[0],.86,-130],[f.shots[1],.72,-170]]){const w=i-m;w>0&&w<.09&&(t.fillStyle=P("#e0a040",.9),t.beginPath(),t.arc(a(b),o+p*s,26*s,0,Math.PI*2),t.fill()),w>0&&Yu(t,a(b)-w*30*s,o+p*s-w*25*s,(30+110*me(w/2))*s,.6*Math.max(0,1-w/4),Math.floor(m*10))}for(let m=0;m<3;m++){const b=yr(M(f.scatter+m*.12,f.scatter+2.1+m*.12,i)),p=a(.2+m*.16)+b*380*s,w=o+(110+m*25)*s-b*(o+300*s),y=i>f.laugh[0]&&i0?1:-1,mouth:y?.5+.5*Math.abs(Math.sin(i*9+m)):0,fluff:0};b>0?Se(t,{...g,legs:!1,wing:1,flap:i*16+m,neckUp:-.7}):(Pt(t,g),y&&Lr(t,p-70*s,w-150*s,34*s,.8,m,-1))}Oe(7,33,(m,b)=>{const p=i-f.scatter+.1-m()*.4;p<=0||wn(t,a(.8)-p*(120+m()*120)*s,o-120*s-p*(150+m()*120)*s,(18+m()*12)*s,i*14+b)});const c=ne(M(f.hide,f.hide+1.4,i)),u=Math.exp(-Math.max(0,i-f.shots[0])*5)*(i>f.shots[0]?1:0),h=E(a(-.08),a(-.55),c),d=o+175*s;Pt(t,{x:h,y:d,s:150*l,t:i,sw:.24,body:Kt,beak:Yt,fluff:1,seed:7,face:c>0&&c<1?-1:1,neckUp:E(0,-.6,c),tuck:.3*c,shiver:i>f.shots[0]?1:0,squash:u*.2}),uo(t,h+20*s,d-220*s,50*s,u>.05?1:0,3);for(let m=0;m<9;m++)ut(t,a(-.78)+m*40*s+oe(m*9)*20*s,n+20*s,(420+oe(m*13)*220)*s,i,150+m,{color:"#2f3b2c",alpha:.85,wind:i>f.shots[0]?1+1.5*Math.exp(-(i-f.shots[0])):1})},cam(e){const{t,u:r}=e;let n=0;for(const i of ee.marsh.shots){const o=t-i;o>0&&(n+=Math.sin(o*90)*10*r*Math.exp(-o*9))}const s=ne(M(9.2,e.D,t));return lt(e,E(1.15,1.55,s),E(e.X(.1),e.X(-.52),s),e.gy+E(90,120,s)*r,{dx:n,dy:n*.5})},enterAt:[.5,.6]},autumn:{label:"秋天的大鸟",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#e3a868",.12,"#e8b878",.38),rr(t,o(.3),i-150*s,72*s,"#c8463a",.85),Ee(t,r,i-40*s,170*s,"#8a7a6a",.32,41),Ee(t,r,i,100*s,"#5e5248",.45,47),De(t,r,i,i+190*s,"#b8a990",.32,81,4*s),Oe(9,3,a=>{const l=i+20*s+a()*120*s,f=(80-(l-i)/s*.3)*s;z(t,[[o(.3)-f,l],[o(.3),l+2*s],[o(.3)+f,l]],5*s,{color:"#c8463a",alpha:.35,dry:.6,seed:Math.floor(l)})}),De(t,r,i+185*s,n,"#b8925a",.32,83),Mt(t,r,i+200*s,n,30,"#7a5a30",.3,19,s),Fr(t,o(-.88),i+260*s,640*s,9,{depth:5,color:"#3a2a20",leaves:(a,l,f)=>{le(t,a,l,f*1.3,"#c0582e",.35),le(t,a+f*.5,l-f*.2,f*.9,"#e0a040",.3)}});for(let a=0;a<10;a++)ut(t,o(.7)+a*30*s,i+200*s,(180+oe(a)*120)*s,0,600+a,{color:"#8a6a3a",alpha:.7})},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a,portrait:l}=e,f=Ye(e);on(t,r,n,i,22,12,s,(p,w,y,g)=>Wt(t,p*.55,w,(9+g()*7)*s,y,["#c0582e","#d98a3a","#b8402a","#e0a040"][Math.floor(g()*4)],.85),55);const c=(r+1400*s)/11.5;let u=0;for(let p=0;p<5;p++){const w=r+160*s+p*170*s-(i-ee.autumn.swans)*c,y=n*(l?.2:.17)+p*(l?70:52)*s+10*s*Math.sin(i*1.3+p);p===0&&(u=w),!(w<-300*s||w>r+300*s)&&Se(t,{x:w,y,s:105*f,t:i,sw:1,body:pr,beak:br,legs:!1,wing:1,flap:i*4.2+p*.9,face:-1,neckUp:-.85,seed:80+p})}const h=$(M(ee.autumn.lookUp,ee.autumn.lookUp+1,i)),d=u0&&m<1?1.4:0})},cam(e){return lt(e,E(1.04,1.12,ne(e.t/e.D)),E(e.X(.15),e.X(.05),ne(e.t/e.D)),e.H*.48)},enterAt:[.8,.3]},winter:{label:"寒冬",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#5f6a74",.35,"#c9d0d4",.1),Ee(t,r,i-30*s,160*s,"#5a6268",.3,53),Oe(10,5,a=>le(t,a()*r,i-120*s-a()*60*s,(50+a()*60)*s,"#ffffff",.45,2,.5)),De(t,r,i-10*s,i+300*s,"#c9d4d8",.5,91,5*s),Oe(10,77,(a,l)=>{const f=a()*r,c=i+20*s+a()*250*s;z(t,[[f,c],[f+(60+a()*120)*s,c+(a()-.5)*20*s]],1.6*s,{color:"#6a7a86",alpha:.35,seed:l})}),De(t,r,i+290*s,n,"#ffffff",.6,93,14*s);for(const[a,l,f]of[[-.86,520,3],[.88,460,7]])Fr(t,o(a),i+40*s,l*s,f,{depth:5,color:"#2a2622",leaves:(c,u,h)=>le(t,c,u-3*s,h*.35,"#ffffff",.85,1.6,.6)});for(let a=0;a<12;a++)ut(t,o(-.5)+a*26*s,i+290*s,(140+oe(a)*90)*s,0,700+a,{color:"#6a6458",alpha:.65,wind:.6})},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a}=e,l=Ye(e),f=.34*$(M(4,6.5,i))-.24*$(M(8.2,9,i))+.24*$(M(9.6,10.6,i))-.3*$(M(11.8,13.2,i));f>0&&(t.fillStyle=P("#26303c",f),t.fillRect(-r,-n,r*3,n*3),le(t,a(.6),n*.16,70*s,"#fffbe8",f*2.2),t.fillStyle=P("#fffbe8",f*2.4),t.beginPath(),t.arc(a(.6),n*.16,30*s,0,Math.PI*2),t.fill());const c=a(-.04),u=o+150*s;for(let h=0;h<6;h++){const d=h/6*Math.PI*2+.4,m=(150+oe(h)*160)*s,b=[[c+Math.cos(d)*60*s,u+10*s+Math.sin(d)*15*s]];for(let p=1;p<5;p++)b.push([b[0][0]+Math.cos(d+mt(p,h)*.4)*m*p/4,b[0][1]+Math.sin(d+mt(p,h)*.4)*m*p/4*.25]);z(t,b,2.2*s,{color:"#3e4c58",alpha:.75,grow:me(M(ee.winter.ice+h*.05,ee.winter.ice+.7+h*.05,i)),taperIn:.05,seed:h})}Se(t,{x:c,y:u,s:175*l,t:i,sw:.5,body:Kt,beak:Yt,fluff:.8,seed:7,face:1,swim:!0,tuck:1,shiver:1,snow:$(M(0,e.D,i)),noBlink:!0});for(let h=0;h<4;h++){const d=(i*.45+h*.27)%1,m=n*(.2+.18*h);z(t,[[E(-300,r+300,d)*1,m],[E(-300,r+300,d)+180*s,m+6*s],[E(-300,r+300,d)+360*s,m]],3*s,{color:"#d8dde0",alpha:.5,dry:.9,seed:h})}on(t,r,n,i,170,31,s,(h,d,m,b)=>{const p=(2+b()*5)*s;t.fillStyle=P("#ffffff",.92),t.beginPath(),t.arc(h,d,p,0,Math.PI*2),t.fill(),t.strokeStyle=P("#8a96a0",.35),t.lineWidth=.8*s,t.stroke()},95)},cam(e){return lt(e,E(1.2,1.5,ne(e.t/e.D)),e.X(-.04),e.gy+70*e.u)},enterAt:[.5,.2]},spring:{label:"春回",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#bcd3c8",.2,"#ffffff",0),rr(t,o(.5),n*.15,50*s,"#d65a40",.7),Ee(t,r,i-40*s,150*s,"#86a87a",.35,61),Ee(t,r,i,90*s,"#5e8a50",.4,67),De(t,r,i-5*s,n,"#a9c48a",.35,101);const a=fe([[o(-1.1),i+120*s],[o(-.3),i+60*s],[o(.3),i+160*s],[o(1.1),i+90*s]],12);z(t,a,70*s,{color:"#9fc0c8",alpha:.55,taperIn:0,taperOut:0,jitter:.2}),z(t,a.map(([l,f])=>[l,f-10*s]),6*s,{color:"#5e7a86",alpha:.3,dry:.7,taperIn:0,taperOut:0}),Mt(t,r,i+200*s,n,26,"#5e8a50",.3,23,s)},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a}=e,l=Ye(e),f=ee.spring;cs(t,a(-.9),o+220*s,620*s,i,3,"#8fb86b",.15+.85*$(M(f.buds,f.buds+1.4,i)));const c=$(M(f.bloom,f.bloom+1.3,i));Fr(t,a(.86),o+220*s,520*s,21,{depth:4,color:"#3a2c26",t:i,leaves:(p,w,y)=>{c>0?(ls(t,p,w,y*.28*c,Math.floor(p),"#ec9aa8",.75),ls(t,p+y*.3,w-y*.2,y*.2*c,Math.floor(w),"#f4b8c0",.7)):le(t,p,w,y*.12,"#b86a74",.5)}});for(let p=0;p<2;p++)wn(t,a(-.2+p*.4)+Math.sin(i*.5+p)*100*s,n*.25+p*40*s,20*s,i*9+p,"#3a3a3a");const u=me(M(f.spread,f.spread+.9,i)),h=yr(M(f.lift,f.lift+4.2,i)),d=a(-.12)+h*r*.55,m=o+170*s-h*(o+260*s),b=i3.4?Math.sin(i):0),legs:h<.05,neckUp:h>0?-.6*$(h*4):E(0,.3,u),look:if.lift&&i{const y=i-f.lift;Wt(t,a(-.12)+(p()-.5)*200*s,o+180*s-y*80*s*p(),8*s,y*4+w,"#9fbf7a",.8*(1-y/1.2))}),on(t,r,n,i,26,41,s,(p,w,y,g)=>Wt(t,p,w,(6+g()*5)*s,y,g()<.5?"#f0a8b4":"#f6c8cf",.85),45)},cam(e){const t=ne(M(ee.spring.lift-.3,ee.spring.lift+3.5,e.t));return lt(e,E(1.3,1.1,t),E(e.X(-.1),e.W/2,t),E(e.gy+60*e.u,e.H*.3,t))},enterAt:[.5,.5]},reflection:{label:"倒影",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#c9d8d8",.18,"#ffffff",0),Ee(t,r,i-60*s,120*s,"#8fa69a",.28,71,{soft:1});for(let a=0;a<7;a++){const l=o(-1+a*.33)+oe(a)*60*s;cs(t,l,i-20*s,(200+oe(a*3)*80)*s,0,40+a,"#8fb86b",1)}De(t,r,i-25*s,n,"#a9bcc0",.32,111,3*s),Mt(t,r,i,n,30,"#5a6e78",.28,29,s),Dt(t,o(-.9),n-90*s,120*s,7),Dt(t,o(-.72),n-30*s,90*s,8),Dt(t,o(.92),n-60*s,110*s,9)},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a}=e,l=Ye(e);fo(t,0,r,o,n-o,i,7,{alpha:.22,lines:10,speed:3});const f=a(-.16),c=o+175*s,u=200*l,h=ee.reflection,d=$(M(h.reveal,h.reveal+1.6,i)),m=$(M(h.bow,h.bow+1,i))*(1-$(M(h.proud-.4,h.proud+.8,i))),b=$(M(h.proud,h.proud+1.2,i)),p=M(0,h.land,i),w=ht("#aaa69e",pr,d),y={x:f,y:c,s:u,t:i,sw:1,body:w,beak:ht("#b88a5c",br,d),seed:7,face:1,neckUp:-.95*m+.25*b,glow:d*(1-$(M(h.reveal+2,h.reveal+4,i))),noBlink:m>.5},g=k=>Se(k,{...y,body:pr,beak:br,swim:!0,glow:0});if(p>=1){const k=$(M(h.mirror,h.mirror+1.2,i));Qu(t,g,{x:f-u*1.3,y:c-u*1.4,w:u*2.8,h:u*1.46},c+u*.06,i,E(.3,.95,k),"#7a90a0"),k>0&&k<1&&le(t,f+60*s,c+140*s,220*s,"#fff4d8",.35*Math.sin(Math.PI*k)),d>0&&d<1&&le(t,f+30*s,c-60*s,320*s,"#fff4d8",.55*Math.sin(Math.PI*d));for(let T=0;T<3;T++){const A=M(h.reveal+T*.5,h.reveal+2.4+T*.5,i);A>0&&A<1&&Zu(t,f+70*s,c+60*s,60*s+A*420*s,.4*(1-A),"#9a7a3a")}}for(let k=0;k<3;k++){const T=me(M(1.2+k*.4,5.2+k*.4,i)),A=E(a(1.3+k*.2),a(.3+k*.24),T),N=o+(110+k*45)*s,S=$(M(h.proud+.4+k*.2,h.proud+1.2+k*.2,i));Pt(t,fr(A,N,(150-k*12)*l,i+k,{face:-1,seed:90+k,neckUp:-.55*S+.08*Math.sin(i+k)}))}if(p<1){const k=E(a(-.65),f,me(p)),T=E(n*.12,c,ne(p));Se(t,{...y,x:k,y:T,legs:!1,wing:1-p*.3,flap:i*9,swim:!0,neckUp:-.5})}else{const k=M(h.land,h.land+.85,i);Pt(t,{...y,swim:!0,wing:(1-me(k))*.7,flap:i*6});const T=i-h.land;T<1&&Oe(14,5,A=>{const N=-Math.PI*A(),S=(100+A()*180)*s,B=f+Math.cos(N)*S*T*1.6,V=c+Math.sin(N)*S*T*1.4+300*s*T*T;t.fillStyle=P("#ffffff",.9*(1-T)),t.beginPath(),t.arc(B,V,(3+A()*5)*s,0,Math.PI*2),t.fill(),t.strokeStyle=P("#6a7a86",.5*(1-T)),t.lineWidth=s,t.stroke()})}Oe(10,61,(k,T)=>{const A=(k()*r+i*14*s)%r,N=o+40*s+k()*(n-o-80*s);Wt(t,A,N,6*s,i*.3+T,"#f0a8b4",.75)})},cam(e){const{t,u:r,gy:n}=e,s=ne(M(ee.reflection.bow-.2,ee.reflection.mirror+1.2,t)),i=ne(M(ee.reflection.reveal-.6,ee.reflection.reveal+2.4,t));return lt(e,E(E(1.25,1.65,s),1.3,i),E(e.X(-.05),e.X(-.1),s),n+E(E(60,230,s),110,i)*r)},enterAt:[.3,.3]},accepted:{label:"接纳",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#b8d0d8",.2,"#ffffff",0),Ee(t,r,i-50*s,110*s,"#88a294",.28,81,{soft:1}),De(t,r,i-20*s,n,"#a3bfc6",.34,121,3*s),Mt(t,r,i,n,30,"#4f6670",.28,33,s);const a=[[-50*s,i-60*s],[o(-.55),i-20*s],[o(-.38),i+120*s],[o(-.5),i+260*s],[-50*s,i+300*s]];ue(t,a,"#9fb878",.6);for(let l=0;l<10;l++)En(t,o(-.95)+l*50*s,i+100*s+oe(l)*120*s,50*s,0,300+l,"#4f6a3e",.6,.3)},draw(e){const{g:t,W:r,H:n,u:s,t:i,gy:o,X:a}=e,l=Ye(e);Wr(t,a(.3)+i*10*s,n*.14,380*s,9,.22),cs(t,a(-.88),o+60*s,560*s,i,8,"#8fb86b",1);const f=i>ee.accepted.cheer;[[-.82,"#c0504a",!0],[-.66,"#4a6a9a",!1],[-.52,"#d0a040",!0]].forEach(([p,w,y],g)=>{co(t,a(p),o+110*s+g*12*s,270*l,i+g,{face:1,dress:w,girl:y,pose:f?g===1?"shoo":"clap":"stand",seed:10+g}),f&&Lr(t,a(p)+50*s,o-200*s*l,40*s,.8*(1-$(M(10,11,i))),g)});const c=a(.62),u=o+70*s,h=$(M(ee.accepted.heart,ee.accepted.heart+2,i));Pt(t,fr(c-70*s,u,115*l,i,{face:1,seed:95,neckUp:-.55*h})),Pt(t,fr(c+70*s,u,115*l,i+1,{face:-1,seed:96,neckUp:-.55*h}));const d=a(.12),m=o+210*s,b=p=>Math.max(0,Math.sin(i*1.6+p))*$(M(.5,1.5,i));Pt(t,fr(d-190*s,m-30*s,165*l,i+2,{face:1,seed:91,neckUp:-.7*b(0)})),Pt(t,fr(d,m,200*l,i,{face:1,seed:7,neckUp:.2+.1*Math.sin(i),glow:.2})),Pt(t,fr(d+220*s,m-20*s,165*l,i+3,{face:-1,seed:92,neckUp:-.7*b(2)})),Oe(12,71,(p,w)=>{const y=(p()*r+i*18*s)%r,g=o+30*s+p()*(n-o-60*s);Wt(t,y,g,6*s,i*.4+w,"#f0a8b4",.75)})},cam(e){return lt(e,E(1.32,1.18,ne(e.t/e.D)),e.X(-.12),e.gy+110*e.u)},enterAt:[.2,.5]},fly:{label:"高飞",bg(e){const{g:t,W:r,H:n,u:s,X:i,portrait:o}=e;et(t,r,n,"#a8c4d4",.32,"#f2ead8",0),rr(t,i(.62),n*(o?.14:.18),60*s,"#d65a40",.75),Ee(t,r,n*.92,300*s,"#7d8f8a",.35,91),Ee(t,r,n*1.02,260*s,"#4f5d58",.5,97)},draw(e){const{g:t,W:r,H:n,u:s,t:i,X:o,portrait:a}=e,l=Ye(e);Oe(7,21,(m,b)=>{const p=n*(.15+m()*.6),w=(260+m()*360)*s,y=(90+m()*160)*s,g=((m()*(r+w*2)-i*y)%(r+w*2)+r+w*2)%(r+w*2)-w;Wr(t,g,p,w,30+b,.25+m()*.15)});const f=me(M(0,2.2,i)),c=E(-r*.4,o(a?.05:.1),f)+Math.sin(i*.9)*20*s,u=n*(a?.42:.44)+Math.sin(i*1.2)*18*s,h=a?.62:1;[[-250,110,.78],[-470,210,.66],[-230,-120,.74],[-440,-210,.6]].forEach(([m,b,p],w)=>Se(t,{x:c+m*s*h,y:u+b*s*h,s:230*l*p,t:i,sw:1,body:pr,beak:br,legs:!1,wing:1,flap:i*3.3+w*.8,neckUp:-.85,seed:80+w})),Se(t,{x:c,y:u,s:240*l,t:i,sw:1,body:pr,beak:br,legs:!1,wing:1,flap:i*3.2,neckUp:-.85,seed:7,glow:.3})},cam(e){return{z:E(1,1.08,e.t/e.D),x:e.W/2,y:e.H/2}},enterAt:[.1,.5]},ending:{label:"寄语",bg(e){const{g:t,W:r,H:n,u:s,gy:i,X:o}=e;et(t,r,i,"#e8a070",.3,"#f0c090",.35),rr(t,o(.05),i-130*s,115*s,"#c8463a",.9),Ee(t,r,i-20*s,200*s,"#6a5048",.42,101),Ee(t,r,i+10*s,120*s,"#3a2c28",.6,107),De(t,r,i+5*s,n,"#d8a878",.3,131,3*s),Oe(14,9,a=>{const l=i+20*s+a()*200*s,f=(130-(l-i)/s*.4)*s;z(t,[[o(.05)-f,l],[o(.05),l+2*s],[o(.05)+f,l]],6*s,{color:"#c8463a",alpha:.35,dry:.6,seed:Math.floor(l)})});for(let a=0;a<8;a++)ut(t,o(-.98)+a*34*s,n+10*s,(300+oe(a)*200)*s,0,800+a,{color:"#2a2220",alpha:.85});for(let a=0;a<6;a++)ut(t,o(.86)+a*30*s,n+10*s,(240+oe(a+9)*160)*s,0,820+a,{color:"#2a2220",alpha:.85})},draw(e){const{g:t,H:r,u:n,t:s,gy:i,X:o,portrait:a}=e;for(let m=0;m<5;m++){const b=M(0,10,s),p=o(-.75+m*.07),w=r*(.3+.03*m),y=o(.05)+m*12*n,g=i-160*n-m*8*n,k=E(p,y,b*.85),T=E(w,g,b*.85);wn(t,k,T,E(28,9,b)*n,s*7+m*1.3,"#2a2220",.9)}const l=(a?150:130)*n,f=o(a?.62:.72),c=a?r*.3:r*.36;mo(t,"相信自己",f,c,l,1.4,.55,s,{vertical:!0,gap:1.02});const u=ee.ending,h=$(M(u.end-.3,u.end+.3,s));h>0&&(t.save(),t.globalAlpha=h,t.font=`${40*n}px ${xr}`,t.fillStyle=P(G,.85),t.textAlign="center",t.save(),t.translate(f-l*.95,c-l*1.45),[..."丑小鸭 · 完"].forEach((m,b)=>t.fillText(m,0,b*46*n)),t.restore(),t.restore());const d=M(u.end+.1,u.end+.35,s);d>0&&(t.save(),t.translate(f-l*.95,c+l*1.2),t.scale(E(1.5,1,me(d)),E(1.5,1,me(d))),Hr(t,0,0,80*n,["童话"],{alpha:Math.min(1,d*2)}),t.restore())},cam(e){return{z:E(1,1.05,e.t/e.D),x:e.W/2,y:e.gy}},enterAt:[.5,.4]}};function ed(e){const t=.8333333333333334,r=Tn,n=js,s=(b,p,w,y,g,k=[0,1,2,1])=>{let T=0;for(let A=b;Ae.bell(c+d.reveal+p*.18,b,.08,p%2?.3:-.3)),e.pad(c+d.reveal,r("accepted")+.5,[x.G2,x.D3,x.B3,x.D4],.05,1200),e.melody(c+d.reveal+1.2,t,[["G5",1.5],["E5",.5],["D5",1],["B4",1],["D5",2]],.14),c=r("accepted"),s(c+.2,r("fly"),[i,o,a,i],t/2,.15),e.pad(c,r("fly")+.3,[x.G2,x.D3],.04),e.melody(c+.8,t,[["G4",1],["A4",.5],["B4",.5],["D5",1],["E5",.5],["D5",.5],["B4",1],["A4",.5],["G4",.5],["G4",2]],.12),c=r("fly"),e.pad(c,r("ending")+1,[x.G2,x.D3,x.G3,x.B3],.06,1600),s(c,r("ending"),[f,i,o,f],t/4,.16,[0,1,2,1,2,1,0,2]);for(let b=0;bjr(Va,e,t),sd={id:"duckling",title:"丑小鸭",tagline:"变成白天鹅",intro:"安徒生童话 · 水墨手绘儿童动画。宣纸、毛笔、淡彩、朱红印章,像小学语文课本里的插图那样,一笔一画在你的浏览器里实时画出来;旁白、原创国风配乐与音效同步播放。",credit:"故事改编自安徒生《丑小鸭》(1843,公有领域)",assets:"film/",slug:"ugly-duckling",timeline:Va,scenes:Ju,seal:["童话"],series:"安徒生童话 · 丑小鸭",score:ed,effects:td,covers:[{id:"reflect",name:"倒影揭晓",t:an("reflection",7.6),title:"丑小鸭",sub:"原来,他是一只白天鹅",side:"left",vertical:!0},{id:"hatch",name:"破壳而出",t:an("bigegg",4.4),title:"丑小鸭",sub:"安徒生童话 · 水墨手绘动画",side:"right",vertical:!0},{id:"fly",name:"展翅高飞",t:an("fly",4.2),title:"相信自己",sub:"你也会展开属于自己的翅膀",side:"right",vertical:!0},{id:"autumn",name:"秋日仰望",t:an("autumn",6.6),title:"丑小鸭",sub:"像语文课本里的插图",side:"left",vertical:!0}],live:Du,platforms:rd,photoPosts:nd,qa:$u,making:[["画面","全部由代码逐帧绘制:程序生成宣纸纹理,变宽毛笔笔触带起笔、收笔和飞白,淡彩晕染有水彩积边,再盖朱砂印章。同一只「鸟」可以从小黄鸭一路长成天鹅。每帧只由时间决定,可逐帧复现。"],["声音","中文神经网络语音旁白;原创五声音阶配乐(古筝拨弦、竹笛、铺底、铃),随四季换情绪;破壳、鸭叫、枪声、冰裂、振翅、拍手等音效都是程序合成。人声出现时配乐自动压低,整体响度 −14 LUFS。"],["流程","按 vibe-video 流程:分镜 storyboard → 配音生成时间轴 → 接触表 / 静帧检查 → 确定性自检 → 全片渲染 → 混音 → vibe qa 质检 → 封面与发布包;实况图按 livecanvas 流程配对封装。"]]},id="狼来了",od="ink-wash textbook illustration",ad=1920,cd=1080,ld=30,fd=137.86666666666667,ud=4136,dd={n:"zh-CN-XiaoxiaoNeural",boy:"zh-CN-YunxiaNeural",farmer:"zh-CN-YunjianNeural"},hd=[{id:"title",start:0,dur:6.5,voice_start:.5,voice_dur:2.568,lines:[{who:"n",text:"伊索寓言,狼来了。",t0:.5,dur:2.568,subs:[{t0:.5,t1:1.967,text:"伊索寓言",who:"n"},{t0:1.967,t1:3.068,text:"狼来了",who:"n"}]}],subs:[{t0:.5,t1:1.967,text:"伊索寓言",who:"n"},{t0:1.967,t1:3.068,text:"狼来了",who:"n"}]},{id:"village",start:6.5,dur:10.566666666666666,voice_start:7,voice_dur:9.168,lines:[{who:"n",text:"山脚下有个小村子。大人们每天在田里干活,有个放羊的孩子,天天赶着羊群上山吃草。",t0:7,dur:9.168,subs:[{t0:7,t1:9.096,text:"山脚下有个小村子",who:"n"},{t0:9.096,t1:11.715,text:"大人们每天在田里干活",who:"n"},{t0:11.715,t1:13.549,text:"有个放羊的孩子",who:"n"},{t0:13.549,t1:16.168,text:"天天赶着羊群上山吃草",who:"n"}]}],subs:[{t0:7,t1:9.096,text:"山脚下有个小村子",who:"n"},{t0:9.096,t1:11.715,text:"大人们每天在田里干活",who:"n"},{t0:11.715,t1:13.549,text:"有个放羊的孩子",who:"n"},{t0:13.549,t1:16.168,text:"天天赶着羊群上山吃草",who:"n"}]},{id:"bored",start:17.0667,dur:12.833333333333334,voice_start:17.567,voice_dur:11.424,lines:[{who:"n",text:"山坡上,羊儿慢悠悠地吃着草。放羊娃躺在大树下,嘴里叼着一根草,觉得无聊极了。忽然,他的眼珠一转。",t0:17.567,dur:11.424,subs:[{t0:17.567,t1:18.403,text:"山坡上",who:"n"},{t0:18.403,t1:20.91,text:"羊儿慢悠悠地吃着草",who:"n"},{t0:20.91,t1:23.139,text:"放羊娃躺在大树下",who:"n"},{t0:23.139,t1:25.09,text:"嘴里叼着一根草",who:"n"},{t0:25.09,t1:26.762,text:"觉得无聊极了",who:"n"},{t0:26.762,t1:27.319,text:"忽然",who:"n"},{t0:27.319,t1:28.991,text:"他的眼珠一转",who:"n"}]}],subs:[{t0:17.567,t1:18.403,text:"山坡上",who:"n"},{t0:18.403,t1:20.91,text:"羊儿慢悠悠地吃着草",who:"n"},{t0:20.91,t1:23.139,text:"放羊娃躺在大树下",who:"n"},{t0:23.139,t1:25.09,text:"嘴里叼着一根草",who:"n"},{t0:25.09,t1:26.762,text:"觉得无聊极了",who:"n"},{t0:26.762,t1:27.319,text:"忽然",who:"n"},{t0:27.319,t1:28.991,text:"他的眼珠一转",who:"n"}]},{id:"shout1",start:29.9,dur:9.3,voice_start:30.4,voice_dur:7.892,lines:[{who:"n",text:"他跳上大石头,拢着嘴大喊:",t0:30.4,dur:3.36,subs:[{t0:30.4,t1:32.233,text:"他跳上大石头",who:"n"},{t0:32.233,t1:33.76,text:"拢着嘴大喊",who:"n"}]},{who:"boy",text:"狼来了!狼来了!快来呀!",t0:34.14,dur:4.152,subs:[{t0:34.14,t1:35.524,text:"狼来了!",who:"boy"},{t0:35.524,t1:36.908,text:"狼来了!",who:"boy"},{t0:36.908,t1:38.292,text:"快来呀!",who:"boy"}]}],subs:[{t0:30.4,t1:32.233,text:"他跳上大石头",who:"n"},{t0:32.233,t1:33.76,text:"拢着嘴大喊",who:"n"},{t0:34.14,t1:35.524,text:"狼来了!",who:"boy"},{t0:35.524,t1:36.908,text:"狼来了!",who:"boy"},{t0:36.908,t1:38.292,text:"快来呀!",who:"boy"}]},{id:"run1",start:39.2,dur:11.8,voice_start:39.7,voice_dur:10.412,lines:[{who:"n",text:"山下的农民听见了,扛起锄头,拿起扁担,急急忙忙往山上跑。",t0:39.7,dur:6.768,subs:[{t0:39.7,t1:41.956,text:"山下的农民听见了",who:"n"},{t0:41.956,t1:43.084,text:"扛起锄头",who:"n"},{t0:43.084,t1:44.212,text:"拿起扁担",who:"n"},{t0:44.212,t1:46.468,text:"急急忙忙往山上跑",who:"n"}]},{who:"farmer",text:"狼在哪儿?狼在哪儿?",t0:46.848,dur:3.264,subs:[{t0:46.848,t1:48.48,text:"狼在哪儿?",who:"farmer"},{t0:48.48,t1:50.112,text:"狼在哪儿?",who:"farmer"}]}],subs:[{t0:39.7,t1:41.956,text:"山下的农民听见了",who:"n"},{t0:41.956,t1:43.084,text:"扛起锄头",who:"n"},{t0:43.084,t1:44.212,text:"拿起扁担",who:"n"},{t0:44.212,t1:46.468,text:"急急忙忙往山上跑",who:"n"},{t0:46.848,t1:48.48,text:"狼在哪儿?",who:"farmer"},{t0:48.48,t1:50.112,text:"狼在哪儿?",who:"farmer"}]},{id:"laugh1",start:51,dur:9.4,voice_start:51.5,voice_dur:7.988,lines:[{who:"boy",text:"哈哈哈,哪有什么狼,我是逗你们玩的!",t0:51.5,dur:3.816,subs:[{t0:51.5,t1:52.215,text:"哈哈哈",who:"boy"},{t0:52.215,t1:53.408,text:"哪有什么狼",who:"boy"},{t0:53.408,t1:55.316,text:"我是逗你们玩的!",who:"boy"}]},{who:"n",text:"农民们气得直摇头,只好下山去了。",t0:55.696,dur:3.792,subs:[{t0:55.696,t1:57.863,text:"农民们气得直摇头",who:"n"},{t0:57.863,t1:59.488,text:"只好下山去了",who:"n"}]}],subs:[{t0:51.5,t1:52.215,text:"哈哈哈",who:"boy"},{t0:52.215,t1:53.408,text:"哪有什么狼",who:"boy"},{t0:53.408,t1:55.316,text:"我是逗你们玩的!",who:"boy"},{t0:55.696,t1:57.863,text:"农民们气得直摇头",who:"n"},{t0:57.863,t1:59.488,text:"只好下山去了",who:"n"}]},{id:"shout2",start:60.4,dur:10.9,voice_start:60.9,voice_dur:9.496,lines:[{who:"n",text:"过了几天,放羊娃又喊起来:",t0:60.9,dur:3.216,subs:[{t0:60.9,t1:62.069,text:"过了几天",who:"n"},{t0:62.069,t1:64.116,text:"放羊娃又喊起来",who:"n"}]},{who:"boy",text:"狼来了!狼来了!",t0:64.496,dur:2.904,subs:[{t0:64.496,t1:65.948,text:"狼来了!",who:"boy"},{t0:65.948,t1:67.4,text:"狼来了!",who:"boy"}]},{who:"n",text:"农民们又一次跑上了山。",t0:67.78,dur:2.616,subs:[{t0:67.78,t1:70.396,text:"农民们又一次跑上了山",who:"n"}]}],subs:[{t0:60.9,t1:62.069,text:"过了几天",who:"n"},{t0:62.069,t1:64.116,text:"放羊娃又喊起来",who:"n"},{t0:64.496,t1:65.948,text:"狼来了!",who:"boy"},{t0:65.948,t1:67.4,text:"狼来了!",who:"boy"},{t0:67.78,t1:70.396,text:"农民们又一次跑上了山",who:"n"}]},{id:"warn",start:71.3,dur:8,voice_start:71.8,voice_dur:6.596,lines:[{who:"boy",text:"哈哈,又上当啦!",t0:71.8,dur:2.352,subs:[{t0:71.8,t1:72.472,text:"哈哈",who:"boy"},{t0:72.472,t1:74.152,text:"又上当啦!",who:"boy"}]},{who:"farmer",text:"你再说谎,下次我们就不信你了!",t0:74.532,dur:3.864,subs:[{t0:74.532,t1:75.636,text:"你再说谎",who:"farmer"},{t0:75.636,t1:78.396,text:"下次我们就不信你了!",who:"farmer"}]}],subs:[{t0:71.8,t1:72.472,text:"哈哈",who:"boy"},{t0:72.472,t1:74.152,text:"又上当啦!",who:"boy"},{t0:74.532,t1:75.636,text:"你再说谎",who:"farmer"},{t0:75.636,t1:78.396,text:"下次我们就不信你了!",who:"farmer"}]},{id:"wolf",start:79.3,dur:12,voice_start:79.8,voice_dur:10.584,lines:[{who:"n",text:"又过了些日子,乌云压着山头。树丛里,一双绿莹莹的眼睛,慢慢靠近了羊群。狼,真的来了!",t0:79.8,dur:10.584,subs:[{t0:79.8,t1:81.564,text:"又过了些日子",who:"n"},{t0:81.564,t1:83.328,text:"乌云压着山头",who:"n"},{t0:83.328,t1:84.21,text:"树丛里",who:"n"},{t0:84.21,t1:86.562,text:"一双绿莹莹的眼睛",who:"n"},{t0:86.562,t1:88.62,text:"慢慢靠近了羊群",who:"n"},{t0:88.62,t1:88.914,text:"狼",who:"n"},{t0:88.914,t1:90.384,text:"真的来了!",who:"n"}]}],subs:[{t0:79.8,t1:81.564,text:"又过了些日子",who:"n"},{t0:81.564,t1:83.328,text:"乌云压着山头",who:"n"},{t0:83.328,t1:84.21,text:"树丛里",who:"n"},{t0:84.21,t1:86.562,text:"一双绿莹莹的眼睛",who:"n"},{t0:86.562,t1:88.62,text:"慢慢靠近了羊群",who:"n"},{t0:88.62,t1:88.914,text:"狼",who:"n"},{t0:88.914,t1:90.384,text:"真的来了!",who:"n"}]},{id:"cry",start:91.3,dur:9.4,voice_start:91.8,voice_dur:7.988,lines:[{who:"n",text:"放羊娃吓坏了,拼命地喊:",t0:91.8,dur:3.192,subs:[{t0:91.8,t1:93.715,text:"放羊娃吓坏了",who:"n"},{t0:93.715,t1:94.992,text:"拼命地喊",who:"n"}]},{who:"boy",text:"狼来了!狼真的来了!救命啊!",t0:95.372,dur:4.416,subs:[{t0:95.372,t1:96.634,text:"狼来了!",who:"boy"},{t0:96.634,t1:98.526,text:"狼真的来了!",who:"boy"},{t0:98.526,t1:99.788,text:"救命啊!",who:"boy"}]}],subs:[{t0:91.8,t1:93.715,text:"放羊娃吓坏了",who:"n"},{t0:93.715,t1:94.992,text:"拼命地喊",who:"n"},{t0:95.372,t1:96.634,text:"狼来了!",who:"boy"},{t0:96.634,t1:98.526,text:"狼真的来了!",who:"boy"},{t0:98.526,t1:99.788,text:"救命啊!",who:"boy"}]},{id:"noone",start:100.7,dur:11.166666666666666,voice_start:101.2,voice_dur:9.76,lines:[{who:"n",text:"山下的农民听见了,都以为他又在说谎。",t0:101.2,dur:4.296,subs:[{t0:101.2,t1:103.348,text:"山下的农民听见了",who:"n"},{t0:103.348,t1:105.496,text:"都以为他又在说谎",who:"n"}]},{who:"farmer",text:"哼,又来骗人。",t0:105.876,dur:2.664,subs:[{t0:105.876,t1:106.409,text:"哼",who:"farmer"},{t0:106.409,t1:108.54,text:"又来骗人",who:"farmer"}]},{who:"n",text:"谁也没有上山。",t0:108.92,dur:2.04,subs:[{t0:108.92,t1:110.96,text:"谁也没有上山",who:"n"}]}],subs:[{t0:101.2,t1:103.348,text:"山下的农民听见了",who:"n"},{t0:103.348,t1:105.496,text:"都以为他又在说谎",who:"n"},{t0:105.876,t1:106.409,text:"哼",who:"farmer"},{t0:106.409,t1:108.54,text:"又来骗人",who:"farmer"},{t0:108.92,t1:110.96,text:"谁也没有上山",who:"n"}]},{id:"chaos",start:111.8667,dur:8,voice_start:112.367,voice_dur:5.976,lines:[{who:"n",text:"狼冲进羊群,羊儿吓得四处乱跑,好几只羊被狼叼走了。",t0:112.367,dur:5.976,subs:[{t0:112.367,t1:113.725,text:"狼冲进羊群",who:"n"},{t0:113.725,t1:115.898,text:"羊儿吓得四处乱跑",who:"n"},{t0:115.898,t1:118.343,text:"好几只羊被狼叼走了",who:"n"}]}],subs:[{t0:112.367,t1:113.725,text:"狼冲进羊群",who:"n"},{t0:113.725,t1:115.898,text:"羊儿吓得四处乱跑",who:"n"},{t0:115.898,t1:118.343,text:"好几只羊被狼叼走了",who:"n"}]},{id:"regret",start:119.8667,dur:8,voice_start:120.367,voice_dur:5.328,lines:[{who:"n",text:"太阳落山了,放羊娃抱着剩下的小羊,伤心地哭了。",t0:120.367,dur:5.328,subs:[{t0:120.367,t1:121.699,text:"太阳落山了",who:"n"},{t0:121.699,t1:124.363,text:"放羊娃抱着剩下的小羊",who:"n"},{t0:124.363,t1:125.695,text:"伤心地哭了",who:"n"}]}],subs:[{t0:120.367,t1:121.699,text:"太阳落山了",who:"n"},{t0:121.699,t1:124.363,text:"放羊娃抱着剩下的小羊",who:"n"},{t0:124.363,t1:125.695,text:"伤心地哭了",who:"n"}]},{id:"moral",start:127.8667,dur:10,voice_start:128.367,voice_dur:7.224,lines:[{who:"n",text:"说谎的人,就算说了真话,也没有人再相信他。做一个诚实的孩子吧。",t0:128.367,dur:7.224,subs:[{t0:128.367,t1:129.437,text:"说谎的人",who:"n"},{t0:129.437,t1:131.042,text:"就算说了真话",who:"n"},{t0:131.042,t1:133.183,text:"也没有人再相信他",who:"n"},{t0:133.183,t1:135.591,text:"做一个诚实的孩子吧",who:"n"}]}],subs:[{t0:128.367,t1:129.437,text:"说谎的人",who:"n"},{t0:129.437,t1:131.042,text:"就算说了真话",who:"n"},{t0:131.042,t1:133.183,text:"也没有人再相信他",who:"n"},{t0:133.183,t1:135.591,text:"做一个诚实的孩子吧",who:"n"}]}],md={title:id,style:od,w:ad,h:cd,fps:ld,total:fd,frames:ud,voice:dd,scenes:hd},pd=[{id:"01-idea",scene:"bored",from:9.4,dur:3,cover:2,caption:"坏主意",index:"其一",xhs:"山坡上太无聊,放羊娃眼珠一转。"},{id:"02-shout",scene:"shout1",from:4,dur:3,cover:1.2,caption:"狼来了",index:"其二",xhs:"跳上石头大喊,朱红大字飞向山下。"},{id:"03-run",scene:"run1",from:5,dur:3,cover:1.5,caption:"急急忙忙",index:"其三",xhs:"农民们扛着锄头跑上山。"},{id:"04-laugh",scene:"laugh1",from:.6,dur:3,cover:1,caption:"哈哈大笑",index:"其四",xhs:"「我是逗你们玩的!」"},{id:"05-eyes",scene:"wolf",from:6.4,dur:3,cover:2.2,caption:"真的来了",index:"其五",xhs:"树丛里,一双绿莹莹的眼睛。"},{id:"06-regret",scene:"regret",from:4,dur:3,cover:1.5,caption:"后悔",index:"其六",xhs:"夕阳下,他抱着小羊哭了。"}],bd="done",wd="2026-10-18",yd={w:1080,h:1920,fps:30,dur:137.866667,audio:!0},gd="PASS",xd=[{level:"PASS",check:"竖屏 分辨率",msg:"1080×1920(时间轴 1080×1920)"},{level:"PASS",check:"竖屏 帧率",msg:"30.0 fps"},{level:"PASS",check:"竖屏 时长",msg:"137.87s(时间轴 137.87s)"},{level:"PASS",check:"竖屏 音轨",msg:"有"},{level:"PASS",check:"竖屏 响度",msg:"-14.0 LUFS(目标 −14)"},{level:"PASS",check:"竖屏 真峰值",msg:"-1.6 dBTP(应 ≤ −1)"},{level:"PASS",check:"竖屏 黑场",msg:"无"},{level:"PASS",check:"竖屏 冻结画面",msg:"无"},{level:"PASS",check:"竖屏 文字@title",msg:"OCR 读出 13 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@village",msg:"OCR 读出 42 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@bored",msg:"OCR 读出 28 字;字幕命中 67%"},{level:"PASS",check:"竖屏 文字@shout1",msg:"OCR 读出 31 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@run1",msg:"OCR 读出 16 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@laugh1",msg:"OCR 读出 24 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@shout2",msg:"OCR 读出 26 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@warn",msg:"OCR 读出 36 字;字幕命中 100%"},{level:"INFO",check:"竖屏 文字@wolf",msg:"OCR 读出 9 字;字幕命中 17%(整帧 OCR 受背景干扰;单独裁出字幕行复检读出「慢慢靠近了羊群」,命中 100%)"},{level:"PASS",check:"竖屏 文字@cry",msg:"OCR 读出 26 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@noone",msg:"OCR 读出 41 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@chaos",msg:"OCR 读出 24 字;字幕命中 89%"},{level:"PASS",check:"竖屏 文字@regret",msg:"OCR 读出 23 字;字幕命中 100%"},{level:"PASS",check:"竖屏 文字@moral",msg:"OCR 读出 14 字;字幕命中 100%"},{level:"PASS",check:"横屏 分辨率",msg:"1920×1080(时间轴 1920×1080)"},{level:"PASS",check:"横屏 帧率",msg:"30.0 fps"},{level:"PASS",check:"横屏 时长",msg:"137.87s(时间轴 137.87s)"},{level:"PASS",check:"横屏 音轨",msg:"有"},{level:"PASS",check:"横屏 响度",msg:"-14.0 LUFS(目标 −14)"},{level:"PASS",check:"横屏 真峰值",msg:"-1.6 dBTP(应 ≤ −1)"},{level:"PASS",check:"横屏 黑场",msg:"无"},{level:"PASS",check:"横屏 冻结画面",msg:"无"},{level:"PASS",check:"横屏 字幕@village",msg:"「天天赶着羊群上山吃草」OCR 命中 0%;墨色/纸带对比度 14.6:1"},{level:"PASS",check:"横屏 字幕@bored",msg:"「觉得无聊极了」OCR 命中 0%;墨色/纸带对比度 13.9:1"},{level:"PASS",check:"横屏 字幕@shout1",msg:"「狼来了」OCR 命中 0%;墨色/纸带对比度 6.5:1"},{level:"PASS",check:"横屏 字幕@run1",msg:"「狼在哪儿」OCR 命中 100%;墨色/纸带对比度 7.7:1"},{level:"PASS",check:"横屏 字幕@laugh1",msg:"「只好下山去了」OCR 命中 0%;墨色/纸带对比度 13.7:1"},{level:"PASS",check:"横屏 字幕@shout2",msg:"「农民们又一次跑上了山」OCR 命中 100%;墨色/纸带对比度 14.1:1"},{level:"PASS",check:"横屏 字幕@warn",msg:"「下次我们就不信你了」OCR 命中 100%;墨色/纸带对比度 7.9:1"},{level:"PASS",check:"横屏 字幕@wolf",msg:"「慢慢靠近了羊群」OCR 命中 100%;墨色/纸带对比度 13.8:1"},{level:"PASS",check:"横屏 字幕@cry",msg:"「狼真的来了」OCR 命中 100%;墨色/纸带对比度 6.6:1"},{level:"PASS",check:"横屏 字幕@noone",msg:"「谁也没有上山」OCR 命中 100%;墨色/纸带对比度 13.6:1"},{level:"PASS",check:"横屏 字幕@chaos",msg:"「好几只羊被狼叼走了」OCR 命中 89%;墨色/纸带对比度 13.9:1"},{level:"PASS",check:"横屏 字幕@moral",msg:"「做一个诚实的孩子吧」OCR 命中 100%;墨色/纸带对比度 14.0:1"},{level:"INFO",check:"说明",msg:"横屏字幕位于画面底部 ~92%,vibe qa 的 OCR 只扫 60–84% 高度带,故横屏改用「字幕带 OCR + 墨色/纸带对比度(≥4.5:1,大字号 WCAG AAA)」补充检查。"},{level:"PASS",check:"确定性",msg:"vibe det:同一帧乱序重渲逐像素一致"},{level:"PASS",check:"实况图",msg:"6 组 JPG+MOV:content identifier 一致、still-image-time 读回正确、MOV 完整解码、哈希校验通过(PhotoKit 本机加载:Linux 环境不可用)"}],vd={status:bd,generated:wd,meta:yd,verdict:gd,results:xd},qt=(e,t,r)=>[E(e[0],t[0],r),E(e[1],t[1],r)],ur=(e,t)=>[e[0]+t[0],e[1]+t[1]],Ae=(e,t,r)=>{const n=e[0]-t[0],s=e[1]-t[1],i=Math.cos(r),o=Math.sin(r);return[t[0]+n*i-s*o,t[1]+n*o+s*i]};function cn(e,t,r,n,s){let i=t[0]-e[0],o=t[1]-e[1],a=Math.hypot(i,o);if(a>r+n-1e-4){const c=(r+n-1e-4)/a;i*=c,o*=c,a=r+n-1e-4,t=[e[0]+i,e[1]+o]}if(a<1e-4)return[[e[0]+r,e[1]],t];const l=Re((r*r+a*a-n*n)/(2*r*a),-1,1),f=Math.atan2(o,i)+s*Math.acos(l);return[[e[0]+Math.cos(f)*r,e[1]+Math.sin(f)*r],t]}function Zt(e,t,r,n,s,i,o,a=0){z(e,t,r+s*2.2,{color:G,alpha:.82,taperIn:.02,taperOut:.04,jitter:.12,seed:i}),e.lineCap="round",e.lineJoin="round",e.strokeStyle=n,e.lineWidth=r,e.beginPath(),e.moveTo(t[0][0],t[0][1]);for(const l of t.slice(1))e.lineTo(l[0],l[1]);if(e.stroke(),o&&a>0){const l=t[t.length-2],f=t[t.length-1],c=qt(l,f,a);e.strokeStyle=o,e.lineWidth=r*.9,e.beginPath(),e.moveTo(c[0],c[1]),e.lineTo(f[0],f[1]),e.stroke()}}const kt="#efcfa8",kd={boy:{hip:-.4,sh:-.68,head:-.85,hr:.13,ua:.15,fa:.15,th:.2,sn:.2,wTop:.095,wBot:.11,limb:.055},man:{hip:-.48,sh:-.79,head:-.91,hr:.088,ua:.17,fa:.165,th:.245,sn:.235,wTop:.11,wBot:.105,limb:.05},woman:{hip:-.47,sh:-.77,head:-.89,hr:.085,ua:.16,fa:.16,th:.24,sn:.23,wTop:.095,wBot:.115,limb:.045}};function Da(e,t){const r=t.s,n=t.face??1,s=t.seed??1,i=t.t,o=kd[t.kind],a=Math.max(1.1,r*.012)/r,l=t.alpha??1;if(l<=0)return;e.save(),e.globalAlpha=l,e.translate(t.x+(t.shiver??0)*r*.006*Math.sin(i*53+s),t.y),e.scale(n*r,r);const f=t.bob??0,c=[0,(t.hipY??o.hip)+f],u=t.lean??0,h=Ae([0,o.sh+f],c,u),d=Ae([.008,o.sh-.035+f],c,u),m=Ae([.022,o.head+f],c,u),b=t.jacket??(t.kind==="boy"?"#c0473a":"#5d7385"),p=t.pants??(t.kind==="boy"?"#3e4a5c":"#3a4048"),w=o.limb,y=()=>{const I=t.tool;if(I){if(I.kind==="hoe"||I.kind==="pole"||I.kind==="stick"){if(z(e,[I.a,qt(I.a,I.b,.5),I.b],.022,{color:"#5a4026",alpha:.95,taperIn:0,taperOut:0,jitter:.08,seed:s+70}),e.strokeStyle=P("#a07a48",.9),e.lineWidth=.012,e.beginPath(),e.moveTo(I.a[0],I.a[1]),e.lineTo(I.b[0],I.b[1]),e.stroke(),I.kind==="hoe"){const j=I.b[0]-I.a[0],Z=I.b[1]-I.a[1],re=Math.hypot(j,Z)||1,ie=j/re,O=Z/re,F=-O,Q=ie,q=I.b,H=[[q[0]-F*.01,q[1]-Q*.01],[q[0]+F*.13-ie*.02,q[1]+Q*.13-O*.02],[q[0]+F*.14+ie*.05,q[1]+Q*.14+O*.05],[q[0]+ie*.03,q[1]+O*.03]];e.fillStyle=P("#4a4c50",.95),e.beginPath(),e.moveTo(H[0][0],H[0][1]);for(const K of H.slice(1))e.lineTo(K[0],K[1]);e.closePath(),e.fill(),e.strokeStyle=P(G,.8),e.lineWidth=a,e.stroke()}if(I.kind==="pole")for(const j of[I.a,I.b])e.strokeStyle=P("#5a4026",.8),e.lineWidth=.006,e.beginPath(),e.moveTo(j[0],j[1]),e.lineTo(j[0],j[1]+.06),e.stroke()}if(I.kind==="whip"){z(e,[I.a,qt(I.a,I.b,.5),I.b],.014,{color:"#5a4026",alpha:.95,taperIn:0,taperOut:.3,seed:s+71});const j=I.swing??Math.sin(i*3),Z=[I.b[0]+.12+.1*j,I.b[1]+.2-.08*j];z(e,fe([I.b,[I.b[0]+.14,I.b[1]-.02+.06*j],Z],8),.006,{color:G,alpha:.8,taperIn:0,taperOut:.6,seed:s+72})}}},g=ur(c,[-.03,.01]),k=ur(c,[.03,.01]),T=ur(h,[-.05,.02]),A=ur(h,[.05,.02]),[N]=cn(g,t.fl,o.th,o.sn,-1),[S]=cn(k,t.fr,o.th,o.sn,-1),[B]=cn(T,t.hl,o.ua,o.fa,1),[V]=cn(A,t.hr,o.ua,o.fa,1),W=I=>ht(I,"#3a3530",.18),L=t.kind==="boy"?kt:p;Zt(e,[g,N,t.fl],w*1.05,W((t.kind==="boy",p)),a,s+1,t.kind==="boy"?W(kt):void 0,(t.kind==="boy",0)),t.kind==="boy"&&Zt(e,[N,t.fl],w*.95,W(kt),a,s+2),wo(e,t.fl,a,t.kind),Zt(e,[T,B,t.hl],w*.95,W(b),a,s+3,W(kt),t.kind==="boy"?.05:.55),bo(e,t.hl,w,a),y();const J=[Ae([-o.wTop,o.sh+.02+f],c,u),Ae([o.wTop*.9,o.sh+.02+f],c,u),Ae([o.wBot,c[1]+.03],c,u),Ae([-o.wBot*.95,c[1]+.03],c,u)],U=[Ae([-o.wBot*.95,c[1]-.01],c,u),Ae([o.wBot,c[1]-.01],c,u),ur(k,[.03,t.kind==="boy"?.09:.06]),ur(g,[-.04,t.kind==="boy"?.09:.06])];if(ue(e,U,p,.95,{lw:a}),t.kind==="boy"?(Zt(e,[k,S],w*1.1,p,a,s+4),Zt(e,[S,t.fr],w,kt,a,s+5)):Zt(e,[k,S,t.fr],w*1.08,L,a,s+4),wo(e,t.fr,a,t.kind),ue(e,J,b,.96,{lw:a}),st(e,J,a*1.2,{seed:s+9,alpha:.8,breaks:[.05,.45,.95]}),t.kind==="boy"){const I=Ae([-.04,o.sh+.02+f],c,u),j=Ae([.05,o.sh+.02+f],c,u),Z=Ae([.005,o.sh+.09+f],c,u);e.fillStyle=P("#f4ead4",.95),e.beginPath(),e.moveTo(I[0],I[1]),e.lineTo(j[0],j[1]),e.lineTo(Z[0],Z[1]),e.closePath(),e.fill()}else{const I=Ae([-o.wBot,c[1]+0],c,u),j=Ae([o.wBot,c[1]+0],c,u);z(e,[I,qt(I,j,.5),j],.025,{color:"#2a2622",alpha:.8,taperIn:0,taperOut:0,seed:s+10});const Z=Ae([.02,o.sh+.03+f],c,u),re=Ae([.035,c[1]+.02],c,u);z(e,[Z,qt(Z,re,.5),re],a*.9,{alpha:.6,seed:s+11})}e.strokeStyle=kt,e.lineWidth=t.kind==="boy"?.05:.045,e.lineCap="round",e.beginPath(),e.moveTo(d[0],d[1]),e.lineTo(qt(d,m,.6)[0],qt(d,m,.6)[1]),e.stroke(),Ed(e,m,o.hr,t,a,s),Zt(e,[A,V,t.hr],w,b,a,s+12,kt,t.kind==="boy"?.05:.55),bo(e,t.hr,w,a),e.restore()}function bo(e,t,r,n){e.fillStyle=kt,e.beginPath(),e.arc(t[0],t[1],r*.62,0,Math.PI*2),e.fill(),e.strokeStyle=P(G,.75),e.lineWidth=n,e.stroke()}function wo(e,t,r,n){const s=n==="boy"?.05:.055;e.fillStyle=P(n==="boy"?"#8a6a3a":"#2e2a26",.95),e.beginPath(),e.ellipse(t[0]+s*.4,t[1]-.008,s,.018,0,0,Math.PI*2),e.fill(),e.strokeStyle=P(G,.6),e.lineWidth=r,e.stroke()}function Ed(e,t,r,n,s,i){const o=n.t;e.save(),e.translate(t[0],t[1]),e.rotate(n.tilt??0);const a=n.kind;e.fillStyle=kt,e.beginPath(),e.ellipse(0,0,r,r*1.05,0,0,Math.PI*2),e.fill();const l=[];for(let g=0;g<14;g++){const k=g/14*Math.PI*2;l.push([Math.cos(k)*r,Math.sin(k)*r*1.05])}st(e,l,s*1.1,{seed:i+20,alpha:.8,breaks:[.08,.5,.95]}),e.fillStyle=P("#1f1c1a",.95),e.beginPath(),a==="boy"?(e.ellipse(-r*.12,-r*.3,r*.98,r*.78,-.15,Math.PI*.95,Math.PI*2.08),e.fill(),z(e,fe([[-r*.2,-r*.95],[-r*.1,-r*1.45],[r*.25,-r*1.7]],6),r*.32,{color:"#1f1c1a",alpha:.95,taperIn:.1,taperOut:.8,seed:i+21}),e.fillStyle=P("#c0473a",.95),e.beginPath(),e.arc(-r*.16,-r*1.02,r*.14,0,Math.PI*2),e.fill()):(e.ellipse(-r*.15,-r*.35,r*.95,r*.72,-.1,Math.PI*.92,Math.PI*2.05),e.fill(),(a==="woman"||n.hat==="bun")&&(e.beginPath(),e.arc(-r*.85,-r*.45,r*.38,0,Math.PI*2),e.fill())),e.fillStyle=kt,e.beginPath(),e.ellipse(-r*.2,r*.05,r*.16,r*.22,0,0,Math.PI*2),e.fill(),e.strokeStyle=P(G,.6),e.lineWidth=s*.8,e.beginPath(),e.arc(-r*.2,r*.05,r*.14,-1.2,1.6),e.stroke();const f=n.expr??{},c=r*.5,u=-r*.08,h=(o+i*.71)%3.7<.12,d=(n.look??0)*r*.08;e.fillStyle=G,e.strokeStyle=G,e.lineWidth=s*1.2,e.lineCap="round";const m=f.eye??"open";h&&m!=="closed"&&m!=="happy"?(e.beginPath(),e.moveTo(c-r*.1,u),e.lineTo(c+r*.1,u),e.stroke()):m==="closed"||m==="happy"?(e.beginPath(),e.arc(c,u+r*.05,r*.11,Math.PI*1.1,Math.PI*1.9),e.stroke()):m==="sad"?(e.beginPath(),e.arc(c,u-r*.02,r*.1,.2,Math.PI-.2),e.stroke()):m==="wide"?(e.fillStyle="#fbf7ee",e.beginPath(),e.ellipse(c,u,r*.14,r*.17,0,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle=G,e.beginPath(),e.arc(c+r*.03+d,u,r*.065,0,Math.PI*2),e.fill()):(e.fillStyle=G,e.beginPath(),e.ellipse(c+(m==="sly"?r*.06:0)+d,u,r*.065,r*.085,0,0,Math.PI*2),e.fill(),m==="sly"&&(e.beginPath(),e.moveTo(c-r*.13,u-r*.07),e.lineTo(c+r*.12,u-r*.1),e.stroke()));const b=f.brow??"none";b!=="none"&&(e.lineWidth=s*1.4,e.beginPath(),b==="angry"?(e.moveTo(c-r*.14,u-r*.32),e.lineTo(c+r*.16,u-r*.2)):b==="worried"?(e.moveTo(c-r*.14,u-r*.2),e.lineTo(c+r*.15,u-r*.32)):(e.moveTo(c-r*.14,u-r*.36),e.quadraticCurveTo(c,u-r*.44,c+r*.15,u-r*.36)),e.stroke()),e.lineWidth=s,e.beginPath(),e.moveTo(r*.92,-r*.02),e.quadraticCurveTo(r*1.06,r*.12,r*.9,r*.17),e.stroke(),a!=="man"&&(e.fillStyle=P("#e07a6a",.35),e.beginPath(),e.arc(r*.45,r*.32,r*.16,0,Math.PI*2),e.fill());const p=f.mouth??"smile",w=r*.68,y=r*.5;if(e.fillStyle=P("#7a2a22",.95),e.strokeStyle=G,e.lineWidth=s*1.1,e.beginPath(),p==="smile")e.arc(w-r*.05,y-r*.12,r*.16,.35,Math.PI-.6),e.stroke();else if(p==="line")e.moveTo(w-r*.15,y),e.lineTo(w+r*.08,y-r*.02),e.stroke();else if(p==="frown")e.arc(w-r*.05,y+r*.1,r*.14,Math.PI+.5,Math.PI*2-.4),e.stroke();else if(p==="chew"){const g=Math.abs(Math.sin(o*6));e.ellipse(w,y,r*.1,r*(.03+.05*g),0,0,Math.PI*2),e.fill(),e.stroke()}else if(p==="open"||p==="shout"){const g=p==="shout"?.22:.12;e.ellipse(w+r*.02,y,r*.12,r*g,0,0,Math.PI*2),e.fill(),e.stroke()}else if(p==="laugh"){const g=.6+.4*Math.abs(Math.sin(o*9));e.moveTo(w-r*.2,y-r*.06),e.quadraticCurveTo(w,y+r*.36*g,w+r*.2,y-r*.08),e.closePath(),e.fill(),e.stroke()}else if(p==="cry"){const g=.8+.2*Math.sin(o*7);e.moveTo(w-r*.18,y+r*.1),e.quadraticCurveTo(w,y-r*.14*g,w+r*.16,y+r*.12),e.quadraticCurveTo(w,y+r*.02,w-r*.18,y+r*.1),e.fill(),e.stroke()}if(n.hat==="straw"){e.fillStyle=P("#d8b45a",.95),e.beginPath(),e.moveTo(-r*1.6,-r*.45),e.quadraticCurveTo(0,-r*1.85,r*1.6,-r*.45),e.quadraticCurveTo(0,-r*.7,-r*1.6,-r*.45),e.fill(),e.strokeStyle=P(G,.7),e.lineWidth=s,e.stroke();for(let g=-3;g<=3;g++)e.beginPath(),e.moveTo(g*r*.18,-r*1.4),e.lineTo(g*r*.5,-r*.62),e.strokeStyle=P("#8a6a2a",.5),e.stroke()}else n.hat==="scarf"&&(e.fillStyle=P("#f2ece0",.97),e.beginPath(),e.ellipse(-r*.05,-r*.55,r*1.04,r*.5,0,Math.PI*.98,Math.PI*2.02),e.fill(),e.strokeStyle=P(G,.6),e.lineWidth=s,e.stroke(),e.beginPath(),e.moveTo(-r*.95,-r*.5),e.quadraticCurveTo(-r*1.3,-r*.3,-r*1.25,r*.1),e.lineWidth=s*2.5,e.strokeStyle=P("#e8e0d0",.95),e.stroke());if(n.tears&&n.tears>0)for(let g=0;g<2;g++){const k=(o*1.4+g*.5)%1;e.fillStyle=P("#7fa4c4",.85*n.tears),e.beginPath(),e.ellipse(c+r*.05,u+r*.2+k*r*1.3,r*.06,r*.1,0,0,Math.PI*2),e.fill()}n.sweat&&n.sweat>0&&(e.fillStyle=P("#9cc0d8",.9*n.sweat),e.beginPath(),e.ellipse(-r*.6,-r*.7+o%.8*r*.5,r*.08,r*.13,.3,0,Math.PI*2),e.fill()),e.restore()}const qe={stand:(e="boy")=>e==="boy"?{hl:[-.05,-.39],hr:[.07,-.4],fl:[-.04,0],fr:[.05,0]}:{hl:[-.06,-.46],hr:[.07,-.47],fl:[-.05,0],fr:[.06,0]},walk:(e,t="boy",r=0)=>{const n=(.09+.07*r)*(t==="boy"?1:1.15),s=.04+.06*r,i=Math.sin(e),o=Math.sin(e+Math.PI),a=t==="boy"?-.42:-.5;return{fl:[n*i,-Math.max(0,Math.cos(e))*s],fr:[n*o,-Math.max(0,Math.cos(e+Math.PI))*s],hl:[-n*.8*i+.02,a+r*.08],hr:[-n*.8*o+.04+r*.06,a+r*.1],bob:-Math.abs(Math.sin(e))*.015*(1+r),lean:.06+.16*r}}};function Bt(e,t){const r=t.s,n=t.face??1,s=t.seed??1,i=t.t,o=t.run??0,a=t.walk??0,l=i*(o>0?11:a>0?5:0)+s,f=o>0?Math.abs(Math.sin(l))*.05*o:a>0?Math.abs(Math.sin(l))*.012:.004*Math.sin(i*2+s),c=Math.max(1,r*.016)/r;e.save(),e.globalAlpha=t.alpha??1,e.translate(t.x,t.y),e.scale(n*r,r);const u=-.36-f;[[-.2,0],[-.12,Math.PI],[.14,Math.PI*.5],[.22,Math.PI*1.5]].forEach(([S,B],V)=>{const W=o>0?Math.sin(l+B)*.1*o:a>0?Math.sin(l+B)*.04:0,L=[S,u+.1],J=[S+W,-0-(o>0?Math.max(0,Math.cos(l+B))*.04:0)];z(e,[L,qt(L,J,.5),J],.045,{color:V<2?"#4a4440":"#2e2a27",alpha:.92,taperIn:0,taperOut:.1,seed:s+V})});const d=.33,m=t.lamb?.21:.2,b=[],p=13;for(let S=0;S.5?Math.sin(i*7+s)*.01:0,k=E(.36,.38,w)+o*.05,T=E(u-.1-y*.06,-.08+g,w),A=E(.35,1.25,w)-o*.2-y*.35;e.save(),e.translate(k,T),e.rotate(A),e.fillStyle=P("#fbf7ee",1),e.beginPath(),e.arc(-.03,-.07,.06,0,Math.PI*2),e.fill();const N=t.lamb?"#5a4c44":"#2e2a27";e.fillStyle=P(N,.96),e.beginPath(),e.ellipse(.04,0,.11,.07,.15,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(-.05,.02,.06,.022,-.6-.3*(t.scared??0),0,Math.PI*2),e.fill(),e.fillStyle="#f6efdf",e.beginPath(),e.arc(.04,-.02,t.scared?.022:.016,0,Math.PI*2),e.fill(),e.fillStyle=G,e.beginPath(),e.arc(.045,-.02,.008,0,Math.PI*2),e.fill(),(t.baa??0)>0&&(e.fillStyle=P("#8a3a32",.95),e.beginPath(),e.ellipse(.13,.03,.02,.018*t.baa,0,0,Math.PI*2),e.fill()),e.restore(),e.restore()}function fs(e,t){const r=t.s,n=t.face??1,s=t.seed??3,i=t.t,o=t.mode??"stand",a=Math.max(1.1,r*.012)/r;e.save(),e.globalAlpha=t.alpha??1,e.translate(t.x,t.y),e.scale(n*r,r);const l=o==="run",f=o==="sneak",c=i*(l?12:f?3:0)+s,u=f?.07:0,h=l?Math.abs(Math.sin(c))*.04:0,d=-.4+u-h,m=l?1.12:1,b="#6d6f73",p="#3f4145",w="#b9b6ad",y=l?-.1:f?.12:.05+.03*Math.sin(i*2);ue(e,[[-.32*m,d-.04],[-.55*m,d+.02-y],[-.68*m,d+.12-y],[-.6*m,d+.16-y],[-.4*m,d+.08]],b,.92,{lw:a}),z(e,fe([[-.32*m,d-.04],[-.55*m,d+.02-y],[-.68*m,d+.12-y]],6),a*2,{color:p,alpha:.8,dry:.7,seed:s+1});const g=[[-.22,0,!1],[.2,Math.PI,!1],[-.17,Math.PI*.6,!0],[.26,Math.PI*1.6,!0]];for(const[U,I,j]of g)j||k(U,I,p);function k(U,I,j){const Z=U*m,re=l?Math.sin(c+I)*.16:f?Math.sin(c+I)*.05:0,ie=[Z,d+.08],O=[Z+re*.5+(Z<0?-.04:.02),d+.22-u*.5],F=[Z+re,-(l?Math.max(0,Math.cos(c+I))*.06:0)];z(e,[ie,O,F],.055,{color:j,alpha:.95,taperIn:0,taperOut:.35,seed:s+U*10})}const T=[[.3*m,d-.1],[.05,d-.14],[-.25*m,d-.12],[-.36*m,d-.02],[-.28*m,d+.12],[0,d+.12],[.26*m,d+.1],[.36*m,d+0]];ue(e,T,b,.95,{lw:a}),le(e,0,d+.06,.3,w,.35,1.3,.3),st(e,T,a*1.3,{seed:s+2,alpha:.85,breaks:[.02,.4,.7,.98]});for(let U=0;U<6;U++){const I=(-.3+U*.11)*m;z(e,[[I,d-.13],[I+.05,d-.17-.02*oe(U+s)],[I+.1,d-.12]],.025,{color:p,alpha:.75,dry:.8,seed:s+10+U,taperIn:.1})}for(const[U,I,j]of g)j&&k(U,I,b);const A=o==="howl",N=o==="snarl",S=.4*m,B=d-.12+u*.6+(l?.04:0),V=A?-.8:f?.25:l?.1:-.05;e.save(),e.translate(S,B),e.rotate(V),ue(e,[[-.14,.12],[-.06,-.07],[.06,-.06],[.08,.08],[-.02,.16]],b,.95,{lw:a});const W=[[-.06,-.08],[.06,-.09],[.24,-.03],[.26,.01],[.2,.04],[.06,.07],[-.06,.05]];ue(e,W,b,.97,{lw:a}),st(e,W,a*1.2,{seed:s+3,alpha:.9,breaks:[.05,.55,.95]});for(const[U,I]of[[-.03,.85],[.03,1]])e.fillStyle=P(I<1?p:b,.97),e.beginPath(),e.moveTo(U-.03,-.07),e.lineTo(U+.01,-.2*I),e.lineTo(U+.05,-.08),e.closePath(),e.fill(),e.strokeStyle=P(G,.8),e.lineWidth=a,e.stroke();e.fillStyle=G,e.beginPath(),e.arc(.255,-.015,.018,0,Math.PI*2),e.fill();const L=A?.07:N?.05+.01*Math.sin(i*20):l?.03:0;if(L>0){if(e.fillStyle=P("#5a1e1a",.95),e.beginPath(),e.moveTo(.06,.04),e.lineTo(.24,.02),e.lineTo(.2,.02+L),e.lineTo(.06,.05),e.closePath(),e.fill(),N||l){e.fillStyle="#f6efdf";for(let U=0;U<3;U++){const I=.12+U*.035;e.beginPath(),e.moveTo(I,.025),e.lineTo(I+.01,.045),e.lineTo(I+.02,.025),e.fill()}}}else e.strokeStyle=G,e.lineWidth=a,e.beginPath(),e.moveTo(.08,.045),e.lineTo(.22,.03),e.stroke();const J=t.glow??(f?.6:.3);le(e,.08,-.035,.05,"#c8f05a",.6*J),e.fillStyle="#c8e050",e.beginPath(),e.ellipse(.08,-.035,.025,.014,-.25,0,Math.PI*2),e.fill(),e.fillStyle=G,e.beginPath(),e.ellipse(.085,-.035,.006,.012,0,0,Math.PI*2),e.fill(),e.strokeStyle=G,e.lineWidth=a*1.3,e.beginPath(),e.moveTo(.04,-.06),e.lineTo(.12,-.045),e.stroke(),t.carry&&(e.save(),e.translate(.16,.16),e.rotate(-.2),Bt(e,{x:0,y:0,s:.42,t:i,face:-1,lamb:!0,scared:1,seed:9}),e.restore()),e.restore(),e.restore()}function yo(e,t,r,n,s,i){if(i<=0)return;const o=(s+.3)%2.6<.12?.15:1;for(const a of[-.5,.5])le(e,t+a*n,r,n*.9,"#c8f05a",.35*i),e.fillStyle=P("#d8f070",i),e.beginPath(),e.ellipse(t+a*n,r,n*.32,n*.16*o,a*.4,0,Math.PI*2),e.fill(),e.fillStyle=P(G,i),e.beginPath(),e.ellipse(t+a*n,r,n*.06,n*.14*o,0,0,Math.PI*2),e.fill()}function Js(e,t,r,n,s){const i=de(s),o=[[t-n*.5,r],[t-n*.5,r-n*.45],[t+n*.5,r-n*.45],[t+n*.5,r]];ue(e,o,"#d9cba8",.92),st(e,o,Math.max(1.2,n*.012),{seed:s,alpha:.75}),ue(e,[[t-n*.1,r],[t-n*.1,r-n*.3],[t+n*.08,r-n*.3],[t+n*.08,r]],"#4a3a2a",.85),ue(e,[[t+n*.22,r-n*.2],[t+n*.22,r-n*.33],[t+n*.38,r-n*.33],[t+n*.38,r-n*.2]],"#3a3530",.8);const a=[[t-n*.66,r-n*.4],[t-n*.3,r-n*.82],[t+n*.3,r-n*.84],[t+n*.66,r-n*.4]];ue(e,a,"#b8955a",.92);for(let l=0;l<26;l++){const f=i(),c=E(t-n*.62,t+n*.62,f),u=r-n*.8+Math.abs(f-.5)*n*.7;z(e,[[c-n*.02,u],[c+n*.01,E(u,r-n*.42,.6)],[c+n*.02,r-n*.4]],Math.max(1,n*.01),{color:"#7a5a2a",alpha:.55,seed:s+l,taperIn:.1,dry:.4})}z(e,[[t-n*.68,r-n*.39],[t,r-n*.43],[t+n*.68,r-n*.39]],Math.max(1.5,n*.025),{color:"#6a4a22",alpha:.8,seed:s+50,dry:.5})}function Td(e,t,r,n,s,i,o=.4){for(let a=0;a<6;a++){const l=(s*.18+a/6+oe(i))%1;le(e,t+Math.sin(l*5+a)*n*.15+l*n*.4,r-l*n*1.6,n*(.08+l*.25),"#9a958c",o*(1-l))}}function za(e,t,r,n,s,i,o){const a=de(o);for(let l=0;l{const b=o-m*f;if(b<=0)return;const p=$(b/.5),w=r+s*(i*.45+m*i*.98*p+b*i*.25),y=n-i*.1*Math.sin(m*1.3)-b*i*.08,g=E(.35,1,p)*(1+.04*Math.sin(b*30)*(1-p));let k=Math.min(1,b*5)*(a.fade??1);if(e.save(),e.translate(w,y),e.rotate(-.08*s+.05*Math.sin(m+b*3)),u>0){const T=$(u),A=de(h*31+m);k*=1-T*.9;for(let N=0;N<4;N++){const S=(N%2?1:-1)*i*.5,B=(N<2?-1:1)*i*.5;e.save(),e.beginPath(),e.rect(N%2?0:-i,N<2?-i:0,i,i),e.clip(),e.translate(S*T*.8+T*i*1.5*s*(.5+A()),B*T*.6-T*i*.6*A()),e.rotate(T*(A()-.5)*1.5),e.globalAlpha*=k,e.fillStyle=c,e.font=`${i*g}px ${Rt}`,e.textAlign="center",e.textBaseline="middle",e.fillText(d,0,0),e.restore()}for(let N=0;N<6;N++){const S=de(h+m*7+N);e.fillStyle=P(c,.6*T*(1-T)),e.beginPath(),e.arc(s*T*i*(1+S()*2),(S()-.5)*i*1.5*T,i*.04*(1+S()),0,Math.PI*2),e.fill()}}else e.globalAlpha*=k,e.font=`${i*g}px ${Rt}`,e.textAlign="center",e.textBaseline="middle",e.shadowColor=P(c,.35),e.shadowBlur=i*.06,e.lineJoin="round",e.strokeStyle=P(Ge,.7),e.lineWidth=i*.08,e.strokeText(d,0,0),e.fillStyle=c,e.fillText(d,0,0);e.restore()})}function hs(e,t,r,n,s,i,o=1){for(let a=0;a<3;a++){const l=(s*1.6+a/3)%1;e.strokeStyle=P(G,o*.6*(1-l)),e.lineWidth=Math.max(1.2,n*.03),e.beginPath(),e.arc(t,r,n*(.3+l*1.4),Math.PI-.6,Math.PI+.6),e.stroke()}}const R={title:{drop:.32,write:1.1,seal:4.55},village:{boyIn:4.8},bored:{chew:6.1,yawn:8,idea:9.8,sly:10.3},shout1:{hop:.6,cup:2.4,shouts:[4.2,5.6,7]},run1:{hear:.6,grab:2.8,run:4.6,arrive:7.3},laugh1:{laugh:.5,angry:4.6,leave:6.6},shout2:{shouts:[4.1,5.5],run:7.2},warn:{point:3.1},wolf:{dark:.3,bush:4,eyes:4.9,creep:7.3,reveal:9.3},cry:{toRock:2.3,shouts:[4.1,5.3,7.2]},noone:{look:.6,shake:2.6,wave:5.2,work:8},chaos:{dash:.4,scatter:1.8,grab:4},regret:{hug:1.8,cry:4.4},moral:{write:4.9,end:8.2}},Le=e=>e.u*(e.portrait?.95:1),We=(e,t,r,n,s={})=>({z:t*(e.portrait?1.22:1),x:r,y:n,...s});function wr(e,t,r,n,s,i,o){const a=e.createLinearGradient(0,0,0,r);a.addColorStop(0,P(n,s)),a.addColorStop(1,P(i,o)),e.fillStyle=a,e.fillRect(0,0,t,r)}function La(e,t,r,n,s,i,o,a=18){const l=[];for(let c=0;c<=24;c++)l.push([c/24*t,r+mt(c*.6,o)*a]);const f=e.createLinearGradient(0,r-a,0,n);f.addColorStop(0,P(s,i)),f.addColorStop(1,P(s,i*.5)),e.fillStyle=f,e.beginPath(),e.moveTo(0,n);for(const c of l)e.lineTo(c[0],c[1]);e.lineTo(t,n),e.closePath(),e.fill(),z(e,l,2.2,{color:s,alpha:Math.min(1,i*1.3),seed:o,jitter:.6,dry:.5})}function qa(e,t,r,n,s,i,o,a,l){const f=de(a);for(let c=0;c{const d=(h-(f.length-1)/2)*s*c,m=l.vertical?r:r+d,b=l.vertical?n+d:n,p=M(i+h*o,i+h*o+o*1.2,a);p<=0||(e.save(),e.beginPath(),e.rect(m-s*.7,b-s*.7,s*1.4,s*1.4*me(p)),e.clip(),e.shadowColor=P(G,.35),e.shadowBlur=s*.06,e.fillStyle=l.color??G,e.fillText(u,m,b),e.restore())}),e.restore()}function ms(e,t,r,n,s,i,o=G){s<=0||(e.save(),e.globalAlpha=s,e.fillStyle=o,e.font=`${n}px ${Rt}`,e.textAlign="center",e.textBaseline="middle",e.fillText(i,t,r),e.restore())}function Cd(e,t,r,n,s){if(!(s<=0))for(let i=0;i<4;i++){const o=i*Math.PI/2+.3;z(e,[[t+Math.cos(o)*n*.3,r+Math.sin(o)*n*.3],[t+Math.cos(o)*n,r+Math.sin(o)*n]],n*.12,{alpha:s*.85,taperIn:.1,taperOut:.6,seed:i})}}function ps(e,t){const r=.5+.5*Math.sin(e),n=E(-2.05,.95,Math.pow(r,1.6)),s=[.06,-.6],i=[Math.cos(n),Math.sin(n)],o=[s[0]-i[0]*.12,s[1]-i[1]*.12],a=[s[0]+i[0]*.78,s[1]+i[1]*.78];return{hl:[s[0]-i[0]*.08,s[1]-i[1]*.08],hr:[s[0]+i[0]*.14,s[1]+i[1]*.14],fl:[-.1,0],fr:[.12,0],lean:.08+.18*r,tool:{kind:"hoe",a:o,b:a}}}function ln(e,t,r,n="hoe"){return{...qe.walk(e,t,r),hr:[.1,-.84],tool:{kind:n,a:n==="hoe"?[-.3,-.62]:[-.42,-.77],b:n==="hoe"?[.42,-.98]:[.42,-.8]}}}function zt(e,t="still",r=0){const n=qe.stand(e);return t==="hips"?{...n,hl:[-.12,-.5],hr:[.13,-.5]}:t==="point"?{...n,hr:[.34,-.8+.02*Math.sin(r*8)],hl:[-.12,-.5],lean:.08}:t==="wave"?{...n,hr:[.22+.06*Math.sin(r*9),-.86],hl:[-.06,-.46]}:t==="shade"?{...n,hr:[.1,-.93]}:n}const xt=[{kind:"man",hat:"scarf",jacket:"#5d7385",pants:"#3a4048",tool:"hoe"},{kind:"man",hat:"straw",jacket:"#6b5a48",pants:"#3e3a36",tool:"pole"},{kind:"woman",hat:"bun",jacket:"#8a5a62",pants:"#3a3a44",tool:"hoe"},{kind:"man",hat:"none",jacket:"#4f6a5a",pants:"#2e3438",tool:"hoe"}];function dr(e,t,r,n,s,i,o,a={}){const l=xt[t%xt.length];Da(e,{x:r,y:n,s,t:i,kind:l.kind,hat:l.hat,jacket:l.jacket,pants:l.pants,seed:10+t*7,...qe.stand(l.kind),...o,...a})}function Ze(e,t,r,n,s,i,o={}){Da(e,{x:t,y:r,s:n,t:s,kind:"boy",seed:3,...qe.stand("boy"),...i,...o})}const bs={hl:[.16,-.8],hr:[.15,-.78],fl:[-.08,0],fr:[.1,0],lean:.12},vo=e=>({hl:[.05,-.5],hr:[.08,-.52],fl:[-.06,0],fr:[.08,0],lean:-.14-.05*Math.abs(Math.sin(e*9)),bob:-.01*Math.abs(Math.sin(e*9)),expr:{eye:"happy",mouth:"laugh"}}),ws=e=>({hl:[-.14,-1+.02*Math.sin(e*20)],hr:[.2,-.98],fl:[-.09,0],fr:[.09,0],lean:-.05,shiver:1,expr:{eye:"wide",mouth:"shout",brow:"worried"}}),Pd={hipY:-.13,fl:[.22,0],fr:[.26,-.01],hl:[.2,-.34],hr:[.22,-.32],lean:.2},ke=(e,t)=>e.gy+30*e.u+110*e.u*Math.pow((t-e.W*.48)/(e.W*.75),2);function gt(e,t={}){return r=>{const{g:n,W:s,H:i,u:o,gy:a,X:l}=r,f=t.seed??1;e==="day"&&wr(n,s,a,"#9fbccb",.22,"#ffffff",0),e==="storm"&&wr(n,s,a+40*o,"#3e464e",.45,"#8a9094",.2),e==="sunset"&&(wr(n,s,a,"#e08a50",.35,"#f0c890",.45),rr(n,l(-.35),a-60*o,110*o,"#c8463a",.9)),e==="dusk"&&wr(n,s,a,"#4a5068",.4,"#d8a878",.35);const c=e==="storm"?"#4a5258":e==="sunset"?"#7a5048":e==="dusk"?"#3a3a48":"#7d8a8c";Ee(n,s,a-40*o,170*o,c,e==="day"?.3:.45,3+f);const u=l(-.8),h=a+10*o;for(let p=0;p<4;p++)Js(n,u+p*46*o-p%2*10*o,h+p%2*14*o,40*o,30+p);const d=[];for(let p=0;p<=30;p++){const w=p/30*s;d.push([w,ke(r,w)+mt(p*.7,f)*6*o])}const m=e==="storm"?"#6a7a5a":e==="sunset"?"#8a8a50":e==="dusk"?"#4a5a48":"#8fae6a",b=n.createLinearGradient(0,a,0,i);b.addColorStop(0,P(m,.6)),b.addColorStop(1,P(ht(m,"#4a5a32",.4),.5)),n.fillStyle=b,n.beginPath(),n.moveTo(0,i);for(const p of d)n.lineTo(p[0],p[1]);n.lineTo(s,i),n.closePath(),n.fill(),z(n,d,3*o,{color:"#3e5a2e",alpha:.7,seed:f,dry:.5,jitter:.5}),qa(n,s,a+80*o,i,30,"#4f6a3e",.35,f+9,o),e==="day"&&Sd(n,0,s,a+90*o,i-20*o,40,f+4),t.tree!==!1&&Wa(n,l(.62),ke(r,l(.62))+10*o,520*o,0,17,{color:e==="storm"?"#4a6a3e":e==="sunset"?"#6a7040":"#5f8a4c"}),t.rock!==!1&&ja(n,l(.05),ke(r,l(.05))+40*o,190*o,110*o,5)}}const ft=e=>[e.X(.05),ke(e,e.X(.05))+40*e.u-105*e.u];function ko(e=!1){return t=>{const{g:r,W:n,H:s,u:i,gy:o,X:a}=t;wr(r,n,o,e?"#4e565e":"#a9c3c9",e?.4:.2,"#ffffff",0),Ee(r,n,o-70*i,200*i,e?"#4a5258":"#7d8a8c",.35,41),Ee(r,n,o-20*i,120*i,e?"#3a4248":"#5e6c62",.4,47),La(r,n,o-10*i,s,e?"#7a8a6a":"#a8b878",.35,21),za(r,a(-.1),o+30*i,n+20*i,s,6,9);for(const[f,c,u,h]of[[-.88,40,150,1],[-.62,70,130,2],[-.92,160,170,3]])Js(r,a(f),o+c*i,u*i,h);const l=fe([[a(-.55),s+20*i],[a(-.35),o+120*i],[a(-.2),o+10*i],[a(.1),o-60*i]],10);for(let f=0;f[e.X(.05+t*.26),e.gy+(120+t%2*70)*e.u];function Md(e){const{g:t,W:r,H:n,u:s,X:i}=e;wr(t,r,n*.5,"#9fbccb",.22,"#ffffff",0),Ee(t,r,n*.42,200*s,"#7d8a8c",.3,61);const o=[[-20*s,n*.62],[i(-.3),n*.5],[i(.3),n*.32],[i(.75),n*.24],[r+20*s,n*.26],[r+20*s,n+20*s],[-20*s,n+20*s]];ue(t,o,"#8fae6a",.55),z(t,fe(o.slice(0,5),10),3*s,{color:"#3e5a2e",alpha:.7,seed:3,dry:.5}),za(t,-20*s,n*.74,i(-.1),n+20*s,4,13);for(const[l,f,c,u]of[[-.9,.72,120,1],[-.7,.8,100,2]])Js(t,i(l),n*f,c*s,u);const a=Rs(e);for(let l=0;lfe([[e.X(-.85),e.H*.92],[e.X(-.45),e.H*.68],[e.X(-.05),e.H*.55],[e.X(.3),e.H*.4],[e.X(.6),e.H*.3]],12),Dr=(e,t)=>{const r=Re(t)*(e.length-1),n=Math.min(e.length-2,Math.floor(r)),s=r-n;return[E(e[n][0],e[n+1][0],s),E(e[n][1],e[n+1][1],s)]},$a=[{f:-.55,dy:60,s:1,face:1,seed:1},{f:-.32,dy:120,s:1.1,face:-1,seed:2},{f:-.15,dy:40,s:.9,face:1,seed:3},{f:.28,dy:140,s:1.15,face:-1,seed:4},{f:.45,dy:70,s:.95,face:1,seed:5},{f:.82,dy:110,s:1.05,face:-1,seed:6},{f:-.75,dy:150,s:.7,face:1,seed:7}];function Jt(e,t=()=>({})){const{g:r,u:n,t:s,X:i}=e,o=Le(e);$a.forEach((a,l)=>{const f=t(a,l),c=i(a.f)+(f.dx??0),u=ke(e,i(a.f))+a.dy*n+(f.dy??0),h=f.graze??(.5+.5*Math.sin(s*.5+a.seed*2)>.35?1:.2);Bt(r,{x:c,y:u,s:150*o*a.s,t:s,face:f.face??a.face,seed:a.seed,graze:h,look:f.look,run:f.run,alpha:f.alpha,baa:f.baa,scared:f.scared})})}const Nd={title:{label:"片头",draw(e){const{g:t,W:r,H:n,u:s,t:i,portrait:o}=e,a=R.title,l=r/2,f=o?n*.37:n*.42;i0){const w=(o?420:350)*s*c;ue(t,nr(l,f,w,9,.06,32),"#8a9a7a",.14+.16*(1-c),{edge:1.4}),go(10,71,y=>{const g=y()*Math.PI*2,k=(w*.9+60*s*y())*me(M(a.drop,.8,i));t.fillStyle=P(G,.6*(1-.6*c)),t.beginPath(),t.arc(l+Math.cos(g)*k,f+Math.sin(g)*k*.9,(3+y()*8)*s,0,Math.PI*2),t.fill()})}const u=$(M(.8,2.6,i));if(u>0){t.save(),t.globalAlpha=u;const w=o?n*.82:n*.88;Ee(t,r,w-40*s,120*s,"#7d8a8c",.3,5,{soft:1}),La(t,r,w,n,"#8fae6a",.45,3,10*s),us(t,r*.86,w+10*s,160*s,4,.7),t.restore(),yo(t,r*.86,w-20*s,14*s,i,$(M(3.6,4.2,i))*u)}const h=(o?230:210)*s;xo(t,"狼来了",l,f,h,a.write,.45,i,{vertical:o});const d=$(M(2.9,3.6,i));if(d>0){t.save(),t.globalAlpha=d,t.font=`${46*s}px ${xr}`,t.fillStyle=P(G,.85),t.textAlign="center",t.textBaseline="middle";const w=o?f+h*1.95:f+h*.95;t.fillText("伊 索 寓 言",l,w),z(t,[[l-150*s,w+38*s],[l,w+44*s],[l+150*s,w+36*s]],4*s,{alpha:.5*d,seed:8,dry:.6}),t.restore()}const m=M(a.seal,a.seal+.3,i);if(m>0){const w=E(1.5,1,me(m)),y=o?l+h*.78:l+h*1.82,g=o?f+h*1.15:f+h*.32;t.save(),t.translate(y,g),t.scale(w,w),t.rotate(-.04),Hr(t,0,0,92*s,["寓言"],{alpha:Math.min(1,m*2)}),t.restore()}const b=o?n*.8:n*.92,p=M(1.8,6.6,i);if(p>0){const w=E(-150*s,r*.6,p);for(let y=0;y<3;y++)Bt(t,{x:w+150*s-y*120*s,y:b+y%2*8*s,s:85*s,t:i,walk:1,seed:y+1,graze:0});Ze(t,w-260*s,b,170*s,i,qe.walk(i*6,"boy"),{tool:{kind:"whip",a:[.07,-.42],b:[.28,-.72]},expr:{mouth:"smile"}})}},enterAt:[.5,.45]},village:{label:"山村",bg:ko(!1),draw(e){const{g:t,u:r,t:n,gy:s,X:i,H:o}=e,a=Le(e);for(const[u,h,d]of[[-.88,40,150],[-.62,70,130]])Td(t,i(u)+d*.3*r,s+h*r-d*.8*r,60*r,n,u*10);for(let u=0;u<3;u++){const[h,d]=ys(e,u);dr(t,u,h,d,330*a,n,ps(n*2.2+u*1.7,xt[u].kind),{face:u===1?-1:1})}const l=ne(M(R.village.boyIn-1.2,e.D+1,n)),f=fe([[i(-.6),o+60*r],[i(-.4),s+160*r],[i(-.22),s+40*r],[i(.05),s-40*r]],10);for(let u=0;u<4;u++){const h=Re(l*1.05-.02+u*.05);if(h<=0)continue;const[d,m]=Dr(f,h);Bt(t,{x:d,y:m,s:130*a*(1-h*.5),t:n,walk:1,seed:u+2,graze:0})}const c=Re(l*1.05-.06);if(c>0){const[u,h]=Dr(f,c);Ze(t,u,h,250*a*(1-c*.5),n,qe.walk(n*6,"boy"),{tool:{kind:"whip",a:[.07,-.42],b:[.28,-.72]},expr:{mouth:"smile"}})}},cam(e){const t=ne(M(R.village.boyIn-.5,e.D,e.t));return We(e,E(1.08,1.35,t),E(e.W/2,e.X(-.35),t),E(e.gy+60*e.u,e.gy+120*e.u,t))},enterAt:[.5,.5]},bored:{label:"山坡上",bg:gt("day",{rock:!1}),draw(e){const{g:t,u:r,t:n,X:s}=e,i=Le(e),o=R.bored;Wr(t,s(-.3)+n*12*r,e.gy-420*r,380*r,3,.25),Jt(e);const a=s(.42),l=ke(e,s(.42))+50*r,f=$(M(o.yawn,o.yawn+.4,n))*(1-$(M(o.yawn+1.3,o.yawn+1.7,n))),c=n>o.idea;if(t.save(),t.translate(a,l),t.rotate(-Math.PI/2),Ze(t,0,0,300*i,n,{hl:f>.3?[-.06,-1.22]:[-.05,-.96],hr:f>.3?[.04,-1.25]:[.01,-.98],fl:[0,0],fr:[.02,-.2],lean:0,expr:{eye:c?n>o.sly?"sly":"wide":f>.3?"closed":"open",mouth:f>.3?"shout":n>o.chew&&!c?"chew":c?"smile":"line",brow:c?"up":"none"},look:n>o.sly?1:0}),t.restore(),n>o.chew-.3&&f<.3){const u=a-300*i*.8,h=l-300*i*.2;z(t,[[u,h],[u+40*r,h-30*r+6*r*Math.sin(n*6)],[u+80*r,h-70*r+10*r*Math.sin(n*6)]],4*r,{color:"#5f8a3c",alpha:.9,taperIn:0,taperOut:.7,seed:4})}Cd(t,a-300*i*.98,l-300*i*.42,40*r,$(M(o.idea,o.idea+.2,n))*(1-$(M(o.idea+1.2,o.idea+1.6,n)))),f>.3&&ms(t,a-300*i*1.05,l-300*i*.5,50*r,f,"~")},cam(e){const t=R.bored,r=e.X(.42),n=ke(e,e.X(.42)),s=ne(M(t.yawn-4.5,t.yawn-2,e.t)),i=ne(M(t.idea-.5,t.idea+.8,e.t));return We(e,E(E(1.05,1.5,s),2.2,i),E(E(e.W/2,r-80*e.u,s),r-230*e.u,i),E(E(e.gy+50*e.u,n,s),n-30*e.u,i))},enterAt:[.5,.5]},shout1:{label:"第一次喊",bg:gt("day"),draw(e){const{g:t,u:r,t:n,X:s}=e,i=Le(e),o=R.shout1;Jt(e,m=>n>o.shouts[0]+.1?{graze:0,look:.6,scared:1,face:m.f>.05?-1:1}:{});const[a,l]=ft(e),f=M(o.hop,o.hop+.7,n),c=E(s(-.2),a,ne(f)),u=E(ke(e,s(-.2))+30*r,l,ne(f))-Math.sin(Math.PI*f)*90*r,h=n>o.cup,d=o.shouts.some(m=>n>m&&n0&&f<1?{hl:[-.12,-.7],hr:[.15,-.72],fl:[-.04,-.06],fr:[.08,-.08]}:qe.stand("boy"),{face:-1,expr:{eye:d?"closed":"sly",mouth:d?"shout":"smile",brow:d?"up":"none"},tool:h?void 0:{kind:"whip",a:[.07,-.42],b:[.28,-.72]}}),h){const m=c-300*i*.25,b=u-300*i*.8;d&&hs(t,m,b,120*r,n,-1,1),o.shouts.forEach((p,w)=>Vr(t,w===2?"快来呀!":"狼来了!",m-40*r,b-140*r-w*40*r,-1,130*r,n-p,{fade:1-$(M(p+2.6,p+3.4,n)),seed:w}))}},cam(e){const[t,r]=ft(e),n=ne(M(3.8,5,e.t));return We(e,E(1.6,1.12,n),E(t,e.X(-.25),n),E(r-80*e.u,e.gy-20*e.u,n))},enterAt:[.3,.6]},run1:{label:"农民上山",bg:Md,draw(e){const{g:t,u:r,t:n,X:s,H:i}=e,o=Le(e),a=R.run1,l=Rs(e);Ze(t,s(.7),i*.27-66*r,140*o,n,{hl:[.05,-.5],hr:[.08,-.52]},{face:-1,expr:{eye:"happy",mouth:"smile"}});for(let f=0;f<4;f++){const c=xt[f],u=ne(M(a.run+f*.15,a.arrive+f*.2,n))*(.92-f*.07),h=Dr(l,.04+f*.03),[d,m]=u>0?Dr(l,.04+f*.03+u):h,b=d+(u>0?0:(f-1.5)*90*r),p=m+f%2*20*r;let w;na.hear+f*.2?{...zt(c.kind,"shade"),tilt:-.25}:ps(n*2.2+f,c.kind):u<=0?w=ln(0,c.kind,0,c.tool):na.arrive+f*.2,g=y?Math.sin(n*2.5+f*1.3)>0?1:-1:1;dr(t,f,b,p,300*o,n,w,{face:g,expr:{eye:"wide",mouth:n>a.grab?"open":"line",brow:y?"worried":"up"},sweat:y?1:0,tilt:y?0:-.1}),y&&ms(t,b+10*r,p-300*o*1.12,70*r,$(M(a.arrive+.3+f*.2,a.arrive+.6+f*.2,n)),"?")}},cam(e){const t=R.run1,r=Rs(e),n=ne(M(t.run,t.arrive+.6,e.t))*.85,[s,i]=Dr(r,.05+n);return We(e,1.55,s+80*e.u,i-90*e.u)},enterAt:[.2,.8]},laugh1:{label:"哈哈大笑",bg:gt("day"),draw(e){const{g:t,u:r,t:n,X:s}=e,i=Le(e),o=R.laugh1;Jt(e);const[a,l]=ft(e),f=n>o.laugh&&no.angry,p=h>0?ln(n*7+c,u.kind,0,u.tool):b?zt(u.kind,"hips"):{...zt(u.kind,"shade")};dr(t,c,d,m,320*i,n,p,{face:h>0?-1:1,tilt:b&&h<=0?.12*Math.sin(n*7+c):0,sweat:b?0:1,expr:{eye:"open",mouth:b?"frown":"open",brow:b?"angry":"worried"}})}},cam(e){const[t]=ft(e);return We(e,E(1.3,1.2,e.t/e.D),E(t,e.X(-.35),ne(M(R.laugh1.angry-.5,R.laugh1.angry+1,e.t))),e.gy+30*e.u)},enterAt:[.5,.4]},shout2:{label:"又喊狼来了",bg:gt("day"),draw(e){const{g:t,W:r,H:n,u:s,t:i,X:o}=e,a=Le(e),l=R.shout2,f=M(0,3,i);if(f<1){const m=Math.max(0,Math.sin(f*Math.PI*2-Math.PI/2)*.5+.5)*.3*(1-f);t.fillStyle=P("#2a3040",m),t.fillRect(-r,-n,r*3,n*3);const b=f*2%1;rr(t,E(o(-1),o(1),b),e.gy-Math.sin(b*Math.PI)*360*s,50*s,"#d65a40",.8*(1-f))}Jt(e,m=>i>l.shouts[0]?{graze:0,look:.5,face:m.f>.05?-1:1}:{});const[c,u]=ft(e),h=i>l.shouts[0]-.4,d=l.shouts.some(m=>i>m&&iVr(t,"狼来了!",m-40*s,b-140*s-w*40*s,-1,120*s,i-p,{fade:1-$(M(p+2.2,p+3,i)),seed:w+5}))}for(let m=0;m<4;m++){const b=xt[m],p=me(M(l.run+m*.2,l.run+2.6+m*.2,i));if(p<=0)continue;const w=E(o(-1.25)-m*90*s,o(-.75+m*.13),p),y=ke(e,o(-.75+m*.13))+(40+m%2*40)*s+(1-p)*120*s;dr(t,m,w,y,320*a,i,p<1?ln(i*12+m,b.kind,1,b.tool):zt(b.kind,"shade"),{expr:{eye:"wide",mouth:"open",brow:"worried"},sweat:1})}},cam(e){const[t,r]=ft(e),n=ne(M(R.shout2.run-.4,R.shout2.run+1.2,e.t));return We(e,E(1.45,1.05,n),E(t-60*e.u,e.W/2-100*e.u,n),E(r-60*e.u,e.gy,n))},enterAt:[.5,.3]},warn:{label:"警告",bg:gt("day"),draw(e){const{g:t,u:r,t:n,X:s}=e,i=Le(e),o=R.warn;Jt(e);const[a,l]=ft(e);Ze(t,a,l,300*i,n,no.point?zt(c.kind,"point",n):zt(c.kind,"hips");dr(t,f,d,m,330*i,n,b,{expr:{eye:"open",mouth:u&&n>o.point?"open":"frown",brow:"angry"},tilt:u&&n>o.point?-.05:0}),u&&n>o.point&&ms(t,d+60*r,m-330*i*1.15,90*r,$(M(o.point,o.point+.2,n)),"!","#b8322a")}},cam(e){const[t,r]=ft(e),n=ne(M(R.warn.point-.4,R.warn.point+.6,e.t));return We(e,E(1.6,1.45,n),E(t-40*e.u,e.X(-.62),n),E(r-50*e.u,e.gy-10*e.u,n))},enterAt:[.3,.5]},wolf:{label:"狼真的来了",bg:gt("storm",{tree:!1}),draw(e){const{g:t,W:r,H:n,u:s,t:i,X:o}=e,a=Le(e),l=R.wolf;for(let d=0;d<4;d++)ds(t,o(-1.2+d*.7)+i*30*s,e.gy-(380+d%2*90)*s,520*s,$(M(l.dark,l.dark+3,i)),40+d);for(let d=0;d<18;d++)En(t,d/18*r+oe(d)*40*s,n-oe(d*3)*160*s,70*s,i,100+d,"#3e5a2e",.6,2.5);Jt(e,()=>({graze:i>l.creep?0:.6+.4*Math.sin(i*2),look:i>l.creep?.4:0,scared:i>l.creep?1:0}));const f=o(.78),c=ke(e,o(.78))+80*s,u=ne(M(l.creep,l.reveal,i));if(i>l.creep-.2){const d=i>l.reveal?"snarl":"sneak";fs(t,{x:f-u*300*s,y:c+10*s,s:330*a,t:i,face:-1,mode:d,glow:.8})}us(t,f+40*s,c+30*s,260*s,11,.85),us(t,f+180*s,c+10*s,200*s,12,.9),yo(t,f+20*s,c-70*s,18*s,i,$(M(l.eyes,l.eyes+.6,i))*(1-$(M(l.creep,l.creep+.3,i))));const h=i>l.reveal&&il.reveal+.28&&i0&&(t.fillStyle=P("#f6f4ee",h),t.fillRect(-r,-n,r*3,n*3))},cam(e){const t=R.wolf,r=e.X(.78),n=ke(e,e.X(.78)),s=ne(M(t.bush-.6,t.eyes,e.t)),i=ne(M(t.reveal-.3,t.reveal+.5,e.t)),o=e.t>t.reveal?Math.sin(e.t*70)*6*e.u*Math.exp(-(e.t-t.reveal)*4):0;return We(e,E(E(1.05,1.8,s),2.1,i),E(E(e.W/2,r+20*e.u,s),r-230*e.u,i),E(E(e.gy-80*e.u,n-40*e.u,s),n-40*e.u,i),{dx:o})},enterAt:[.5,.2]},cry:{label:"呼救",bg:gt("storm"),draw(e){const{g:t,W:r,H:n,u:s,t:i,X:o}=e,a=Le(e),l=R.cry;for(let p=0;p<4;p++)ds(t,o(-1.2+p*.7)+i*40*s,e.gy-(380+p%2*90)*s,520*s,1,50+p);for(let p=0;p<5;p++){const w=(i*.6+p*.21)%1,y=n*(.15+.12*p);z(t,[[E(r*1.2,-r*.3,w),y],[E(r*1.2,-r*.3,w)-220*s,y+8*s]],3*s,{color:"#cfd3d4",alpha:.5,dry:.9,seed:p})}Jt(e,()=>({graze:0,look:.4,scared:1})),fs(t,{x:o(1.05)-i*25*s,y:ke(e,o(.95))+60*s,s:230*a,t:i,face:-1,mode:"sneak",glow:.9});const[f,c]=ft(e),u=ne(M(l.toRock,l.toRock+1,i)),h=E(o(.4),f,u),d=E(ke(e,o(.4))+40*s,c,u)-Math.sin(Math.PI*u)*70*s,m=l.shouts.some(p=>i>p&&il.shouts[1]?1:0,shiver:.6}),u>=1){const p=f-300*a*.25,w=c-300*a*.8;m&&hs(t,p,w,120*s,i,-1,.8),l.shouts.forEach((y,g)=>Vr(t,g===2?"救命啊!":"狼来了!",p-40*s,w-140*s-g*50*s,-1,130*s,i-y,{scatter:M(y+.9,y+2.2,i),seed:g+11}))}},cam(e){const[t,r]=ft(e);return We(e,E(1.35,1.25,e.t/e.D),E(e.X(.3),t-120*e.u,ne(M(1.8,3.6,e.t))),r-60*e.u)},enterAt:[.4,.5]},noone:{label:"没人来",bg:ko(!0),draw(e){const{g:t,u:r,t:n,X:s}=e,i=Le(e),o=R.noone;Vr(t,"狼来了",s(.85),e.gy-300*r,-1,60*r,n-.2,{scatter:M(1.4,3.2,n),color:"#b8322a",seed:21});for(let a=0;a<3;a++){const[l,f]=ys(e,a),c=n>o.look+a*.15&&no.wave&&no.shake&&no.work?a===1?1:-1:a===1?-1:1,tilt:c?-.18+d:0,expr:{eye:"open",mouth:u&&n>o.wave&&no.shake?"angry":"up"}})}},cam(e){const t=R.noone,[r,n]=ys(e,0),s=ne(M(t.wave-.6,t.wave+.4,e.t))*(1-ne(M(t.work-.3,t.work+.9,e.t)));return We(e,E(1.3,1.9,s),E(e.X(.25),r+40*e.u,s),E(e.gy+20*e.u,n-180*e.u,s))},enterAt:[.8,.2]},chaos:{label:"羊群四散",bg:gt("storm",{rock:!0}),draw(e){const{g:t,W:r,H:n,u:s,t:i,X:o}=e,a=Le(e),l=R.chaos;for(let d=0;d<3;d++)ds(t,o(-1+d*.8)+i*30*s,e.gy-400*s,520*s,1,60+d);const[f,c]=ft(e);Ze(t,f,c,300*a,i,ws(i),{face:1,tears:1});const u=E(o(1.2),o(.35),me(M(l.dash,l.scatter,i)))+yr(M(l.grab,l.grab+2.2,i))*900*s,h=ke(e,o(.4))+90*s;$a.forEach((d,m)=>{const b=o(d.f),p=ke(e,b)+d.dy*s,w=M(l.scatter+m*.06,l.scatter+.3+m*.06,i),y=bl.grab||(Bt(t,{x:b+y*g*(260+oe(m)*200)*s,y:p+(oe(m+3)-.5)*g*60*s,s:150*a*d.s,t:i,face:w>0?y:d.face,run:w>0?1:0,scared:w>0?1:0,baa:w>0?Math.abs(Math.sin(i*6+m)):0,seed:d.seed,graze:w>0?0:1}),w>0&&g<1.2&&le(t,b+y*g*200*s,p+10*s,60*s,"#a89a78",.3*(1-g/1.2)))}),fs(t,{x:u,y:h,s:340*a,t:i,face:i>l.grab?1:-1,mode:il.grab});for(let d=0;d<12;d++)En(t,d/12*r,n-oe(d)*100*s,60*s,i,200+d,"#3e5a2e",.6,2.5);go(6,9,(d,m)=>{const b=i-l.scatter;b>0&&b<2.5&&Wt(t,o(.35)+(d()-.5)*400*s+b*100*s,h-b*120*s*d(),10*s,b*4+m,"#f2ece0",.8*(1-b/2.5))})},cam(e){const t=e.t>R.chaos.scatter&&e.to.cry;Ze(t,a,l,300*i,n,Pd,{expr:{eye:c?"sad":"open",mouth:c?"cry":"frown",brow:"worried"},tears:c?1:0,tilt:.12*f,shiver:c?.4:0}),Bt(t,{x:a+300*i*.2,y:l-300*i*.06,s:100*i,t:n,lamb:!0,face:-1,graze:0,look:.3,seed:9,alpha:.4+.6*f}),c&&yn(t,a+300*i*.2,l-300*i*.6+n*40%50*r,8*r,1)},cam(e){const t=e.X(.02),r=ke(e,t);return We(e,E(1.15,1.6,ne(e.t/e.D)),t+20*e.u,r-40*e.u)},enterAt:[.3,.3]},moral:{label:"寓意",bg:gt("dusk",{rock:!1,tree:!0}),draw(e){const{g:t,H:r,u:n,t:s,X:i,portrait:o}=e,a=R.moral;for(let p=0;p<4;p++)le(t,i(-.8)+p*46*n+10*n,e.gy-2*n+p%2*14*n,14*n,"#ffd890",.7*$(M(.5+p*.3,1.5+p*.3,s)));const l=M(0,e.D,s),f=E(i(.5),i(-.3),l),c=ke(e,E(i(.5),i(-.3),l))+60*n;Ze(t,f,c,170*n,s,qe.walk(s*5,"boy"),{face:-1,alpha:.85,expr:{mouth:"line",eye:"sad"}}),Bt(t,{x:f-110*n,y:c+4*n,s:70*n,t:s,walk:1,face:-1,lamb:!0,seed:9}),Vr(t,"狼来了",i(-.2),r*.25,1,80*n,s-.6,{scatter:M(2,4.6,s),seed:31,color:"#9a3a32"});const u=(o?200:180)*n,h=i(o?.6:.68),d=o?r*.28:r*.36;xo(t,"诚实",h,d,u,a.write,.55,s,{vertical:!0,gap:1.02});const m=$(M(a.end-.3,a.end+.3,s));m>0&&(t.save(),t.globalAlpha=m,t.font=`${40*n}px ${xr}`,t.fillStyle=P(G,.85),t.textAlign="center",t.save(),t.translate(h-u*.95,d-u*.9),[..."狼来了 · 完"].forEach((p,w)=>t.fillText(p,0,w*46*n)),t.restore(),t.restore());const b=M(a.end+.1,a.end+.35,s);b>0&&(t.save(),t.translate(h-u*.95,d+u*.9),t.scale(E(1.5,1,me(b)),E(1.5,1,me(b))),Hr(t,0,0,80*n,["寓言"],{alpha:Math.min(1,b*2)}),t.restore())},cam(e){return We(e,E(1,1.06,e.t/e.D),e.W/2,e.H/2)},enterAt:[.5,.4]}},Ce=60/96,hr=[x.G3,x.D4,x.B4],fn=[x.E3,x.B3,x.G4],gs=[x.D3,x.A3,x.D4],Bd=[x.A2,x.E3,x.A3],Eo=[x.G4,x.B4,x.D5];function zr(e,t,r,n,s,i,o=[0,1,2,1]){let a=0;for(let l=t;le.tone(r+R.laugh1.angry+i*.42,440*Math.pow(2,(s-69)/12),440*Math.pow(2,(s-69.4)/12),i===3?.9:.36,.05,"sawtooth",0,.3)),e.pad(r+R.laugh1.angry,t("shout2")+.4,[x.E2,x.B2],.035,600),r=t("shout2"),e.melody(r+.4,Ce,To,.13,0,"pluck");for(const s of R.shout2.shouts)e.pluck(r+s,x.G2,.28,0,{dur:1.2}),e.pluck(r+s,x.D3,.22,0,{dur:1.2});for(let s=r+R.shout2.run;se.pluck(r+R.wolf.reveal,s,.22,0,{dur:2.5})),r=t("cry");for(let s=r+.2;s{const o=e.ctx,a=o.createOscillator();a.type="sawtooth",a.frequency.setValueAtTime(330*s,t),a.frequency.linearRampToValueAtTime(290*s,t+i);const l=o.createOscillator();l.frequency.value=22;const f=o.createGain();f.gain.value=.45;const c=o.createGain();c.gain.setValueAtTime(0,t),c.gain.linearRampToValueAtTime(r,t+.05),c.gain.setValueAtTime(r,t+i*.7),c.gain.linearRampToValueAtTime(0,t+i);const u=o.createGain();u.gain.value=.6,l.connect(f),f.connect(u.gain);const h=o.createBiquadFilter();h.type="bandpass",h.frequency.value=950*s,h.Q.value=2.5;const d=o.createBiquadFilter();d.type="peaking",d.frequency.value=2500,d.gain.value=6,a.connect(u),u.connect(h),h.connect(d),d.connect(c),e.out(c,e.sfx,n,.25),a.start(t),a.stop(t+i+.05),l.start(t),l.stop(t+i+.05)})}function xs(e,t,r=1.4,n=.12){e.at(t,()=>{const s=e.ctx,i=s.createOscillator();i.type="sawtooth",i.frequency.setValueAtTime(78,t),i.frequency.linearRampToValueAtTime(95,t+r*.6),i.frequency.linearRampToValueAtTime(70,t+r);const o=s.createBufferSource();o.buffer=e.noise,o.loop=!0;const a=s.createOscillator();a.frequency.value=17;const l=s.createGain();l.gain.value=.5;const f=s.createGain();f.gain.value=.5,a.connect(l),l.connect(f.gain);const c=s.createBiquadFilter();c.type="lowpass",c.frequency.value=650;const u=s.createGain();u.gain.setValueAtTime(0,t),u.gain.linearRampToValueAtTime(n,t+.25),u.gain.setValueAtTime(n,t+r-.3),u.gain.linearRampToValueAtTime(0,t+r),i.connect(f),o.connect(f),f.connect(c),c.connect(u),e.out(u,e.sfx,.3,.2),i.start(t),i.stop(t+r+.05),o.start(t,t*.37%1.5),o.stop(t+r+.05),a.start(t),a.stop(t+r+.05)})}function Rd(e,t,r=.09){e.at(t,()=>{const n=e.ctx,s=2.4,i=n.createOscillator();i.type="triangle",i.frequency.setValueAtTime(330,t),i.frequency.linearRampToValueAtTime(600,t+.6),i.frequency.setValueAtTime(600,t+1.4),i.frequency.linearRampToValueAtTime(420,t+s);const o=n.createOscillator();o.frequency.value=5.5;const a=n.createGain();a.gain.value=8,o.connect(a),a.connect(i.frequency);const l=n.createBiquadFilter();l.type="bandpass",l.frequency.value=900,l.Q.value=.8;const f=n.createGain();f.gain.setValueAtTime(0,t),f.gain.linearRampToValueAtTime(r,t+.4),f.gain.setValueAtTime(r,t+1.6),f.gain.linearRampToValueAtTime(0,t+s),i.connect(l),l.connect(f),e.out(f,e.sfx,.4,.9),i.start(t),i.stop(t+s+.05),o.start(t),o.stop(t+s+.05)})}function So(e,t,r=.3){e.noiseHit(t,.25,"lowpass",3e3,300,r,0,.6,.5);for(let n=0;n<5;n++)e.noiseHit(t+.15+n*.32,.9,"lowpass",400,60,r*.6*(1-n*.15),n%2?.3:-.3,.7,.8)}const Co=(e,t,r=0)=>{e.noiseHit(t,.04,"highpass",5e3,2500,.25,r,.7,.2),e.tone(t,2400,700,.05,.05,"triangle",r,.2)},Ua=(e,t,r=.12,n=0)=>{e.tone(t,110,60,.12,r,"sine",n,.1),e.noiseHit(t,.06,"lowpass",900,200,r*.7,n,.7,.05)},un=(e,t,r,n,s=.06,i=0)=>{for(let o=t;o{for(let s=t;ss.id===t);for(const s of(n==null?void 0:n.lines)??[])s.who==="boy"&&/狼|救命|快来/.test(s.text)&&e.echo(s.t0,s.dur,.45,r,3)}function dn(e,t,r,n,s,i=.06,o=0){for(let a=0;a<200;a++){const l=(Math.PI/2+a*Math.PI*2-s)/n;if(l<0)continue;const f=t+l;if(f>r)break;Ua(e,f,i,o)}}function Od(e){const t=Tn,r=js,n=de(77);let s=t("title");e.drop(s+R.title.drop),e.swish(s+R.title.write),e.swish(s+R.title.write+.45),e.swish(s+R.title.write+.9),e.thump(s+R.title.seal+.01,.45),Je(e,s+2.6,.06,.2),Je(e,s+4,.05,-.1,1.15),Co(e,s+3.3),s=t("village");for(let i=s+.5;i[{id:"shout",name:"大喊狼来了",t:jr(e,"shout1",5),title:"狼来了",sub:"放羊娃的玩笑",side:"right",vertical:!0},{id:"wolf",name:"狼真的来了",t:jr(e,"wolf",10.2),title:"狼来了",sub:"这一次,是真的",side:"left",vertical:!0},{id:"run",name:"农民上山",t:jr(e,"run1",6.4),title:"狼来了",sub:"伊索寓言 · 水墨手绘动画",side:"right",vertical:!0},{id:"regret",name:"夕阳后悔",t:jr(e,"regret",5.5),title:"诚实",sub:"说谎的人,说真话也没人信",side:"right",vertical:!0}],Vd=[{id:"xhs",name:"小红书",titleMax:20,tagMax:10,color:"#ff2442",titles:["水墨版狼来了|像语文书里的插图","睡前故事|放羊娃和狼来了","诚实的孩子才有人信|狼来了"],desc:`还记得那个放羊娃吗? +这次把《狼来了》画成了一部会动的水墨插图:山村、梯田、羊群,还有一个爱开玩笑的小孩。 + +🐑 山坡上太无聊,他眼珠一转 +📢 「狼来了!」——农民们扛着锄头跑上山 +😂 「我是逗你们玩的!」 +🐺 有一天,绿莹莹的眼睛从树丛里探出来…… + +这一次他喊破了嗓子,也没有人来。 +说谎的人,就算说了真话,也没人再相信他。 + +旁白、放羊娃、农民三个声音,适合当睡前故事和孩子一起看 👀`,tags:["狼来了","伊索寓言","水墨动画","睡前故事","儿童故事","国风插画","手绘动画","亲子阅读","诚实","寓言故事"],tip:"标题 ≤20 字,话题 ≤10 个;话题粘贴后在 App 里点选一下即可变成蓝色话题。"},{id:"dy",name:"抖音",titleMax:30,tagMax:5,color:"#111111",titles:["用代码一笔一画,画了一部水墨版《狼来了》","小时候听过的《狼来了》,这次是水墨手绘版","说了两次谎,狼真的来了:水墨版放羊娃"],desc:`宣纸、毛笔、淡彩、朱红印章——把伊索寓言《狼来了》画成了会动的课本插图。 +放羊娃两次喊「狼来了」骗大家上山,第三次狼真的来了,却再也没有人相信他。 +做一个诚实的孩子吧。`,tags:["狼来了","水墨动画","儿童故事","伊索寓言","国风动画"],tip:"标题 ≤30 字,话题 ≤5 个。"},{id:"sph",name:"微信视频号",titleMax:16,tagMax:null,color:"#07c160",titles:["水墨版《狼来了》","放羊娃说谎,狼真的来了","做个诚实的孩子"],desc:`一部像语文课本插图的水墨动画。 +山坡上放羊的孩子觉得无聊,大喊「狼来了」骗农民们上山;第二次又骗了一回。等狼真的来了,他喊破嗓子,谁也没有来。 +转给家里的小朋友,当作今晚的睡前故事吧。`,tags:["狼来了","伊索寓言","水墨动画","儿童动画","睡前故事","寓言故事","国风","手绘","亲子","诚实","语文课本","成长"],tip:"标题 ≤16 字,话题数量不限。"},{id:"bili",name:"哔哩哔哩",titleMax:80,tagMax:10,color:"#fb7299",titles:["【水墨手绘】伊索寓言《狼来了》|语文课本插图风儿童动画,多角色配音+原创国风配乐,全程代码逐帧绘制","【儿童动画】水墨版《狼来了》:放羊娃的三声「狼来了」"],desc:`一部约 2 分 18 秒的水墨手绘风儿童动画,改编自伊索寓言《狼来了》(放羊的孩子)。 + +画面:全部由代码在浏览器里逐帧绘制——宣纸纹理、变宽毛笔、飞白、淡彩、朱砂印章;放羊娃、农民、羊群和狼都是程序画出来的,横竖屏各自构图。「狼来了」三个朱红大字从画面里喊出来,第三次被风吹散。 +声音:旁白、放羊娃(童声)、农民三个声音;原创五声音阶配乐;羊叫、山谷回声、脚步、锄头、雷声、狼嚎、羊群奔逃等音效全部程序合成。 + +章节: +00:00 片头 +00:06 山村 +00:17 山坡上 +00:29 第一次喊 +00:39 农民上山 +00:51 哈哈大笑 +01:00 又喊狼来了 +01:11 警告 +01:19 狼真的来了 +01:31 呼救 +01:40 没人来 +01:51 羊群四散 +01:59 后悔 +02:07 寓意`,tags:["狼来了","伊索寓言","水墨动画","儿童动画","国风","手绘动画","睡前故事","原创配乐","代码艺术","寓言"],tip:"标题 ≤80 字,标签 ≤10 个;章节时间可直接粘进简介。"}],Dd=[{id:"xhs",name:"小红书图文(实况图)",titleMax:20,tagMax:10,titles:["狼来了的六个瞬间|水墨实况图","会动的课本插图|狼来了"],body:`把《狼来了》画成了 6 张会动的水墨插图,长按就能看它动起来 ✨ + +其一 · 坏主意:山坡上太无聊,放羊娃眼珠一转 +其二 · 狼来了:跳上石头大喊,朱红大字飞向山下 +其三 · 急急忙忙:农民们扛着锄头跑上山 +其四 · 哈哈大笑:「我是逗你们玩的!」 +其五 · 真的来了:树丛里,一双绿莹莹的眼睛 +其六 · 后悔:夕阳下,他抱着小羊哭了 + +说谎的人,就算说了真话,也没有人再相信他。 +你小时候听过这个故事吗?`,tags:["狼来了","实况图","水墨插画","伊索寓言","国风插画","手绘","寓言故事","睡前故事","诚实","动态壁纸"],tip:"发布时选择「实况图」上传 6 组 JPG+MOV;标题 ≤20 字,话题 ≤10 个。"},{id:"dy",name:"抖音图文(实况图)",titleMax:30,tagMax:5,titles:["水墨实况图:放羊娃喊狼来了的六个瞬间","会动的课本插图,狼真的来了"],body:`6 张会动的水墨插图,讲完一个《狼来了》。 +坏主意 → 大喊狼来了 → 农民上山 → 哈哈大笑 → 狼真的来了 → 夕阳下后悔。 +做一个诚实的孩子吧。`,tags:["狼来了","实况图","水墨插画","伊索寓言","国风"],tip:"图文模式上传实况图;标题 ≤30 字,话题 ≤5 个。"}],Mo=md,zd={id:"wolf",title:"狼来了",tagline:"放羊的孩子",intro:"伊索寓言 · 水墨手绘儿童动画。山村、梯田、羊群和一个爱开玩笑的放羊娃——宣纸、毛笔、淡彩、朱红印章,像语文课本里的插图一样在浏览器里实时画出来;旁白、放羊娃和农民各有声音,配乐与音效同步播放。",credit:"故事改编自《伊索寓言 · 狼来了》(公有领域)",assets:"wolf/",slug:"wolf-coming",timeline:Mo,scenes:Nd,seal:["寓言"],series:"伊索寓言 · 狼来了",subColors:{boy:"#9a2f22",farmer:"#2f4a5a"},score:Id,effects:Od,covers:Fd(Mo),live:pd,platforms:Vd,photoPosts:Dd,qa:vd,making:[["画面","与《丑小鸭》共用一套代码水墨引擎(宣纸、变宽毛笔、飞白、淡彩、印章),本片新画了放羊娃、农民、羊群、狼、梯田和草屋。「狼来了」三个朱红大字从画面里喊出来,第三次喊时被风吹散——没人信了。"],["声音","三个声音:旁白(晓晓)、放羊娃(云夏童声)、农民(云健)逐句合成后按时间轴拼接。原创配乐由调皮的竹笛木鱼、紧张的低音颤奏、惋惜的古筝组成;羊叫、喊声回声、脚步、狼嚎、羊群奔逃等音效全部程序合成,整体 −14 LUFS。"],["流程","按 vibe-video 流程:分镜 → 多角色配音生成时间轴 → 接触表 / 静帧 → 确定性自检 → 全片渲染 → 混音 → vibe qa;实况图按 livecanvas 流程配对封装。"]]},Os=[sd,zd];function jd(){const e="duckling";return{film:Os.find(t=>t.id===e)??Os[0],locked:!0}}const ei=jd();c0(ei.film);typeof window<"u"&&!ei.locked&&window.addEventListener("hashchange",()=>location.reload());const Wd=()=>typeof window<"u"&&window.innerWidth<768&&window.innerHeight>window.innerWidth;function mr({id:e,no:t,title:r,sub:n,children:s}){return v.jsxDEV("section",{id:e,className:"scroll-mt-16",children:[v.jsxDEV("div",{className:"mb-4 flex items-end gap-3 border-b border-[#d9cdb2] pb-2",children:[v.jsxDEV("span",{className:"font-brush text-3xl leading-none text-[#b8322a]",children:t},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:21,columnNumber:9},this),v.jsxDEV("h2",{className:"font-brush text-3xl leading-none text-[#2a2420]",children:r},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:22,columnNumber:9},this),n&&v.jsxDEV("span",{className:"pb-0.5 text-sm text-[#8a7a5a]",children:n},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:23,columnNumber:17},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:20,columnNumber:7},this),s]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:19,columnNumber:5},this)}function Gd(){const[e,t]=te.useState(Wd()?"portrait":"landscape"),r=[["watch","观看"],["export","导出"],["covers","封面"],["copy","文案"],["live","实况图"],["making","制作"]];return v.jsxDEV("div",{className:"paper-bg min-h-screen text-[#2a2420]",children:[v.jsxDEV("header",{className:"sticky top-0 z-20 border-b border-[#d9cdb2] bg-[#f3ecdc]/90 backdrop-blur",children:v.jsxDEV("div",{className:"mx-auto flex max-w-6xl items-center gap-3 px-4 py-2",children:[v.jsxDEV("span",{className:"flex h-9 w-9 items-center justify-center rounded-sm bg-[#b8322a] font-brush text-lg leading-none text-[#f6efdf]",children:[...ae.title].pop()},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:37,columnNumber:11},this),v.jsxDEV("span",{className:"font-brush text-2xl",children:ae.title},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:38,columnNumber:11},this),v.jsxDEV("span",{className:"hidden items-center gap-1 sm:flex",children:Os.filter(n=>n.id!==ae.id).map(n=>v.jsxDEV("a",{href:ei.locked?`../${n.slug}/`:`#${n.id}`,className:"rounded border border-[#d0c3a5] px-2 py-0.5 text-xs text-[#6b5d48] hover:border-[#8a7a5a]",children:["系列 · 看《",n.title,"》"]},n.id,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:41,columnNumber:15},this))},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:39,columnNumber:11},this),v.jsxDEV("nav",{className:"ml-auto flex gap-1 overflow-x-auto text-sm",children:r.map(([n,s])=>v.jsxDEV("a",{href:`#${n}`,className:"shrink-0 rounded px-2 py-1 text-[#4a3f30] hover:bg-[#e8dec6]",children:s},n,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:45,columnNumber:35},this))},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:44,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:36,columnNumber:9},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:35,columnNumber:7},this),v.jsxDEV("main",{className:"mx-auto flex max-w-6xl flex-col gap-14 px-4 pb-24 pt-8",children:[v.jsxDEV("div",{className:"flex flex-col gap-3 md:flex-row md:items-end md:justify-between",children:[v.jsxDEV("div",{children:[v.jsxDEV("h1",{className:"font-brush text-5xl leading-tight md:text-6xl",children:[ae.title,v.jsxDEV("span",{className:"ml-3 align-middle text-2xl text-[#8a7a5a] md:text-3xl",children:ae.tagline},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:53,columnNumber:87},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:53,columnNumber:13},this),v.jsxDEV("p",{className:"mt-2 max-w-2xl leading-relaxed text-[#4a3f30]",children:ae.intro},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:54,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:52,columnNumber:11},this),v.jsxDEV("div",{className:"flex gap-4 text-sm text-[#6b5d48]",children:[v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"font-brush text-2xl text-[#2a2420]",children:[Math.floor(ve/60),"′",Math.round(ve%60),"″"]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:57,columnNumber:18},this),"片长"]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:57,columnNumber:13},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"font-brush text-2xl text-[#2a2420]",children:ae.timeline.scenes.length},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:58,columnNumber:18},this),"场景"]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:58,columnNumber:13},this),v.jsxDEV("div",{children:[v.jsxDEV("div",{className:"font-brush text-2xl text-[#2a2420]",children:"2"},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:59,columnNumber:18},this),"画幅"]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:59,columnNumber:13},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:56,columnNumber:11},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:51,columnNumber:9},this),v.jsxDEV(mr,{id:"watch",no:"一",title:"观看",sub:"点击画面播放 · 空格暂停 · 手机自动竖屏",children:v.jsxDEV(p0,{orientation:e,onOrientation:t},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:64,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:63,columnNumber:9},this),v.jsxDEV(mr,{id:"export",no:"二",title:"导出 MP4",sub:"浏览器本地渲染 · 24 / 30 / 60 fps · 横屏 / 竖屏",children:v.jsxDEV(eu,{defaultAspect:e==="portrait"?"9:16":"16:9"},e,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:68,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:67,columnNumber:9},this),v.jsxDEV(mr,{id:"covers",no:"三",title:"封面",sub:`4:3 · 3:4 · 16:9 各 ${ae.covers.length} 款,可多选打包`,children:v.jsxDEV(vu,{},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:72,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:71,columnNumber:9},this),v.jsxDEV(mr,{id:"copy",no:"四",title:"发布文案",sub:"小红书 · 抖音 · 视频号 · 哔哩哔哩,一键复制",children:v.jsxDEV(ku,{},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:76,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:75,columnNumber:9},this),v.jsxDEV(mr,{id:"live",no:"五",title:"实况图",sub:`${ae.live.length} 组 JPG + MOV · 小红书 / 抖音图文`,children:v.jsxDEV(Su,{},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:80,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:79,columnNumber:9},this),v.jsxDEV(mr,{id:"making",no:"六",title:"制作与质检",sub:"分镜 · 接触表 · vibe qa 报告",children:v.jsxDEV(Pu,{},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:84,columnNumber:11},this)},void 0,!1,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:83,columnNumber:9},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:50,columnNumber:7},this),v.jsxDEV("footer",{className:"border-t border-[#d9cdb2] py-6 text-center text-xs text-[#8a7a5a]",children:[ae.credit," · 画面、配乐、音效均由代码原创生成 · 字体:霞鹜文楷、马善政毛笔楷书(SIL OFL)"]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:87,columnNumber:7},this)]},void 0,!0,{fileName:"/workspaces/frontend/src/App.tsx",lineNumber:34,columnNumber:5},this)}export{Gd as default}; diff --git a/ugly-duckling/dist/assets/index-DjlK4Epu.js b/ugly-duckling/dist/assets/index-DjlK4Epu.js new file mode 100644 index 0000000000000000000000000000000000000000..8ce1e21f3e25044125905c7491c4361d13700060 --- /dev/null +++ b/ugly-duckling/dist/assets/index-DjlK4Epu.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index-Br42UDbl.js","./index-BlGQAcOD.js","./client-BlQxAbrl.js","./index-tKuM8enU.js","./App-BEh-_Ljg.js","./jsx-dev-runtime-Czb3f8pQ.js","./ErrorBoundary-DwultlP_.js"])))=>i.map(i=>d[i]); +(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))d(e);new MutationObserver(e=>{for(const t of e)if(t.type==="childList")for(const s of t.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&d(s)}).observe(document,{childList:!0,subtree:!0});function i(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?t.credentials="include":e.crossOrigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function d(e){if(e.ep)return;e.ep=!0;const t=i(e);fetch(e.href,t)}})();const E="modulepreload",v=function(r,o){return new URL(r,o).href},w={},y=function(o,i,d){let e=Promise.resolve();if(i&&i.length>0){let s=function(n){return Promise.all(n.map(u=>Promise.resolve(u).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};const l=document.getElementsByTagName("link"),f=document.querySelector("meta[property=csp-nonce]"),h=(f==null?void 0:f.nonce)||(f==null?void 0:f.getAttribute("nonce"));e=s(i.map(n=>{if(n=v(n,d),n in w)return;w[n]=!0;const u=n.endsWith(".css"),p=u?'[rel="stylesheet"]':"";if(!!d)for(let m=l.length-1;m>=0;m--){const g=l[m];if(g.href===n&&(!u||g.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${n}"]${p}`))return;const c=document.createElement("link");if(c.rel=u?"stylesheet":E,u||(c.as="script"),c.crossOrigin="",c.href=n,h&&c.setAttribute("nonce",h),document.head.appendChild(c),u)return new Promise((m,g)=>{c.addEventListener("load",m),c.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${n}`)))})}))}function t(s){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=s,window.dispatchEvent(l),!l.defaultPrevented)throw s}return e.then(s=>{for(const l of s||[])l.status==="rejected"&&t(l.reason);return o().catch(t)})},_="__dep_reload",L=6,O=6e4;function P(){try{return JSON.parse(sessionStorage.getItem(_)||"{}")}catch{return{c:0,t:0}}}async function R(){const r=document.getElementById("root");try{let o=function(){if(!document.querySelector("[data-surf-placeholder]"))try{window.parent.postMessage({type:"surf-app-ready"},"*")}catch{}};const i=await y(()=>import("./index-Br42UDbl.js").then(a=>a.i),__vite__mapDeps([0,1]),import.meta.url);if(typeof i.useState!="function")throw new Error("dep-stub");const{createElement:d}=i,{createRoot:e,hydrateRoot:t}=await y(async()=>{const{createRoot:a,hydrateRoot:c}=await import("./client-BlQxAbrl.js").then(m=>m.c);return{createRoot:a,hydrateRoot:c}},__vite__mapDeps([2,1]),import.meta.url),{QueryClient:s,QueryClientProvider:l}=await y(async()=>{const{QueryClient:a,QueryClientProvider:c}=await import("./index-tKuM8enU.js");return{QueryClient:a,QueryClientProvider:c}},__vite__mapDeps([3,0,1]),import.meta.url),{default:f}=await y(async()=>{const{default:a}=await import("./App-BEh-_Ljg.js");return{default:a}},__vite__mapDeps([4,5,1,0]),import.meta.url),{default:h}=await y(async()=>{const{default:a}=await import("./ErrorBoundary-DwultlP_.js");return{default:a}},__vite__mapDeps([6,5,1,0]),import.meta.url),n=new s({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:3,retryDelay:a=>Math.min(1e3*2**a,1e4),staleTime:3e4}}}),u=d(h,null,d(l,{client:n},d(f))),p=!!r.querySelector("[data-surf-placeholder]");r.childNodes.length>0&&r.innerHTML!==""&&!p?t(r,u):(r.innerHTML="",e(r).render(u)),window.__reactOk=!0,sessionStorage.removeItem(_),o(),new MutationObserver(o).observe(r,{childList:!0,subtree:!0})}catch{const o=P(),i=Date.now()-o.t>O?0:o.c;i','

Loading dependencies...

','

Reloading automatically

',""].join(""),sessionStorage.setItem(_,JSON.stringify({c:i+1,t:Date.now()})),setTimeout(()=>location.reload(),3e3+i*1e3)):r.innerHTML=['
','

Failed to load dependencies

','

Please refresh the page

',"
"].join("")}}R(); diff --git a/ugly-duckling/dist/assets/index-Dujsqd5W.css b/ugly-duckling/dist/assets/index-Dujsqd5W.css new file mode 100644 index 0000000000000000000000000000000000000000..9a34401b9a5809bd0335497be032c1768060075e --- /dev/null +++ b/ugly-duckling/dist/assets/index-Dujsqd5W.css @@ -0,0 +1 @@ +@import"https://fonts.googleapis.com/css2?family=Lato:wght@400;600;700;900&family=Roboto+Mono:wght@400;500&display=swap";/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-600:oklch(57.7% .245 27.325);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-2xl:42rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-tight:1.25;--leading-relaxed:1.625;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-2xl:40px;--aspect-video:16 / 9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-background:var(--background);--color-foreground:var(--foreground);--color-border:var(--border)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:after,:before{border-color:var(--color-border)}body{background-color:var(--color-background);color:var(--color-foreground);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Lato,PingFang SC,sans-serif}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-x-0{inset-inline:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.-top-12{top:calc(var(--spacing) * -12)}.top-0{top:calc(var(--spacing) * 0)}.top-1{top:calc(var(--spacing) * 1)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing) * 2)}.top-4{top:calc(var(--spacing) * 4)}.top-\[1px\]{top:1px}.top-\[19px\]{top:19px}.top-\[50\%\]{top:50%}.top-\[60\%\]{top:60%}.top-full{top:100%}.-right-12{right:calc(var(--spacing) * -12)}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.-bottom-12{bottom:calc(var(--spacing) * -12)}.bottom-0{bottom:calc(var(--spacing) * 0)}.-left-12{left:calc(var(--spacing) * -12)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing) * 2)}.left-\[50\%\]{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[1\]{z-index:1}.z-\[100\]{z-index:100}.m-auto{margin:auto}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing) * 1)}.-mt-4{margin-top:calc(var(--spacing) * -4)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-24{margin-top:calc(var(--spacing) * 24)}.mt-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.-ml-4{margin-left:calc(var(--spacing) * -4)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[3\/4\]{aspect-ratio:3/4}.aspect-\[9\/16\]{aspect-ratio:9/16}.aspect-square{aspect-ratio:1}.aspect-video{aspect-ratio:var(--aspect-video)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-\[--cell-size\]{width:--cell-size;height:--cell-size}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-20{height:calc(var(--spacing) * 20)}.h-\[--cell-size\]{height:--cell-size}.h-\[1px\]{height:1px}.h-\[5px\]{height:5px}.h-\[var\(--radix-navigation-menu-viewport-height\)\]{height:var(--radix-navigation-menu-viewport-height)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-\[--radix-context-menu-content-available-height\]{max-height:--radix-context-menu-content-available-height}.max-h-\[--radix-select-content-available-height\]{max-height:--radix-select-content-available-height}.max-h-\[300px\]{max-height:300px}.max-h-\[var\(--radix-dropdown-menu-content-available-height\)\]{max-height:var(--radix-dropdown-menu-content-available-height)}.max-h-screen{max-height:100vh}.min-h-\[60px\]{min-height:60px}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-20{width:calc(var(--spacing) * 20)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-\[--cell-size\]{width:--cell-size}.w-\[1px\]{width:1px}.w-\[100px\]{width:100px}.w-\[min\(100\%\,calc\(min\(78vh\,820px\)\*9\/16\)\)\]{width:min(100%,min(78vh,820px)/1.77778)}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-lg{max-width:var(--container-lg)}.max-w-max{max-width:max-content}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-10{min-width:calc(var(--spacing) * 10)}.min-w-\[--cell-size\]{min-width:--cell-size}.min-w-\[8rem\]{min-width:8rem}.min-w-\[12rem\]{min-width:12rem}.min-w-\[240px\]{min-width:240px}.min-w-\[640px\]{min-width:640px}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-full{flex-basis:100%}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-\[--radix-context-menu-content-transform-origin\]{transform-origin:--radix-context-menu-content-transform-origin}.origin-\[--radix-dropdown-menu-content-transform-origin\]{transform-origin:--radix-dropdown-menu-content-transform-origin}.origin-\[--radix-hover-card-content-transform-origin\]{transform-origin:--radix-hover-card-content-transform-origin}.origin-\[--radix-menubar-content-transform-origin\]{transform-origin:--radix-menubar-content-transform-origin}.origin-\[--radix-popover-content-transform-origin\]{transform-origin:--radix-popover-content-transform-origin}.origin-\[--radix-select-content-transform-origin\]{transform-origin:--radix-select-content-transform-origin}.origin-\[--radix-tooltip-content-transform-origin\]{transform-origin:--radix-tooltip-content-transform-origin}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.scroll-mt-16{scroll-margin-top:calc(var(--spacing) * 16)}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-14{gap:calc(var(--spacing) * 14)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-t-\[10px\]{border-top-left-radius:10px;border-top-right-radius:10px}.rounded-l-md{border-top-left-radius:calc(var(--radius) - 2px);border-bottom-left-radius:calc(var(--radius) - 2px)}.rounded-tl-sm{border-top-left-radius:calc(var(--radius) - 4px)}.rounded-r-md{border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-\[\#2a2420\]{border-color:#2a2420}.border-\[\#2f5d3a\]{border-color:#2f5d3a}.border-\[\#8a7a5a\]{border-color:#8a7a5a}.border-\[\#b8322a\]{border-color:#b8322a}.border-\[\#cdbf9f\]{border-color:#cdbf9f}.border-\[\#d0c3a5\]{border-color:#d0c3a5}.border-\[\#d9cdb2\]{border-color:#d9cdb2}.border-\[\#e3d9c2\]{border-color:#e3d9c2}.border-\[var\(--border-base\)\]{border-color:var(--border-base)}.border-destructive,.border-destructive\/50{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/50{border-color:color-mix(in oklab,var(--destructive) 50%,transparent)}}.border-input{border-color:var(--input)}.border-primary,.border-primary\/50{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/50{border-color:color-mix(in oklab,var(--primary) 50%,transparent)}}.border-transparent{border-color:#0000}.border-white\/90{border-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.border-white\/90{border-color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-\[\#2a2420\]{background-color:#2a2420}.bg-\[\#2f5d3a\]{background-color:#2f5d3a}.bg-\[\#8a7a5a\]{background-color:#8a7a5a}.bg-\[\#b8322a\]{background-color:#b8322a}.bg-\[\#b8322a\]\/90{background-color:#b8322ae6}.bg-\[\#e3ecd9\]{background-color:#e3ecd9}.bg-\[\#e6dcc5\]{background-color:#e6dcc5}.bg-\[\#e6e0d2\]{background-color:#e6e0d2}.bg-\[\#ece2cc\]{background-color:#ece2cc}.bg-\[\#efe6d2\]{background-color:#efe6d2}.bg-\[\#f2ead8\]{background-color:#f2ead8}.bg-\[\#f3ead6\]{background-color:#f3ead6}.bg-\[\#f3ecdc\]\/90{background-color:#f3ecdce6}.bg-\[\#f5d9d5\]{background-color:#f5d9d5}.bg-\[\#f6e7c4\]{background-color:#f6e7c4}.bg-\[\#f8f2e4\]{background-color:#f8f2e4}.bg-\[\#f8f3e7\]{background-color:#f8f3e7}.bg-\[\#fbf7ee\]{background-color:#fbf7ee}.bg-\[var\(--bg-subtle\)\]{background-color:var(--bg-subtle)}.bg-\[var\(--neutral-0\)\]{background-color:var(--neutral-0)}.bg-accent{background-color:var(--accent)}.bg-background{background-color:var(--background)}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab,red,red)){.bg-black\/20{background-color:color-mix(in oklab,var(--color-black) 20%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black) 80%,transparent)}}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-foreground\/5{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/5{background-color:color-mix(in oklab,var(--foreground) 5%,transparent)}}.bg-muted,.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted) 50%,transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/20{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/20{background-color:color-mix(in oklab,var(--primary) 20%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-transparent{background-color:#0000}.fill-current{fill:currentColor}.fill-primary{fill:var(--primary)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing) * 0)}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-\[1px\]{padding:1px}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.px-\[--cell-size\]{padding-inline:--cell-size}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.pt-0{padding-top:calc(var(--spacing) * 0)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-8{padding-top:calc(var(--spacing) * 8)}.pr-1{padding-right:calc(var(--spacing) * 1)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-24{padding-bottom:calc(var(--spacing) * 24)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.align-middle{vertical-align:middle}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.8rem\]{font-size:.8rem}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#2a2420\]{color:#2a2420}.text-\[\#2f5d3a\]{color:#2f5d3a}.text-\[\#3a3128\]{color:#3a3128}.text-\[\#4a3f30\]{color:#4a3f30}.text-\[\#5a4f3c\]{color:#5a4f3c}.text-\[\#6b5d48\]{color:#6b5d48}.text-\[\#8a5a10\]{color:#8a5a10}.text-\[\#8a7a5a\]{color:#8a7a5a}.text-\[\#a09070\]{color:#a09070}.text-\[\#b8322a\]{color:#b8322a}.text-\[\#f6efdf\]{color:#f6efdf}.text-\[var\(--fg-subtle\)\]{color:var(--fg-subtle)}.text-accent-foreground{color:var(--accent-foreground)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-foreground,.text-foreground\/50{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\/50{color:color-mix(in oklab,var(--foreground) 50%,transparent)}}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{color:var(--color-white)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline-offset-4{text-underline-offset:4px}.accent-\[\#b8322a\]{accent-color:#b8322a}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(60\,40\,20\,0\.18\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#3c28142e);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-\[\#d6c9ad\]{--tw-ring-color:#d6c9ad}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-2xl{--tw-backdrop-blur:blur(var(--blur-2xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.\[--cell-size\:2rem\]{--cell-size:2rem}.running{animation-play-state:running}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-data-\[focused\=true\]\/day\:relative:is(:where(.group\/day)[data-focused=true] *){position:relative}.group-data-\[focused\=true\]\/day\:z-10:is(:where(.group\/day)[data-focused=true] *){z-index:10}.group-data-\[focused\=true\]\/day\:border-ring:is(:where(.group\/day)[data-focused=true] *){border-color:var(--ring)}.group-data-\[focused\=true\]\/day\:ring-\[3px\]:is(:where(.group\/day)[data-focused=true] *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.group-data-\[focused\=true\]\/day\:ring-ring\/50:is(:where(.group\/day)[data-focused=true] *){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.group-data-\[focused\=true\]\/day\:ring-ring\/50:is(:where(.group\/day)[data-focused=true] *){--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.group-data-\[state\=open\]\:rotate-180:is(:where(.group)[data-state=open] *){rotate:180deg}.group-\[\.destructive\]\:border-muted\/40:is(:where(.group).destructive *){border-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.group-\[\.destructive\]\:border-muted\/40:is(:where(.group).destructive *){border-color:color-mix(in oklab,var(--muted) 40%,transparent)}}.group-\[\.destructive\]\:text-red-300:is(:where(.group).destructive *){color:var(--color-red-300)}.group-\[\.toast\]\:bg-muted:is(:where(.group).toast *){background-color:var(--muted)}.group-\[\.toast\]\:bg-primary:is(:where(.group).toast *){background-color:var(--primary)}.group-\[\.toast\]\:text-muted-foreground:is(:where(.group).toast *){color:var(--muted-foreground)}.group-\[\.toast\]\:text-primary-foreground:is(:where(.group).toast *){color:var(--primary-foreground)}.group-\[\.toaster\]\:border-border:is(:where(.group).toaster *){border-color:var(--border)}.group-\[\.toaster\]\:bg-background:is(:where(.group).toaster *){background-color:var(--background)}.group-\[\.toaster\]\:text-foreground:is(:where(.group).toaster *){color:var(--foreground)}.group-\[\.toaster\]\:shadow-lg:is(:where(.group).toaster *){--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-70:is(:where(.peer):disabled~*){opacity:.7}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing) * 0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-1:after{content:var(--tw-content);width:calc(var(--spacing) * 1)}.after\:-translate-x-1\/2:after{content:var(--tw-content);--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}@media(hover:hover){.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:border-\[\#8a7a5a\]:hover{border-color:#8a7a5a}.hover\:bg-\[\#a02a23\]:hover{background-color:#a02a23}.hover\:bg-\[\#e8dec6\]:hover{background-color:#e8dec6}.hover\:bg-\[\#ece2cc\]:hover{background-color:#ece2cc}.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-destructive\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/80:hover{background-color:color-mix(in oklab,var(--destructive) 80%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive) 90%,transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted) 50%,transparent)}}.hover\:bg-primary\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/80:hover{background-color:color-mix(in oklab,var(--primary) 80%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary) 90%,transparent)}}.hover\:bg-secondary:hover,.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--secondary) 80%,transparent)}}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.group-\[\.destructive\]\:hover\:border-destructive\/30:is(:where(.group).destructive *):hover{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.group-\[\.destructive\]\:hover\:border-destructive\/30:is(:where(.group).destructive *):hover{border-color:color-mix(in oklab,var(--destructive) 30%,transparent)}}.group-\[\.destructive\]\:hover\:bg-destructive:is(:where(.group).destructive *):hover{background-color:var(--destructive)}.group-\[\.destructive\]\:hover\:text-destructive-foreground:is(:where(.group).destructive *):hover{color:var(--destructive-foreground)}.group-\[\.destructive\]\:hover\:text-red-50:is(:where(.group).destructive *):hover{color:var(--color-red-50)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:opacity-100:focus{opacity:1}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.group-\[\.destructive\]\:focus\:ring-destructive:is(:where(.group).destructive *):focus{--tw-ring-color:var(--destructive)}.group-\[\.destructive\]\:focus\:ring-red-400:is(:where(.group).destructive *):focus{--tw-ring-color:var(--color-red-400)}.group-\[\.destructive\]\:focus\:ring-offset-red-600:is(:where(.group).destructive *):focus{--tw-ring-offset-color:var(--color-red-600)}.focus-visible\:border-\[var\(--fg-base\)\]:focus-visible{border-color:var(--fg-base)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--ring)}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:var(--background)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.has-focus\:border-ring:has(:focus){border-color:var(--ring)}.has-focus\:ring-\[3px\]:has(:focus){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.has-focus\:ring-ring\/50:has(:focus){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.has-focus\:ring-ring\/50:has(:focus){--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-selected\:text-muted-foreground[aria-selected=true]{color:var(--muted-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[disabled\=true\]\:pointer-events-none[data-disabled=true]{pointer-events:none}.data-\[disabled\=true\]\:opacity-50[data-disabled=true]{opacity:.5}.data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion=from-end]{--tw-enter-translate-x:calc(52*var(--spacing))}.data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion=from-start]{--tw-enter-translate-x:calc(52*var(--spacing)*-1)}.data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion=to-end]{--tw-exit-translate-x:calc(52*var(--spacing))}.data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion=to-start]{--tw-exit-translate-x:calc(52*var(--spacing)*-1)}.data-\[motion\^\=from-\]\:animate-in[data-motion^=from-]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[motion\^\=from-\]\:fade-in[data-motion^=from-]{--tw-enter-opacity:0}.data-\[motion\^\=to-\]\:animate-out[data-motion^=to-]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[motion\^\=to-\]\:fade-out[data-motion^=to-]{--tw-exit-opacity:0}.data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical]{height:1px}.data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical]{width:100%}.data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical]{flex-direction:column}.data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);left:calc(var(--spacing) * 0)}.data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]:after{content:var(--tw-content);height:calc(var(--spacing) * 1)}.data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]:after{content:var(--tw-content);width:100%}.data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[range-end\=true\]\:rounded-md[data-range-end=true]{border-radius:calc(var(--radius) - 2px)}.data-\[range-end\=true\]\:bg-primary[data-range-end=true]{background-color:var(--primary)}.data-\[range-end\=true\]\:text-primary-foreground[data-range-end=true]{color:var(--primary-foreground)}.data-\[range-middle\=true\]\:rounded-none[data-range-middle=true]{border-radius:0}.data-\[range-middle\=true\]\:bg-accent[data-range-middle=true]{background-color:var(--accent)}.data-\[range-middle\=true\]\:text-accent-foreground[data-range-middle=true]{color:var(--accent-foreground)}.data-\[range-start\=true\]\:rounded-md[data-range-start=true]{border-radius:calc(var(--radius) - 2px)}.data-\[range-start\=true\]\:bg-primary[data-range-start=true]{background-color:var(--primary)}.data-\[range-start\=true\]\:text-primary-foreground[data-range-start=true]{color:var(--primary-foreground)}.data-\[selected-single\=true\]\:bg-primary[data-selected-single=true]{background-color:var(--primary)}.data-\[selected-single\=true\]\:text-primary-foreground[data-selected-single=true]{color:var(--primary-foreground)}.data-\[selected\=true\]\:rounded-none[data-selected=true]{border-radius:0}.data-\[selected\=true\]\:bg-accent[data-selected=true]{background-color:var(--accent)}.data-\[selected\=true\]\:text-accent-foreground[data-selected=true]{color:var(--accent-foreground)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.data-\[state\=active\]\:text-foreground[data-state=active]{color:var(--foreground)}.data-\[state\=active\]\:shadow[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:.2s ease-out accordion-up}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity:.8}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50% }.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed]{--tw-exit-translate-x: 100% }.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48% }.data-\[state\=hidden\]\:animate-out[data-state=hidden]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=hidden\]\:fade-out[data-state=hidden]{--tw-exit-opacity:0}.data-\[state\=on\]\:bg-accent[data-state=on]{background-color:var(--accent)}.data-\[state\=on\]\:text-accent-foreground[data-state=on]{color:var(--accent-foreground)}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:.2s ease-out accordion-down}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open],.data-\[state\=open\]\:bg-accent\/50[data-state=open]{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.data-\[state\=open\]\:bg-accent\/50[data-state=open]{background-color:color-mix(in oklab,var(--accent) 50%,transparent)}}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-90[data-state=open]{--tw-enter-scale:.9}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50% }.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48% }.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y: -100% }@media(hover:hover){.data-\[state\=open\]\:hover\:bg-accent[data-state=open]:hover{background-color:var(--accent)}}.data-\[state\=open\]\:focus\:bg-accent[data-state=open]:focus{background-color:var(--accent)}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[state\=visible\]\:animate-in[data-state=visible]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=visible\]\:fade-in[data-state=visible]{--tw-enter-opacity:0}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x:var(--radix-toast-swipe-end-x);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[swipe\=end\]\:animate-out[data-swipe=end]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x:var(--radix-toast-swipe-move-x);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}@media(min-width:40rem){.sm\:top-auto{top:auto}.sm\:right-0{right:calc(var(--spacing) * 0)}.sm\:bottom-0{bottom:calc(var(--spacing) * 0)}.sm\:flex{display:flex}.sm\:inline{display:inline}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-col{flex-direction:column}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:gap-2\.5{gap:calc(var(--spacing) * 2.5)}:where(.sm\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y: 100% }}@media(min-width:48rem){.md\:absolute{position:absolute}.md\:w-\[var\(--radix-navigation-menu-viewport-width\)\]{width:var(--radix-navigation-menu-viewport-width)}.md\:w-auto{width:auto}.md\:max-w-\[420px\]{max-width:420px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-\[1fr_1\.1fr\]{grid-template-columns:1fr 1.1fr}.md\:flex-row{flex-direction:row}.md\:items-end{align-items:flex-end}.md\:justify-between{justify-content:space-between}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media(min-width:64rem){.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}@media(prefers-color-scheme:dark){.dark\:border-destructive{border-color:var(--destructive)}}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-inline:calc(var(--spacing) * 2)}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-block:calc(var(--spacing) * 1.5)}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading]{color:var(--muted-foreground)}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-inline:calc(var(--spacing) * 2)}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:calc(var(--spacing) * 0)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:calc(var(--spacing) * 5)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:calc(var(--spacing) * 5)}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:calc(var(--spacing) * 12)}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-inline:calc(var(--spacing) * 2)}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-block:calc(var(--spacing) * 3)}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:calc(var(--spacing) * 5)}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:calc(var(--spacing) * 5)}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\+div\]\:text-xs+div{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.\[\&\:first-child\[data-selected\=true\]_button\]\:rounded-l-md:first-child[data-selected=true] button{border-top-left-radius:calc(var(--radius) - 2px);border-bottom-left-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--spacing) * 0)}:is(.rtl\:\*\*\:\[\.rdp-button\\_next\>svg\]\:rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *) *):is(.rdp-button_next>svg),:is(.rtl\:\*\*\:\[\.rdp-button\\_previous\>svg\]\:rotate-180:where(:dir(rtl),[dir=rtl],[dir=rtl] *) *):is(.rdp-button_previous>svg){rotate:180deg}.\[\&\:last-child\[data-selected\=true\]_button\]\:rounded-r-md:last-child[data-selected=true] button{border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>span\]\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\[\&\>span\]\:text-xs>span{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.\[\&\>span\]\:opacity-70>span{opacity:.7}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:top-4>svg{top:calc(var(--spacing) * 4)}.\[\&\>svg\]\:left-4>svg{left:calc(var(--spacing) * 4)}.\[\&\>svg\]\:size-3\.5>svg{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&\>svg\]\:h-3\.5>svg{height:calc(var(--spacing) * 3.5)}.\[\&\>svg\]\:w-3\.5>svg{width:calc(var(--spacing) * 3.5)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-destructive>svg{color:var(--destructive)}.\[\&\>svg\]\:text-foreground>svg{color:var(--foreground)}.\[\&\>svg\]\:text-muted-foreground>svg{color:var(--muted-foreground)}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:calc(var(--spacing) * 7)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div{rotate:90deg}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}[data-slot=card-content] .\[\[data-slot\=card-content\]_\&\]\:bg-transparent,[data-slot=popover-content] .\[\[data-slot\=popover-content\]_\&\]\:bg-transparent{background-color:#0000}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}to{height:0}}:root{--background:#fff;--foreground:#212121;--card:#fff;--card-foreground:#212121;--popover:#fff;--popover-foreground:#212121;--primary:#ff2974;--primary-foreground:#fff;--secondary:#f3f2f2;--secondary-foreground:#212121;--muted:#f3f2f2;--muted-foreground:#7a7a7a;--accent:#f3f2f2;--accent-foreground:#212121;--destructive:#f5242e;--destructive-foreground:#fff;--border:#e8e8e8;--input:#e8e8e8;--ring:#212121;--radius:.5rem;--neutral-0:#fff;--neutral-50:#fafafa;--neutral-100:#f5f4f4;--neutral-200:#e7e7e7;--neutral-300:#d8d8d8;--neutral-400:#aaa;--neutral-500:#7a7a7a;--neutral-600:#5b5b5b;--neutral-700:#464646;--neutral-800:#2a2a2a;--neutral-900:#212121;--neutral-950:#1b1b1b;--neutral-1000:#101010;--brand-100:#ff2882;--brand-60:#ff288299;--brand-30:#ff28824d;--brand-10:#ff28821a;--brand-seafoam:#b6cfd0;--brand-dusty-teal:#6ba4b8;--brand-black-aqua:#002f38;--brand-deep-teal-blue:#07272d;--visualizer-rose-pop:#fd4b96;--visualizer-indigo-breeze:#6366f1;--visualizer-emerald-mint:#10b981;--visualizer-golden-amber:#f59e0b;--visualizer-royal-blue:#1d4ed8;--visualizer-crimson-spark:#ef4444;--visualizer-aqua-glow:#06b6d4;--visualizer-sunbeam-yellow:#facc15;--bg-subtle:#2a2a2a0a;--bg-subtle-hover:#2a2a2a0f;--bg-base:#ffffffe0;--bg-base-opaque:#fff;--bg-menu:#fff;--bg-chat:#fcfcfc;--bg-chat-nav:#f4f4f4;--fg-base:#212121;--fg-subtle:#7a7a7a;--fg-muted:#aaa;--fg-disabled:#d8d8d8;--border-base:#2a2a2a0a;--border-strong:#2a2a2a14;--border-contrast:#2a2a2a1f;--border-focus:#2a2a2a66;--tag-blue-10:#5bb5ff1a;--tag-blue-100:#5bb5ff;--tag-yellow-10:#dec3781a;--tag-yellow-100:#dec378;--tag-purple-10:#908eb81a;--tag-purple-100:#908eb8;--tag-cyan-10:#74ada41a;--tag-cyan-100:#74ada4;--tag-pink-10:#b88ea71a;--tag-pink-100:#b88ea7;--tag-orange-10:#b8a08e1a;--tag-orange-100:#b8a08e;--gradient-max-dark:linear-gradient(213deg, #8c421d 2%, #fbe67b 31%, #fcfbe7 53%, #f7d14e 77%, #d4a041 100%);--gradient-max-light:linear-gradient(125deg, #8c421d 22%, #d2af00 47%, #cd9124 67%, #d4a041 88%);--gradient-pro-dark:linear-gradient(213deg, #7a96ac 1%, #c5d6e2 27%, #eaeff3 47%, #c5d6e2 65%, #a3bccf 89%);--gradient-pro-light:linear-gradient(129deg, #7a96ac 4%, #6d7f8d 27%, #9fb9ce 55%, #6d7f8d 90%);--gradient-plus-dark:linear-gradient(214deg, #986732 0%, #a7825b 23%, #f6d0ab 45%, #a07043 66%, #9d774e 100%);--gradient-plus-light:linear-gradient(129deg, #9e8976 5%, #7a5e50 19%, #f6d0ab 35%, #9d774e 49%, #c99b70 65%, #795f52 78%);--gradient-upgrade-dark:linear-gradient(214deg, #ffedf3 0%, #ff9fbf 30%, #fd5d92 59%, #ffdab2 100%);--gradient-upgrade-light:linear-gradient(32deg, #fc3bb2 6%, #f7906e 79%);--gradient-new-function:linear-gradient(33deg, #6e86ff 17%, #ff2882 55%, #ff98a4 103%);--gradient-suggestion:linear-gradient(32deg, #fd538c 4%, #5aa6e0 47%, #ffdab2 98%);--gradient-g1:linear-gradient(189deg, #ffacc6 6%, #b6e0f5 99%);--gradient-g2:linear-gradient(34deg, #9796f0 9%, #fbc7d4 84%);--gradient-g3:linear-gradient(to right, #ed4264, #ffedbc);--gradient-g4:linear-gradient(232deg, #fedc2a 4%, #dd5789 56%, #7a2c9e 104%);--gradient-g5:linear-gradient(32deg, #fc3bb2 6%, #f7906e 79%);--gradient-max:var(--gradient-max-light);--gradient-pro:var(--gradient-pro-light);--gradient-plus:var(--gradient-plus-light);--gradient-upgrade:var(--gradient-upgrade-light);--spacing-2:2px;--spacing-4:4px;--spacing-6:6px;--spacing-8:8px;--spacing-10:10px;--spacing-12:12px;--spacing-16:16px;--spacing-24:24px;--spacing-32:32px;--spacing-40:40px;--spacing-64:64px;--spacing-96:96px;--spacing-128:128px;--radius-2:2px;--radius-4:4px;--radius-6:6px;--radius-8:8px;--radius-10:10px;--radius-12:12px;--radius-16:16px;--radius-20:20px;--radius-24:24px;--radius-full:999px;--font-family-header:"Lato", "PingFang SC", sans-serif;--font-family-body:"Lato", "PingFang SC", sans-serif;--font-family-code:"Roboto Mono", monospace}.dark{--background:#171717;--foreground:#e8e8e8;--card:#171717;--card-foreground:#e8e8e8;--popover:#262626;--popover-foreground:#e8e8e8;--primary:#ff2974;--primary-foreground:#fff;--secondary:#292929;--secondary-foreground:#e8e8e8;--muted:#292929;--muted-foreground:#ababab;--accent:#292929;--accent-foreground:#e8e8e8;--destructive:#ff4c4f;--destructive-foreground:#fff;--border:#333;--input:#333;--ring:#e7e7e7;--gradient-max:var(--gradient-max-dark);--gradient-pro:var(--gradient-pro-dark);--gradient-plus:var(--gradient-plus-dark);--gradient-upgrade:var(--gradient-upgrade-dark);--bg-subtle:#ffffff0a;--bg-subtle-hover:#ffffff1f;--bg-base:#2a2a2acc;--bg-base-opaque:#171717;--bg-menu:#252525;--bg-chat:#101010;--bg-chat-nav:#171717;--fg-base:#e7e7e7;--fg-subtle:#aaa;--fg-muted:#7a7a7a;--fg-disabled:#5b5b5b;--border-base:#ffffff0a;--border-strong:#ffffff14;--border-contrast:#ffffff1f;--border-focus:#fff6}@media(max-width:768px){:root{--spacing-96:64px;--spacing-128:64px}}html,body{background:#f3ecdc}.font-brush{font-family:MaShanZhengFilm,WenKaiFilm,Kaiti SC,STKaiti,KaiTi,serif;font-weight:400}.paper-bg{background-color:#f3ecdc;background-image:radial-gradient(at 20% 0,#fffcf0b3,#0000 60%),radial-gradient(at 90% 30%,#c8b48c1f,#0000 50%),radial-gradient(at 10% 80%,#c8b48c1a,#0000 50%);font-family:WenKaiFilm,PingFang SC,Hiragino Sans GB,Microsoft YaHei,system-ui,sans-serif}.ink-btn{color:#2a2420;background:#fbf7ee;border:1px solid #cdbf9f;border-radius:4px;align-items:center;gap:4px;padding:4px 8px;transition:all .15s;display:inline-flex}.ink-btn:hover{border-color:#8a7a5a}.ink-btn:disabled{opacity:.5}.ink-btn-on{color:#f6efdf;background:#2a2420;border-color:#2a2420}.ink-range{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;background:0 0;height:24px}.ink-range::-webkit-slider-runnable-track{background:linear-gradient(90deg,#8a7a5a,#cdbf9f);border-radius:2px;height:4px}.ink-range::-moz-range-track{background:#cdbf9f;border-radius:2px;height:4px}.ink-range::-webkit-slider-thumb{-webkit-appearance:none;background:#b8322a;border:2px solid #f6efdf;border-radius:50%;width:16px;height:16px;margin-top:-6px;box-shadow:0 1px 3px #0000004d}.ink-range::-moz-range-thumb{background:#b8322a;border:2px solid #f6efdf;border-radius:50%;width:14px;height:14px}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}} diff --git a/ugly-duckling/dist/film/fonts/chars.txt b/ugly-duckling/dist/film/fonts/chars.txt index 5348265823253b4387b6741f8393f709b944c985..5a6a6801656437bb54e03fd44f64af53b5e26e4f 100644 --- a/ugly-duckling/dist/film/fonts/chars.txt +++ b/ugly-duckling/dist/film/fonts/chars.txt @@ -1 +1 @@ - !#%+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz·—‘’“”…、。《》「」【】・一三上下不与丑世丛东丝两个中为主丽举久么乎乐也乡书乱了事二于云五些交享亮亲人仅今介从他代以们仰任伙会传伴位低住体何作你例侧便俏保信倒候值停傍像儿充入全公六共关其具内再写冠冬冰冲决况冷冻准几出击函分则创到制前剩副力加动勾匀包化区半单印即压原又叉友发取变叠口古句另只叫可台右叶号叽各合同名后向吓吗吧呆告命和咔咦咬哀哈响哔哥哩哪唧啄喂善喊喙喜喳嘲嘴器噪嚓四回团围固国图圆圈在地场均坐块垂垛域堆塘填境增墨壁声壳处备复夏外多夜大天太失头夹奇奏她好妈始姐姑娘嫌子字存季孤学孩孵它安完官定实宣宫家宽寂寄密寒寸对导封小尖尚就尺尼尾层屋屏展属山岸峰工左差己已布希帘带帧幅幕干平并序应底度廓延建开式引张弦弯弹归当录形彩影往径很徒得循微心快念忽态怕性怪总息悄悬情惊愈意感慢成截所扇手才扑打扫扬把抖折报拉拍拖拢拨择持按挖振换掉排掠接控推描提插揭撇播擎支收改放政故效敢数整文斑断新旁无日时明春是昼显晓晕晚景晰暂暖暗曲更替最有朋望朝期木未本术朱机权村条来极构果枝枪染柔查柳标树校样核根格桃案梅梦梳检楷模横檐欠次欢欺款歌正此步段每比毛气水池治沼沿法波泥泪泽洁流测浏浓浮消淡深混清渐渡温渲游湖湿溅源溪滴漂漫潮灰点烟烧然照熬片版牙物状猎率环现理瓣生用田由电画畅界痕登白的皮皴益盖盛目直相省看真眼着睡瞬瞻短码砂砰破确碍示神离秋种秒积程空窗窝立竖章童竹笆笑笔笛符等筝签简篇篱簧粒粘系素紧红纤约级纳纷纸纹线组终经绒结绘给络绪续维缓编缘缩网置美群羽翅翻翼而肉背能脊脑脖脚脸腿膀臂自致色艺节芦花芽苇苹范茸草荷落蓝藏蛋蜓蜻蝴蝶融行衣补表被裁裂装西要见观规视览觉角解触计让记讲证试话语说请读课调负败质贴资走起路跳身躲轨转轮软轴轻载输辨边过近还这进远连迟述追送适选逐递通遮那部都配采里重野量金钟钻铃铺错键镜长门间阅阴阵阶阻附限除随难雁雪雾需霞露静面音页顶顿预领颈频颗题颜颤风飘飞马驳验高髻鱼鸟鸡鸣鸭鹅鹜麦黄黑齐️︐!()+,:;?|🐓👀🥚🦢 \ No newline at end of file + !#%+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz·—‘’“”…、。《》「」【】・一三上下不与丑专世丛东丝两个中为主丽举久么义之乌乎乐也乡书乱了事二于云互五些交享亮亲人什仅今介从他代以们仰任伊伙会传伤伴伸位低住体何余作你使例供侧便促俏保信倒候值做停健傍像儿充先光免入全公六共关其具内再写农冠冬冰冲决况冷冻准几出击函分切列则创到制刻前剩剪副力加动勾匀包化区升半单印即却卷压原去又叉及友双发取受变叠口古句另只叫可台右叶号叼叽吃各合同名后向吓吗否吧听吹吻呀呆呈告呼命和咔咦咬哀哈响哔哥哩哪哭哼唧啄啊啦喂善喊喘喙喜喳嗓嘲嘴器噪嚎嚓四回团园围固国图圆圈在地场址均坏坐块坡垂型垛域堆塘塞填境墙增墨壁声壳处备复夏夕外多夜大天太失头夹奇奏奔套她好如妈始姐姑姿娃娘嫌子字存季孤学孩孵它安完官定实宣宫家宽寂寄密寒寓寸对导封小尔尖尚就尺尼尾层屋屏展属山岸峰工左差己已巾布希帘带帧帽幅幕干平并序库应底度廓延建开式引张弦弧弯弹强归当录形彩影往径很徒得循微心忙快念忽态怕急性怪总息悄悔悠悬情惊惋惜愈意感慢憋懒成我或截所扁扇手才扑打扛扫扬扰把抖折报抱担拉拍拖拢拨择拱拼拿持挂按挖挨振换掉排掠探接控推描提插握揭摇撇播擎支收改放政故效救敢散数整文斑斜断新方旁无日时明易春是昼显晓晕晚景晰暂暖暗曲更替最有朋望朝期木未末本术朱机权村条来极构果枝枪柄染柔查柳标树校样核根格桃案梅梦梯梳检楷模横檐欠次欢欺款歌正此步段每比毛民气水求汗池沙没治沼沿法波泥泪泽洁洋活流测浏浓浮消润淡深混清渐渡温渲游湖湿溅源溪滑滴漂漫潮灯灰点烟烧然照熬爱片版牙物状独狼猎率玩环现珠理瓣生用田由电画畅界略疏痕登白的皮皴益盖盛目直相省眉看真眼着睛睡瞬瞻短石码砂砰破确碍碎示神离秋种秒秧积移稀程稍空窗窝立竖章童竹笆笑笔笛符第等筐筝签简算篇篱簧类粒粗粘系素索紧红纤约级纳纷纸纹线组细终经绑绒结绘给络绪续维绿缓编缘缩网置署羊美群羽翅翻翼而耳聊肉肢肤背胸能脊脑脖脚脸腮腰腿膀膝臂自致色艺节芦花芽苇苗苹范茅茸草荷莹落蓝藏蛋蜓蜻蝴蝶融行衣补表袖被裁裂装裤襟西要见观规视览觉角解触言警计认让记讲设证词试诚话语说请读课谁调谎谷贝负败质贴资走赶起越跑路跳身躯躲躺轨转轮软轴轻载输辨辫边迁过近还这进远连迟述追送适逃选逐递逗通逼遍道遮避那部都配采里重野量金钟钻铃铺链锄错键镜长门闪间阅阳阴阵阶阻附限除随难雁雪雷雾需霞露静靠面音页顶顿预领颈频颗题颜额颤风飘飞马驳验骗骨高髻鱼鸟鸡鸣鸦鸭鹅鹜麦黄黑默鼻齐️︐!()+,:;?|~🐑🐓🐺👀📢😂🥚🦢 \ No newline at end of file diff --git a/ugly-duckling/dist/film/fonts/mashanzheng.woff2 b/ugly-duckling/dist/film/fonts/mashanzheng.woff2 index c6ebf28b26ed73e01389a2ba5bcee1e199f70a24..c86f0d69b2acb5f9e7ca4b019192cbbe92406667 100644 --- a/ugly-duckling/dist/film/fonts/mashanzheng.woff2 +++ b/ugly-duckling/dist/film/fonts/mashanzheng.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009f88eccbb75b93ece3214992da9dbab3c96e3e003d1078a88d753a001b630 -size 343228 +oid sha256:27e0895918dceab34deaf286fa50e2dee8abb9b73fb32b2ce13270f8f8215224 +size 423596 diff --git a/ugly-duckling/dist/film/fonts/wenkai.woff2 b/ugly-duckling/dist/film/fonts/wenkai.woff2 index a9a12a80e05800bdfaaf761ad87db25d19e2e1cf..5238c23358aee4284723168731e22d88c781f4c1 100644 --- a/ugly-duckling/dist/film/fonts/wenkai.woff2 +++ b/ugly-duckling/dist/film/fonts/wenkai.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c673c2d90b5d3e48ff76694316aec488e221d610b6269440d795d093207566fe -size 200844 +oid sha256:88500492b8f490478e791976eef7824f7527c1d788ee205d7dc443c153c3ab8f +size 242444 diff --git a/ugly-duckling/dist/index.html b/ugly-duckling/dist/index.html index a1c84cabd71557f7940355d49fa1daa8585e5298..13fa1c7432da7db118e7d02ebcaf06f70b189680 100644 --- a/ugly-duckling/dist/index.html +++ b/ugly-duckling/dist/index.html @@ -57,8 +57,8 @@ 丑小鸭 · 水墨手绘儿童动画 - - + +
diff --git a/ugly-duckling/scripts/dbg_draw.mjs b/ugly-duckling/scripts/dbg_draw.mjs index 941dbed745107538fdeefb4d192d7ace86f2be6d..8a760dd6ce6111f31bbb467d9eb329b0da14a3ee 100644 --- a/ugly-duckling/scripts/dbg_draw.mjs +++ b/ugly-duckling/scripts/dbg_draw.mjs @@ -4,7 +4,7 @@ import { readFileSync, existsSync, statSync, writeFileSync } from 'node:fs' import { join, extname } from 'node:path' import { pathToFileURL } from 'node:url' const [out, W, H, code] = process.argv.slice(2) -const PROJ = '/workspaces/ugly-duckling/video' +const PROJ = process.env.PROJ || '/workspaces/ugly-duckling/video' const { chromium } = await import(pathToFileURL('/root/.vibe-studio/node_modules/playwright/index.mjs').href) const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.mp3': 'audio/mpeg', '.woff2': 'font/woff2' } const srv = createServer((q, s) => { let u = decodeURIComponent(q.url.split('?')[0]); if (u === '/') u = '/index.html'; const f = join(PROJ, u); if (existsSync(f) && statSync(f).isFile()) { s.setHeader('content-type', MIME[extname(f)] || 'application/octet-stream'); return s.end(readFileSync(f)) } s.statusCode = 404; s.end() }) diff --git a/ugly-duckling/scripts/make_live.py b/ugly-duckling/scripts/make_live.py index b148b70df5fbe4c75e0b65d215b277cb49cc9034..b48cd27ba2c4efb70256d6db861ff5f113a709b8 100644 --- a/ugly-duckling/scripts/make_live.py +++ b/ugly-duckling/scripts/make_live.py @@ -4,10 +4,13 @@ import json, shutil, subprocess, sys, zipfile, hashlib from pathlib import Path SK = Path('/workspaces/ugly-duckling/livephotos/livecanvas_scripts') # livecanvas 脚本(含 FFmpeg 7.1 字段兼容补丁) PY = sys.executable -ROOT = Path('/workspaces/ugly-duckling/livephotos') -BUILD, PAIRS, PUB = ROOT / 'build', ROOT / 'pairs', Path('/workspaces/frontend/public/film/live') -live = json.loads(Path('/workspaces/frontend/src/film/live.json').read_text(encoding='utf-8')) -posts = Path('/workspaces/frontend/src/content.ts').read_text(encoding='utf-8') +import os +FILM = os.environ.get('FILM', 'duckling') +CFG = {'duckling': ('ugly-duckling', 'film', '丑小鸭'), 'wolf': ('wolf-coming', 'wolf', '狼来了')}[FILM] +ROOT = Path(f'/workspaces/{CFG[0]}/livephotos') +BUILD, PAIRS, PUB = ROOT / 'build', ROOT / 'pairs', Path(f'/workspaces/frontend/public/{CFG[1]}/live') +live = json.loads(Path(f'/workspaces/frontend/src/films/{FILM}/live.json').read_text(encoding='utf-8')) +posts = Path(f'/workspaces/frontend/src/films/{FILM}/content.ts').read_text(encoding='utf-8') for d in (PAIRS,): shutil.rmtree(d, ignore_errors=True); d.mkdir(parents=True) PUB.mkdir(parents=True, exist_ok=True) @@ -32,7 +35,7 @@ for l in live: print('paired', l['id'], man['identifier'], 'cover', man['coverTime']) (ROOT / 'delivery.json').write_text(json.dumps(report, ensure_ascii=False, indent=1), encoding='utf-8') -readme = """丑小鸭 · 水墨实况图(6 组) +readme = f"""{CFG[2]} · 水墨实况图(6 组) 每组同名的 .jpg + .mov 就是一张苹果实况照片(Live Photo): · 已按 livecanvas 流程写入配对信息:JPG 的 Apple MakerNote 与 MOV 的 com.apple.quicktime.content.identifier 共用同一个 UUID, @@ -59,7 +62,7 @@ for name in ['小红书图文(实况图)', '抖音图文(实况图)']: copy_txt += f"【{name}】\n标题(任选其一):\n" + '\n'.join(' ' + t for t in titles) + f"\n\n正文:\n{body}\n\n话题:\n" + ' '.join('#' + t for t in tags) + '\n\n' + '-' * 30 + '\n\n' zp = PUB / 'livephotos.zip' with zipfile.ZipFile(zp, 'w', zipfile.ZIP_STORED) as z: - base = '丑小鸭_水墨实况图/' + base = f'{CFG[2]}_水墨实况图/' for l in live: pd = PAIRS / f"{l['id']}.pair" z.write(pd / 'live.jpg', base + f"{l['id']}.jpg") diff --git a/ugly-duckling/scripts/qa_report.py b/ugly-duckling/scripts/qa_report.py index 9daffd9c4a9c4e73bf5da3125e4fdf690cba92eb..f5d59ccc09be3bd8d1be7289c4e4c102c96e7410 100644 --- a/ugly-duckling/scripts/qa_report.py +++ b/ugly-duckling/scripts/qa_report.py @@ -1,11 +1,28 @@ #!/usr/bin/env python3 """合并横 / 竖屏 vibe qa + 字幕带补充检查 → web/src/film/qa.json(网页「制作与质检」)""" -import json, datetime +import json, datetime, sys, os +PROJ = sys.argv[1] if len(sys.argv) > 1 else '/workspaces/ugly-duckling'; FILM = sys.argv[2] if len(sys.argv) > 2 else 'duckling' +os.chdir(PROJ) L=json.load(open('video/out/qa.json')); P=json.load(open('video-portrait/out/qa.json')); S=json.load(open('video/out/qa_subband.json')) +import re, subprocess +def recheck(scene_id): + # 竖屏字幕行(~78.5% 高度)单独裁出来 OCR,作为整帧 OCR 失败时的佐证 + tl=json.load(open('video-portrait/timeline.json')); sc=[x for x in tl['scenes'] if x['id']==scene_id][0] + t=sc['start']+sc['dur']*0.75 + subs=[c for c in sc['subs'] if c['t0']<=t=0.6: r={**r,'level':'INFO','msg':r['msg'].split('(')[0]+f'(整帧 OCR 受背景干扰;单独裁出字幕行复检读出「{txt[:20]}」,命中 {hit:.0%})'} if r['check']=='接触表': continue - if r['check']=='文字@reflection' and r['level']=='WARN': + if r['check']=='文字@reflection' and r['level']=='WARN' and FILM=='duckling': r={**r,'level':'INFO','msg':r['msg'].split('(')[0]+'(检查窗口同时计入了上一句字幕;当前字幕「而是一只美丽的白天鹅」已被读出)'} res.append({**r,'check':'竖屏 '+r['check']}) for r in L['results']: @@ -13,10 +30,10 @@ for r in L['results']: for r in S: if '片头' in r['msg']: continue res.append({**r,'check':'横屏 '+r['check']}) -res.append({'level':'INFO','check':'说明','msg':'横屏字幕位于画面底部 ~92%,vibe qa 的 OCR 只扫 60–84% 高度带,故横屏改用「字幕带 OCR + 墨色/纸带对比度(≥7:1)」补充检查。'}) +res.append({'level':'INFO','check':'说明','msg':'横屏字幕位于画面底部 ~92%,vibe qa 的 OCR 只扫 60–84% 高度带,故横屏改用「字幕带 OCR + 墨色/纸带对比度(≥4.5:1,大字号 WCAG AAA)」补充检查。'}) res.append({'level':'PASS','check':'确定性','msg':'vibe det:同一帧乱序重渲逐像素一致'}) res.append({'level':'PASS','check':'实况图','msg':'6 组 JPG+MOV:content identifier 一致、still-image-time 读回正确、MOV 完整解码、哈希校验通过(PhotoKit 本机加载:Linux 环境不可用)'}) bad=sum(r['level']=='FAIL' for r in res); warn=sum(r['level']=='WARN' for r in res) out={'status':'done','generated':datetime.date.today().isoformat(),'meta':P['meta'],'verdict':'FAIL' if bad else 'WARN' if warn else 'PASS','results':res} -json.dump(out,open('/workspaces/frontend/src/film/qa.json','w'),ensure_ascii=False,indent=1) +json.dump(out,open(f'/workspaces/frontend/src/films/{FILM}/qa.json','w'),ensure_ascii=False,indent=1) print(out['verdict'], len(res)) diff --git a/ugly-duckling/scripts/qa_subband.py b/ugly-duckling/scripts/qa_subband.py index 5ffaa503ebd4c520b1e46ae2e426aa55ff012a1c..c88e3a763dcf0e84aa85f5cf515464c54d2b413b 100644 --- a/ugly-duckling/scripts/qa_subband.py +++ b/ugly-duckling/scripts/qa_subband.py @@ -16,10 +16,10 @@ for s in tl['scenes']: # 对比度(WCAG):字幕墨色(最暗 2%)对纸带底色(中间 30% 宽度里亮的一半的中位数) from PIL import Image im = Image.open(f).convert('L'); w, hh = im.size; c = im.crop((int(w * 0.25), 0, int(w * 0.75), hh)) - px = sorted(c.getdata()); ink = px[max(0, int(len(px) * 0.02))]; bg = px[int(len(px) * 0.75)] + px = sorted(c.getdata()); ink = px[max(0, int(len(px) * 0.004))]; bg = px[int(len(px) * 0.9)] lin = lambda v: (v / 255 / 12.92) if v / 255 <= 0.03928 else ((v / 255 + 0.055) / 1.055) ** 2.4 cr = (lin(bg) + 0.05) / (lin(ink) + 0.05) - ok = (hit >= 0.6) or (cr >= 7 and want) + ok = (hit >= 0.6) or (cr >= 4.5 and want) res.append({'level': 'PASS' if ok else 'WARN', 'check': f'字幕@{s["id"]}', 'msg': f'「{want or "(片头无字幕)"}」OCR 命中 {hit:.0%};墨色/纸带对比度 {cr:.1f}:1'}) print(res[-1]['level'], res[-1]['check'], res[-1]['msg']) (p / 'out/qa_subband.json').write_text(json.dumps(res, ensure_ascii=False, indent=1)) diff --git a/ugly-duckling/scripts/render_live.mjs b/ugly-duckling/scripts/render_live.mjs index 8febbf7ec6459465566b52382d451307df23c839..af9b66b5878f9ef65d15bb6e8962ac985338636c 100644 --- a/ugly-duckling/scripts/render_live.mjs +++ b/ugly-duckling/scripts/render_live.mjs @@ -4,7 +4,7 @@ import { readFileSync, existsSync, statSync, writeFileSync, mkdirSync } from 'no import { join, extname } from 'node:path' import { spawn, spawnSync } from 'node:child_process' import { pathToFileURL } from 'node:url' -const PROJ = '/workspaces/ugly-duckling/video', OUT = '/workspaces/ugly-duckling/livephotos/build' +const PROJ = process.env.PROJ || '/workspaces/ugly-duckling/video', OUT = process.env.OUT || '/workspaces/ugly-duckling/livephotos/build' const W = 1080, H = 1440, FPS = 30 const { chromium } = await import(pathToFileURL('/root/.vibe-studio/node_modules/playwright/index.mjs').href) const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.mp3': 'audio/mpeg', '.woff2': 'font/woff2' } diff --git a/ugly-duckling/video-portrait/film.bundle.js b/ugly-duckling/video-portrait/film.bundle.js index 11a6781e4319cbea1ba8d2c8719b9545b342d4d1..016068fd7b2e3f32e934d76059baa35b44ca9e2e 100644 --- a/ugly-duckling/video-portrait/film.bundle.js +++ b/ugly-duckling/video-portrait/film.bundle.js @@ -6,1093 +6,1503 @@ __defProp(target, name, { get: all[name], enumerable: true }); }; - // src/film/timeline.json - var timeline_default = { - title: "\u4E11\u5C0F\u9E2D", - style: "ink-wash textbook illustration", - w: 1920, - h: 1080, - fps: 30, - total: 156.3, - frames: 4689, - voice: "zh-CN-XiaoxiaoNeural", - scenes: [ - { - id: "title", - start: 0, - dur: 6.5, - voice_start: 0.5, - voice_dur: 2.832, - subs: [ - { - t0: 0.5, - t1: 2.199, - text: "\u5B89\u5F92\u751F\u7AE5\u8BDD" - }, - { - t0: 2.199, - t1: 3.332, - text: "\u4E11\u5C0F\u9E2D" - } - ], - cue: { - type: "title" - }, - sfx: [ - { - at: 0.3, - type: "drop" - }, - { - at: 4.6, - type: "seal" - } - ] - }, - { - id: "summer", - start: 6.5, - dur: 11.566666666666666, - voice_start: 7, - voice_dur: 10.176, - subs: [ - { - t0: 7, - t1: 8.42, - text: "\u590F\u5929\u7684\u4E61\u4E0B" - }, - { - t0: 8.42, - t1: 9.603, - text: "\u9EA6\u5B50\u91D1\u9EC4" - }, - { - t0: 9.603, - t1: 12.68, - text: "\u6C60\u5858\u8FB9\u7684\u82A6\u82C7\u957F\u5F97\u6BD4\u4EBA\u8FD8\u9AD8" - }, - { - t0: 12.68, - t1: 14.573, - text: "\u9E2D\u5988\u5988\u5C31\u5728\u8FD9\u91CC" - }, - { - t0: 14.573, - t1: 17.176, - text: "\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB" - } - ], - cue: { - type: "summer" - }, - sfx: [ - { - at: 0.2, - type: "breeze" - } - ] - }, - { - id: "hatch", - start: 18.0667, - dur: 11.566666666666666, - voice_start: 18.5667, - voice_dur: 10.176, - subs: [ - { - t0: 18.567, - t1: 19.26, - text: "\u5494\u5693" - }, - { - t0: 19.26, - t1: 19.954, - text: "\u5494\u5693\uFF01" - }, - { - t0: 19.954, - t1: 22.267, - text: "\u86CB\u58F3\u4E00\u4E2A\u63A5\u4E00\u4E2A\u88C2\u5F00" - }, - { - t0: 22.267, - t1: 25.042, - text: "\u6BDB\u8338\u8338\u7684\u5C0F\u9EC4\u9E2D\u94BB\u4E86\u51FA\u6765" - }, - { - t0: 25.042, - t1: 26.43, - text: "\u5527\u5527\u5730\u53EB\u7740" - }, - { - t0: 26.43, - t1: 28.743, - text: "\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C" - } - ], - cue: { - type: "hatch" - }, - sfx: [ - { - at: 0.5, - type: "crack" - }, - { - at: 1.2, - type: "crack" - }, - { - at: 2.2, - type: "peep" - } - ] - }, - { - id: "bigegg", - start: 29.6333, - dur: 11.8, - voice_start: 30.1333, - voice_dur: 10.416, - subs: [ - { - t0: 30.133, - t1: 32.217, - text: "\u53EA\u5269\u6700\u5927\u7684\u90A3\u9897\u86CB" - }, - { - t0: 32.217, - t1: 33.374, - text: "\u8FC7\u4E86\u597D\u4E45" - }, - { - t0: 33.374, - t1: 34.763, - text: "\u624D\u6162\u6162\u88C2\u5F00" - }, - { - t0: 34.763, - t1: 36.614, - text: "\u94BB\u51FA\u6765\u7684\u5C0F\u5BB6\u4F19" - }, - { - t0: 36.614, - t1: 37.772, - text: "\u53C8\u5927\u53C8\u7070" - }, - { - t0: 37.772, - t1: 40.549, - text: "\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0" - } - ], - cue: { - type: "bigegg" - }, - sfx: [ - { - at: 2.6, - type: "bigcrack" - } - ] - }, - { - id: "bullied", - start: 41.4333, - dur: 10.366666666666667, - voice_start: 41.9333, - voice_dur: 8.952, - subs: [ - { - t0: 41.933, - t1: 44.111, - text: "\u5927\u5BB6\u90FD\u53EB\u4ED6\u4E11\u5C0F\u9E2D" - }, - { - t0: 44.111, - t1: 46.046, - text: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6" - }, - { - t0: 46.046, - t1: 47.74, - text: "\u516C\u9E21\u8FFD\u7740\u5544\u4ED6" - }, - { - t0: 47.74, - t1: 50.885, - text: "\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B" - } - ], - cue: { - type: "bullied" - }, - sfx: [ - { - at: 0.8, - type: "quack" - }, - { - at: 3.2, - type: "flap" - } - ] - }, - { - id: "leave", - start: 51.8, - dur: 7.533333333333333, - voice_start: 52.3, - voice_dur: 6.144, - subs: [ - { - t0: 52.3, - t1: 54.348, - text: "\u4E11\u5C0F\u9E2D\u96BE\u8FC7\u6781\u4E86" - }, - { - t0: 54.348, - t1: 55.116, - text: "\u4E00\u5929" - }, - { - t0: 55.116, - t1: 57.164, - text: "\u4ED6\u6084\u6084\u94BB\u8FC7\u7BF1\u7B06" - }, - { - t0: 57.164, - t1: 58.444, - text: "\u79BB\u5F00\u4E86\u5BB6" - } - ], - cue: { - type: "leave" - }, - sfx: [ - { - at: 0.3, - type: "wind" + // src/film/core.ts + var core_exports = {}; + __export(core_exports, { + FONT_BRUSH: () => FONT_BRUSH, + FONT_KAI: () => FONT_KAI, + INK: () => INK, + PAPER: () => PAPER, + SEAL: () => SEAL, + backOut: () => backOut, + blobLoop: () => blobLoop, + blot: () => blot, + brush: () => brush, + clamp: () => clamp, + ctx2d: () => ctx2d, + easeIn: () => easeIn, + easeInOut: () => easeInOut, + easeOut: () => easeOut, + hash: () => hash, + inv: () => inv, + lerp: () => lerp, + loadFonts: () => loadFonts, + makeCanvas: () => makeCanvas, + mixHex: () => mixHex, + mountains: () => mountains, + noise1: () => noise1, + nz: () => nz, + outline: () => outline, + paper: () => paper, + rgba: () => rgba, + rng: () => rng, + seal: () => seal, + smooth: () => smooth, + spline: () => spline, + spring: () => spring, + stroke: () => stroke, + subPath: () => subPath, + tracePath: () => tracePath, + wash: () => wash + }); + function rng(seed) { + let a = seed >>> 0 || 1; + return () => { + a = a + 1831565813 >>> 0; + let t = a; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + }; + } + function hash(n) { + const s = Math.sin(n * 127.1 + 311.7) * 43758.5453; + return s - Math.floor(s); + } + function noise1(x, seed = 0) { + const i = Math.floor(x), f = x - i; + const a = hash(i + seed * 101.3), b = hash(i + 1 + seed * 101.3); + const u = f * f * (3 - 2 * f); + return a + (b - a) * u; + } + var nz = (x, seed = 0) => noise1(x, seed) * 2 - 1; + var clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x)); + var lerp = (a, b, t) => a + (b - a) * t; + var inv = (a, b, x) => clamp((x - a) / (b - a)); + var smooth = (x) => { + x = clamp(x); + return x * x * (3 - 2 * x); + }; + var easeOut = (x) => 1 - Math.pow(1 - clamp(x), 3); + var easeIn = (x) => Math.pow(clamp(x), 3); + var easeInOut = (x) => { + x = clamp(x); + return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; + }; + var backOut = (x) => { + x = clamp(x); + const c1 = 1.70158, c3 = c1 + 1; + return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2); + }; + var spring = (x) => { + x = Math.max(0, x); + return 1 - Math.exp(-6 * x) * Math.cos(11 * x); + }; + var INK = "#1d1a17"; + var PAPER = "#f6efdf"; + var SEAL = "#b8322a"; + function rgba(hex, a) { + const n = parseInt(hex.slice(1), 16); + return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`; + } + function mixHex(a, b, t) { + const x = parseInt(a.slice(1), 16), y = parseInt(b.slice(1), 16); + const c2 = (s) => Math.round(lerp(x >> s & 255, y >> s & 255, clamp(t))); + return "#" + (1 << 24 | c2(16) << 16 | c2(8) << 8 | c2(0)).toString(16).slice(1); + } + function makeCanvas(w, h) { + if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(w, h); + const c2 = document.createElement("canvas"); + c2.width = w; + c2.height = h; + return c2; + } + function ctx2d(c2) { + return c2.getContext("2d"); + } + var paperCache = /* @__PURE__ */ new Map(); + function paper(W2, H2) { + const key = W2 + "x" + H2; + const hit = paperCache.get(key); + if (hit) return hit; + const c2 = makeCanvas(W2, H2), g2 = ctx2d(c2), r = rng(20261017); + g2.fillStyle = PAPER; + g2.fillRect(0, 0, W2, H2); + const u = Math.min(W2, H2) / 1080; + for (let i = 0; i < 46; i++) { + const x = r() * W2, y = r() * H2, rad = (120 + r() * 420) * u; + const gr = g2.createRadialGradient(x, y, 0, x, y, rad); + const dark = r() < 0.55; + gr.addColorStop(0, dark ? "rgba(170,140,90,0.028)" : "rgba(255,252,240,0.08)"); + gr.addColorStop(1, "rgba(0,0,0,0)"); + g2.fillStyle = gr; + g2.fillRect(x - rad, y - rad, rad * 2, rad * 2); + } + g2.strokeStyle = "rgba(150,125,85,0.035)"; + g2.lineWidth = Math.max(1, u); + for (let y = 0; y < H2; y += 9 * u) { + g2.beginPath(); + g2.moveTo(0, y + r() * 2); + g2.lineTo(W2, y + r() * 2); + g2.stroke(); + } + const nf = Math.round(W2 * H2 / 2600); + for (let i = 0; i < nf; i++) { + const x = r() * W2, y = r() * H2, len = (6 + r() * 26) * u, ang = r() * Math.PI * 2; + g2.strokeStyle = r() < 0.7 ? `rgba(150,120,75,${0.05 + r() * 0.08})` : `rgba(255,252,240,${0.2 + r() * 0.25})`; + g2.lineWidth = (0.4 + r() * 0.9) * u; + g2.beginPath(); + g2.moveTo(x, y); + g2.quadraticCurveTo(x + Math.cos(ang + 0.6) * len * 0.5, y + Math.sin(ang + 0.6) * len * 0.5, x + Math.cos(ang) * len, y + Math.sin(ang) * len); + g2.stroke(); + } + try { + const id = g2.getImageData(0, 0, W2, H2), d = id.data; + let s = 12345; + for (let i = 0; i < d.length; i += 4) { + s = s * 1103515245 + 12345 & 2147483647; + const n = (s >> 8) % 15 - 7; + d[i] += n; + d[i + 1] += n; + d[i + 2] += n; + } + g2.putImageData(id, 0, 0); + } catch { + } + const vg = g2.createRadialGradient(W2 / 2, H2 / 2, Math.min(W2, H2) * 0.35, W2 / 2, H2 / 2, Math.hypot(W2, H2) * 0.62); + vg.addColorStop(0, "rgba(0,0,0,0)"); + vg.addColorStop(1, "rgba(120,90,50,0.13)"); + g2.fillStyle = vg; + g2.fillRect(0, 0, W2, H2); + paperCache.set(key, c2); + if (paperCache.size > 4) paperCache.delete(paperCache.keys().next().value); + return c2; + } + function spline(pts, seg = 8, closed = false) { + const out = []; + const n = pts.length; + if (n < 3) return pts.slice(); + const get = (i) => closed ? pts[(i + n) % n] : pts[Math.max(0, Math.min(n - 1, i))]; + const last = closed ? n : n - 1; + for (let i = 0; i < last; i++) { + const p0 = get(i - 1), p1 = get(i), p2 = get(i + 1), p3 = get(i + 2); + for (let j = 0; j < seg; j++) { + const t = j / seg, t2 = t * t, t3 = t2 * t; + out.push([ + 0.5 * (2 * p1[0] + (-p0[0] + p2[0]) * t + (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 + (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3), + 0.5 * (2 * p1[1] + (-p0[1] + p2[1]) * t + (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 + (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3) + ]); + } + } + if (!closed) out.push(pts[n - 1]); + return out; + } + function subPath(pts, a, b) { + if (pts.length < 2) return pts; + const L = [0]; + for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); + const tot = L[L.length - 1], A = a * tot, B2 = b * tot, out = []; + const at3 = (d) => { + for (let i = 1; i < L.length; i++) if (L[i] >= d) { + const f = (d - L[i - 1]) / Math.max(1e-6, L[i] - L[i - 1]); + return [lerp(pts[i - 1][0], pts[i][0], f), lerp(pts[i - 1][1], pts[i][1], f)]; + } + return pts[pts.length - 1]; + }; + out.push(at3(A)); + for (let i = 0; i < pts.length; i++) if (L[i] > A && L[i] < B2) out.push(pts[i]); + out.push(at3(B2)); + return out; + } + function tracePath(g2, pts, close = false) { + g2.beginPath(); + g2.moveTo(pts[0][0], pts[0][1]); + for (let i = 1; i < pts.length; i++) g2.lineTo(pts[i][0], pts[i][1]); + if (close) g2.closePath(); + } + function brush(g2, pts0, w, o = {}) { + const grow = o.grow ?? 1; + if (grow <= 0 || pts0.length < 2) return; + const pts = grow < 1 ? subPath(pts0, 0, grow) : pts0; + const n = pts.length; + const L = [0]; + for (let i = 1; i < n; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); + const tot = Math.max(1e-6, L[n - 1]); + const tin = o.taperIn ?? 0.18, tout = o.taperOut ?? 0.35, jit = o.jitter ?? 0.18, seed = o.seed ?? 1; + const left = [], right = []; + for (let i = 0; i < n; i++) { + const s = L[i] / tot; + const a = pts[Math.max(0, i - 1)], b = pts[Math.min(n - 1, i + 1)]; + let dx = b[0] - a[0], dy = b[1] - a[1]; + const d = Math.hypot(dx, dy) || 1; + dx /= d; + dy /= d; + let prof = Math.min(1, 0.35 + 0.65 * smooth(s / tin)) * Math.min(1, Math.pow(clamp((1 - s) / tout), 0.7) * 0.92 + 0.08); + if (o.press) prof *= 1 + o.press * Math.exp(-s * 9); + const ww = w / 2 * prof * (1 + jit * nz(s * 7 + seed * 3.1, seed)); + left.push([pts[i][0] - dy * ww, pts[i][1] + dx * ww]); + right.push([pts[i][0] + dy * ww, pts[i][1] - dx * ww]); + } + g2.fillStyle = rgba(o.color ?? INK, o.alpha ?? 0.92); + g2.beginPath(); + g2.moveTo(left[0][0], left[0][1]); + for (let i = 1; i < n; i++) g2.lineTo(left[i][0], left[i][1]); + for (let i = n - 1; i >= 0; i--) g2.lineTo(right[i][0], right[i][1]); + g2.closePath(); + g2.fill(); + if ((o.alpha ?? 0.92) > 0.5 && w > 3) { + g2.fillStyle = rgba(o.color ?? INK, 0.12); + g2.beginPath(); + g2.arc(pts[0][0], pts[0][1], w / 2 * 0.55, 0, Math.PI * 2); + g2.fill(); + } + if (o.dry && o.dry > 0) { + g2.strokeStyle = rgba(PAPER, 0.75 * o.dry); + const lines = 4; + for (let k = 0; k < lines; k++) { + const off = (k + 0.5) / lines - 0.5; + g2.lineWidth = Math.max(0.6, w * 0.07); + g2.beginPath(); + let started = false; + for (let i = Math.floor(n * (0.45 + 0.1 * hash(seed + k))); i < n; i++) { + const sx = lerp(left[i][0], right[i][0], 0.5 + off * 0.8), sy = lerp(left[i][1], right[i][1], 0.5 + off * 0.8); + if (hash(i * 0.37 + k * 13 + seed) < 0.18) { + started = false; + continue; } - ] - }, + if (!started) { + g2.moveTo(sx, sy); + started = true; + } else g2.lineTo(sx, sy); + } + g2.stroke(); + } + } + } + function stroke(g2, ctrl, w, o = {}) { + brush(g2, spline(ctrl, 10), w, o); + } + function outline(g2, loop, w, o = {}) { + const pts = spline(loop, 8, true); + pts.push(pts[0]); + const br = o.breaks ?? [0, 0.36, 0.7, 1]; + for (let i = 0; i < br.length - 1; i++) { + const a = br[i] + 0.012, b = br[i + 1] - 0.012; + if (b > a) brush(g2, subPath(pts, a, b), w, { taperIn: 0.12, taperOut: 0.25, ...o, seed: (o.seed ?? 1) + i * 7 }); + } + } + function wash(g2, loop, color, alpha = 0.6, o = {}) { + const pts = spline(loop, 8, o.closed ?? true); + g2.save(); + g2.translate(o.dx ?? 0, o.dy ?? 0); + tracePath(g2, pts, true); + g2.fillStyle = rgba(color, alpha); + g2.fill(); + if ((o.edge ?? 1) > 0) { + g2.strokeStyle = rgba(color, Math.min(1, alpha * 0.5 * (o.edge ?? 1))); + g2.lineWidth = o.lw ?? 2.2; + g2.lineJoin = "round"; + g2.stroke(); + } + g2.restore(); + } + function blot(g2, x, y, r, color, a, sx = 1, sy = 1) { + if (r <= 0 || a <= 0) return; + g2.save(); + g2.translate(x, y); + g2.scale(sx, sy); + const gr = g2.createRadialGradient(0, 0, 0, 0, 0, r); + gr.addColorStop(0, rgba(color, a)); + gr.addColorStop(0.55, rgba(color, a * 0.6)); + gr.addColorStop(1, rgba(color, 0)); + g2.fillStyle = gr; + g2.fillRect(-r, -r, r * 2, r * 2); + g2.restore(); + } + function blobLoop(cx, cy, r, seed, rough = 0.18, n = 28) { + const out = []; + for (let i = 0; i < n; i++) { + const a = i / n * Math.PI * 2; + const rr = r * (1 + rough * nz(i * 0.7 + seed, seed) + rough * 0.5 * nz(i * 2.3 + seed * 3, seed + 1)); + out.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]); + } + return out; + } + function mountains(g2, W2, baseY, height, color, alpha, seed, o = {}) { + const f = o.freq ?? 2.2; + const pts = []; + const steps2 = 60; + for (let i = 0; i <= steps2; i++) { + const x = i / steps2 * W2; + const v = 0.55 * noise1(i / steps2 * f + seed, seed) + 0.3 * noise1(i / steps2 * f * 2.7 + seed * 2, seed + 1) + 0.15 * noise1(i / steps2 * f * 7 + seed * 5, seed + 2); + pts.push([x, baseY - height * Math.pow(v, 1.4) * 1.6]); + } + const top = Math.min(...pts.map((p) => p[1])); + const gr = g2.createLinearGradient(0, top, 0, baseY); + gr.addColorStop(0, rgba(color, alpha)); + gr.addColorStop(0.6, rgba(color, alpha * 0.55)); + gr.addColorStop(1, rgba(color, 0)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, baseY + 2); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, baseY + 2); + g2.closePath(); + g2.fill(); + if ((o.soft ?? 0) < 1) { + const r = rng(seed * 991); + for (let k = 0; k < 6; k++) { + const a = r() * 0.85, b = a + 0.06 + r() * 0.12; + brush(g2, subPath(pts, a, Math.min(1, b)), 2.5 + r() * 3, { color, alpha: alpha * 0.9, seed: seed + k, jitter: 0.4, dry: 0.4 }); + } + } + } + function seal(g2, x, y, size, text, o = {}) { + const a = o.alpha ?? 1; + if (a <= 0) return; + g2.save(); + g2.translate(x, y); + const s = size; + g2.globalAlpha = a; + const loop = [[-s / 2, -s / 2], [s / 2, -s / 2], [s / 2, s / 2], [-s / 2, s / 2]]; + g2.fillStyle = SEAL; + g2.beginPath(); + if (o.round) g2.arc(0, 0, s / 2, 0, Math.PI * 2); + else { + g2.moveTo(loop[0][0], loop[0][1]); + for (const p of loop.slice(1)) g2.lineTo(p[0], p[1]); + g2.closePath(); + } + g2.fill(); + g2.fillStyle = PAPER; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const cols = text.length; + const rows = Math.max(...text.map((c2) => c2.length)); + const cell = s * 0.84 / Math.max(cols, rows); + g2.font = `${cell * 0.95}px ${o.font ?? FONT_BRUSH}`; + for (let c2 = 0; c2 < cols; c2++) { + const col = text[c2]; + for (let r2 = 0; r2 < col.length; r2++) { + const cx = (cols - 1) / 2 * cell - c2 * cell; + const cy = (r2 - (col.length - 1) / 2) * cell * (rows / col.length); + g2.fillText(col[r2], cx, cy + cell * 0.04); + } + } + const r = rng(Math.round(x * 7 + y * 13)); + g2.fillStyle = PAPER; + for (let i = 0; i < 26; i++) { + g2.globalAlpha = a * (0.25 + r() * 0.5); + g2.beginPath(); + g2.arc((r() - 0.5) * s, (r() - 0.5) * s, r() * s * 0.025 + 0.5, 0, Math.PI * 2); + g2.fill(); + } + g2.restore(); + } + var FONT_KAI = '"WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; + var FONT_BRUSH = '"MaShanZhengFilm", "WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; + var fontsPromise = null; + function loadFonts(base) { + if (fontsPromise) return fontsPromise; + if (typeof FontFace === "undefined") return fontsPromise = Promise.resolve(); + const list = [["WenKaiFilm", base + "film/fonts/wenkai.woff2"], ["MaShanZhengFilm", base + "film/fonts/mashanzheng.woff2"]]; + fontsPromise = Promise.all(list.map(async ([fam, url]) => { + try { + const f = new FontFace(fam, `url(${url})`); + await f.load(); + document.fonts.add(f); + } catch (e) { + console.warn("font load failed", fam, e); + } + })).then(() => void 0); + return fontsPromise; + } + + // src/film/film.ts + var FILM = null; + var TL = { total: 1, fps: 30, scenes: [] }; + var TOTAL = 1; + var SCENES = {}; + function setFilm(f) { + if (FILM === f) return; + FILM = f; + TL = f.timeline; + TOTAL = f.timeline.total; + SCENES = f.scenes; + bgCache.clear(); + } + var bgCache = /* @__PURE__ */ new Map(); + function cachedBg(id, W2, H2, draw) { + const k = `${id}:${W2}x${H2}`; + let c2 = bgCache.get(k); + if (c2) { + bgCache.delete(k); + bgCache.set(k, c2); + } + if (!c2) { + c2 = makeCanvas(W2, H2); + const g2 = ctx2d(c2); + draw(g2, W2, H2); + bgCache.set(k, c2); + if (bgCache.size > 8) bgCache.delete(bgCache.keys().next().value); + } + return c2; + } + function sceneIndexAt(T) { + const s = TL.scenes; + for (let i = s.length - 1; i >= 0; i--) if (T >= s[i].start) return i; + return 0; + } + function drawScene(g2, W2, H2, i, T) { + const tl3 = TL.scenes[i]; + const def = SCENES[tl3.id]; + const u = Math.min(W2, H2) / 1080; + const portrait = H2 > W2 * 1.05; + const t = T - tl3.start; + const sc = { g: g2, W: W2, H: H2, u, t, D: tl3.dur, T, portrait, gy: H2 * (portrait ? 0.62 : 0.6), X: (f) => W2 / 2 + f * Math.min(W2 / 2, H2 * 0.85) }; + g2.drawImage(paper(W2, H2), 0, 0); + g2.save(); + const cam3 = def.cam ? def.cam(sc) : { z: 1, x: W2 / 2, y: H2 / 2, dx: 0, dy: 0 }; + const z = Math.max(1, cam3.z); + const cx = Math.min(Math.max(cam3.x, W2 / (2 * z)), W2 - W2 / (2 * z)); + const cy = Math.min(Math.max(cam3.y, H2 / (2 * z)), H2 - H2 / (2 * z)); + g2.translate(W2 / 2 + (cam3.dx ?? 0), H2 / 2 + (cam3.dy ?? 0)); + g2.scale(z, z); + g2.translate(-cx, -cy); + if (def.bg) g2.drawImage(cachedBg(tl3.id, W2, H2, (bg, w, h) => def.bg({ ...sc, g: bg, W: w, H: h })), 0, 0); + def.draw(sc); + g2.restore(); + if (def.overlay) def.overlay(sc); + } + var TR = 1.1; + function renderFrame(g2, W2, H2, T, opts = {}) { + T = clamp(T, 0, TOTAL - 1e-4); + const i = sceneIndexAt(T); + const tl3 = TL.scenes[i]; + const lt = T - tl3.start; + g2.save(); + g2.setTransform(1, 0, 0, 1, 0, 0); + g2.globalAlpha = 1; + g2.globalCompositeOperation = "source-over"; + if (i > 0 && lt < TR) { + drawScene(g2, W2, H2, i - 1, T); + const p = easeInOut(lt / TR); + const R = Math.hypot(W2, H2) * 0.62 * p / 0.8 + 1; + const def = SCENES[tl3.id]; + const fx = (def.enterAt?.[0] ?? 0.5) * W2, fy = (def.enterAt?.[1] ?? 0.5) * H2; + const loop = spline(blobLoop(fx, fy, R, i * 17 + 3, 0.16, 26), 4, true); + g2.save(); + tracePath(g2, loop, true); + g2.clip(); + drawScene(g2, W2, H2, i, T); + g2.restore(); + g2.save(); + tracePath(g2, loop, true); + g2.strokeStyle = rgba(INK, 0.16 * (1 - p)); + g2.lineWidth = Math.min(W2, H2) * 0.02 * (1 - p) + 1; + g2.stroke(); + g2.strokeStyle = rgba("#7a6a50", 0.12 * (1 - p)); + g2.lineWidth = Math.min(W2, H2) * 0.05 * (1 - p) + 1; + g2.stroke(); + g2.restore(); + } else { + drawScene(g2, W2, H2, i, T); + } + if (opts.subs !== false) drawSubs(g2, W2, H2, T); + if (opts.fadeEnd !== false) { + const f = smooth(inv(TOTAL - 1.2, TOTAL - 0.1, T)); + if (f > 0) { + g2.globalAlpha = f; + g2.drawImage(paper(W2, H2), 0, 0); + g2.globalAlpha = 1; + } + } + g2.restore(); + } + function currentSub(T) { + const i = sceneIndexAt(T); + const subs = TL.scenes[i].subs ?? []; + for (let k = 0; k < subs.length; k++) { + const s = subs[k]; + const end = k + 1 < subs.length ? subs[k + 1].t0 : s.t1 + 0.35; + if (T >= s.t0 - 0.05 && T < end) { + const joinedPrev = k > 0 && s.t0 - subs[k - 1].t1 < 0.12; + const joinedNext = k + 1 < subs.length && subs[k + 1].t0 - s.t1 < 0.12; + const fin = joinedPrev ? 1 : smooth((T - s.t0 + 0.05) / 0.18); + const fout = joinedNext ? 1 : smooth((end - T) / 0.15); + return { text: s.text, a: Math.min(fin, fout), who: s.who }; + } + } + return null; + } + function drawSubs(g2, W2, H2, T) { + const s = currentSub(T); + if (!s || s.a <= 0) return; + if (SCENES[TL.scenes[sceneIndexAt(T)].id]?.noSubs) return; + const portrait = H2 > W2 * 1.05; + const u = Math.min(W2, H2) / 1080; + const fs = (portrait ? 58 : 50) * u; + const y = portrait ? H2 * 0.785 : H2 - 86 * u; + g2.save(); + g2.globalAlpha = s.a; + g2.font = `${fs}px ${FONT_KAI}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const w = g2.measureText(s.text).width; + const rx = w / 2 + fs * 2.2, ry = fs * 1.35; + g2.save(); + g2.translate(W2 / 2, y); + g2.scale(rx / ry, 1); + const gr = g2.createRadialGradient(0, 0, 0, 0, 0, ry); + gr.addColorStop(0, rgba(PAPER, 0.95)); + gr.addColorStop(0.72, rgba(PAPER, 0.9)); + gr.addColorStop(1, rgba(PAPER, 0)); + g2.fillStyle = gr; + g2.fillRect(-ry, -ry, ry * 2, ry * 2); + g2.restore(); + g2.lineJoin = "round"; + g2.strokeStyle = rgba(PAPER, 0.96); + g2.lineWidth = fs * 0.2; + g2.strokeText(s.text, W2 / 2, y); + g2.fillStyle = rgba(s.who && FILM.subColors?.[s.who] || INK, 0.97); + g2.fillText(s.text, W2 / 2, y); + g2.restore(); + } + + // src/film/types.ts + var sceneAt = (tl3, id, dt) => (tl3.scenes.find((s) => s.id === id)?.start ?? 0) + dt; + + // src/films/duckling/timeline.json + var timeline_default = { + title: "\u4E11\u5C0F\u9E2D", + style: "ink-wash textbook illustration", + w: 1920, + h: 1080, + fps: 30, + total: 156.3, + frames: 4689, + voice: "zh-CN-XiaoxiaoNeural", + scenes: [ { - id: "marsh", - start: 59.3333, - dur: 13.666666666666666, - voice_start: 59.8333, - voice_dur: 12.264, + id: "title", + start: 0, + dur: 6.5, + voice_start: 0.5, + voice_dur: 2.832, subs: [ { - t0: 59.833, - t1: 61.921, - text: "\u4ED6\u6765\u5230\u4E00\u7247\u6CBC\u6CFD" - }, - { - t0: 61.921, - t1: 64.269, - text: "\u91CE\u9E2D\u4EEC\u7B11\u4ED6\u957F\u5F97\u602A" - }, - { - t0: 64.269, - t1: 65.052, - text: "\u5FFD\u7136" - }, - { - t0: 65.052, - t1: 65.574, - text: "\u7830\uFF01" - }, - { - t0: 65.574, - t1: 66.096, - text: "\u7830\uFF01" - }, - { - t0: 66.096, - t1: 68.183, - text: "\u730E\u4EBA\u7684\u67AA\u58F0\u54CD\u8D77" - }, - { - t0: 68.183, - t1: 70.271, - text: "\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7" + t0: 0.5, + t1: 2.199, + text: "\u5B89\u5F92\u751F\u7AE5\u8BDD" }, { - t0: 70.271, - t1: 72.097, - text: "\u4E00\u52A8\u4E5F\u4E0D\u6562\u52A8" + t0: 2.199, + t1: 3.332, + text: "\u4E11\u5C0F\u9E2D" } ], cue: { - type: "marsh" + type: "title" }, sfx: [ { - at: 3.4, - type: "shot" - }, - { - at: 3.9, - type: "shot" + at: 0.3, + type: "drop" }, { - at: 4.1, - type: "scatter" + at: 4.6, + type: "seal" } ] }, { - id: "autumn", - start: 73, - dur: 15.8, - voice_start: 73.5, - voice_dur: 14.4, + id: "summer", + start: 6.5, + dur: 11.566666666666666, + voice_start: 7, + voice_dur: 10.176, subs: [ { - t0: 73.5, - t1: 74.7, - text: "\u79CB\u5929\u5230\u4E86" - }, - { - t0: 74.7, - t1: 76.38, - text: "\u6811\u53F6\u53D8\u9EC4\u53D8\u7EA2" - }, - { - t0: 76.38, - t1: 77.58, - text: "\u4E00\u5929\u508D\u665A" - }, - { - t0: 77.58, - t1: 80.7, - text: "\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7" + t0: 7, + t1: 8.42, + text: "\u590F\u5929\u7684\u4E61\u4E0B" }, { - t0: 80.7, - t1: 82.14, - text: "\u8116\u5B50\u957F\u957F\u7684" + t0: 8.42, + t1: 9.603, + text: "\u9EA6\u5B50\u91D1\u9EC4" }, { - t0: 82.14, - t1: 83.82, - text: "\u7FC5\u8180\u53C8\u5927\u53C8\u7F8E" + t0: 9.603, + t1: 12.68, + text: "\u6C60\u5858\u8FB9\u7684\u82A6\u82C7\u957F\u5F97\u6BD4\u4EBA\u8FD8\u9AD8" }, { - t0: 83.82, - t1: 85.74, - text: "\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC" + t0: 12.68, + t1: 14.573, + text: "\u9E2D\u5988\u5988\u5C31\u5728\u8FD9\u91CC" }, { - t0: 85.74, - t1: 87.9, - text: "\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80" + t0: 14.573, + t1: 17.176, + text: "\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB" } ], cue: { - type: "autumn" + type: "summer" }, sfx: [ { - at: 2.4, - type: "call" + at: 0.2, + type: "breeze" } ] }, { - id: "winter", - start: 88.8, - dur: 13.433333333333334, - voice_start: 89.3, - voice_dur: 12.024, + id: "hatch", + start: 18.0667, + dur: 11.566666666666666, + voice_start: 18.5667, + voice_dur: 10.176, subs: [ { - t0: 89.3, - t1: 90.502, - text: "\u51AC\u5929\u6765\u4E86" - }, - { - t0: 90.502, - t1: 92.186, - text: "\u96EA\u82B1\u7EB7\u7EB7\u626C\u626C" + t0: 18.567, + t1: 19.26, + text: "\u5494\u5693" }, { - t0: 92.186, - t1: 93.629, - text: "\u6E56\u9762\u7ED3\u4E86\u51B0" + t0: 19.26, + t1: 19.954, + text: "\u5494\u5693\uFF01" }, { - t0: 93.629, - t1: 96.033, - text: "\u4E11\u5C0F\u9E2D\u51BB\u5F97\u7F29\u6210\u4E00\u56E2" + t0: 19.954, + t1: 22.267, + text: "\u86CB\u58F3\u4E00\u4E2A\u63A5\u4E00\u4E2A\u88C2\u5F00" }, { - t0: 96.033, - t1: 96.755, - text: "\u53EF\u662F" + t0: 22.267, + t1: 25.042, + text: "\u6BDB\u8338\u8338\u7684\u5C0F\u9EC4\u9E2D\u94BB\u4E86\u51FA\u6765" }, { - t0: 96.755, - t1: 97.957, - text: "\u4ED6\u54AC\u7740\u7259" + t0: 25.042, + t1: 26.43, + text: "\u5527\u5527\u5730\u53EB\u7740" }, { - t0: 97.957, - t1: 101.324, - text: "\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A" + t0: 26.43, + t1: 28.743, + text: "\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C" } ], cue: { - type: "winter" + type: "hatch" }, sfx: [ { - at: 0.2, - type: "wind" + at: 0.5, + type: "crack" }, { - at: 3, - type: "ice" + at: 1.2, + type: "crack" + }, + { + at: 2.2, + type: "peep" } ] }, { - id: "spring", - start: 102.2333, - dur: 12.366666666666667, - voice_start: 102.7333, - voice_dur: 10.968, + id: "bigegg", + start: 29.6333, + dur: 11.8, + voice_start: 30.1333, + voice_dur: 10.416, subs: [ { - t0: 102.733, - t1: 104.519, - text: "\u6625\u5929\u7EC8\u4E8E\u6765\u4E86\uFF01" - }, - { - t0: 104.519, - t1: 105.794, - text: "\u67F3\u6811\u53D1\u82BD" + t0: 30.133, + t1: 32.217, + text: "\u53EA\u5269\u6700\u5927\u7684\u90A3\u9897\u86CB" }, { - t0: 105.794, - t1: 107.07, - text: "\u6843\u82B1\u76DB\u5F00" + t0: 32.217, + t1: 33.374, + text: "\u8FC7\u4E86\u597D\u4E45" }, { - t0: 107.07, - t1: 109.365, - text: "\u4E11\u5C0F\u9E2D\u6247\u4E86\u6247\u7FC5\u8180" + t0: 33.374, + t1: 34.763, + text: "\u624D\u6162\u6162\u88C2\u5F00" }, { - t0: 109.365, - t1: 109.875, - text: "\u54A6\uFF1F" + t0: 34.763, + t1: 36.614, + text: "\u94BB\u51FA\u6765\u7684\u5C0F\u5BB6\u4F19" }, { - t0: 109.875, - t1: 112.171, - text: "\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B" + t0: 36.614, + t1: 37.772, + text: "\u53C8\u5927\u53C8\u7070" }, { - t0: 112.171, - t1: 113.701, - text: "\u4ED6\u98DE\u8D77\u6765\u4E86\uFF01" + t0: 37.772, + t1: 40.549, + text: "\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0" } ], cue: { - type: "spring" + type: "bigegg" }, sfx: [ { - at: 0.2, - type: "chime" - }, - { - at: 4.8, - type: "rise" + at: 2.6, + type: "bigcrack" } ] }, { - id: "reflection", - start: 114.6, - dur: 13.2, - voice_start: 115.1, - voice_dur: 11.784, + id: "bullied", + start: 41.4333, + dur: 10.366666666666667, + voice_start: 41.9333, + voice_dur: 8.952, subs: [ { - t0: 115.1, - t1: 116.686, - text: "\u4ED6\u843D\u5230\u6E56\u9762\u4E0A" - }, - { - t0: 116.686, - t1: 118.952, - text: "\u51E0\u53EA\u767D\u5929\u9E45\u5411\u4ED6\u6E38\u6765" - }, - { - t0: 118.952, - t1: 120.086, - text: "\u4ED6\u4F4E\u4E0B\u5934" + t0: 41.933, + t1: 44.111, + text: "\u5927\u5BB6\u90FD\u53EB\u4ED6\u4E11\u5C0F\u9E2D" }, { - t0: 120.086, - t1: 121.898, - text: "\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71" + t0: 44.111, + t1: 46.046, + text: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6" }, { - t0: 121.898, - t1: 124.391, - text: "\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50" + t0: 46.046, + t1: 47.74, + text: "\u516C\u9E21\u8FFD\u7740\u5544\u4ED6" }, { - t0: 124.391, - t1: 126.884, - text: "\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\uFF01" + t0: 47.74, + t1: 50.885, + text: "\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B" } ], cue: { - type: "reflection" + type: "bullied" }, sfx: [ { - at: 0.6, - type: "splash" + at: 0.8, + type: "quack" }, { - at: 5.6, - type: "reveal" + at: 3.2, + type: "flap" } ] }, { - id: "accepted", - start: 127.8, - dur: 11.5, - voice_start: 128.3, - voice_dur: 10.104, + id: "leave", + start: 51.8, + dur: 7.533333333333333, + voice_start: 52.3, + voice_dur: 6.144, subs: [ { - t0: 128.3, - t1: 129.945, - text: "\u5929\u9E45\u4EEC\u56F4\u7740\u4ED6" - }, - { - t0: 129.945, - t1: 132.53, - text: "\u7528\u5634\u8F7B\u8F7B\u68B3\u7406\u4ED6\u7684\u7FBD\u6BDB" + t0: 52.3, + t1: 54.348, + text: "\u4E11\u5C0F\u9E2D\u96BE\u8FC7\u6781\u4E86" }, { - t0: 132.53, - t1: 135.114, - text: "\u5CB8\u8FB9\u7684\u5B69\u5B50\u4EEC\u62CD\u7740\u624B\u558A" + t0: 54.348, + t1: 55.116, + text: "\u4E00\u5929" }, { - t0: 135.114, - t1: 135.819, - text: "\u5FEB\u770B" + t0: 55.116, + t1: 57.164, + text: "\u4ED6\u6084\u6084\u94BB\u8FC7\u7BF1\u7B06" }, { - t0: 135.819, - t1: 138.404, - text: "\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\uFF01" + t0: 57.164, + t1: 58.444, + text: "\u79BB\u5F00\u4E86\u5BB6" } ], cue: { - type: "accepted" + type: "leave" }, sfx: [ { - at: 3.6, - type: "cheer" + at: 0.3, + type: "wind" } ] }, { - id: "fly", - start: 139.3, - dur: 7, - voice_start: 139.8, - voice_dur: 5.472, + id: "marsh", + start: 59.3333, + dur: 13.666666666666666, + voice_start: 59.8333, + voice_dur: 12.264, subs: [ { - t0: 139.8, - t1: 141.852, - text: "\u4ED6\u5F20\u5F00\u96EA\u767D\u7684\u7FC5\u8180" + t0: 59.833, + t1: 61.921, + text: "\u4ED6\u6765\u5230\u4E00\u7247\u6CBC\u6CFD" }, { - t0: 141.852, - t1: 143.448, - text: "\u548C\u4F19\u4F34\u4EEC\u4E00\u8D77" + t0: 61.921, + t1: 64.269, + text: "\u91CE\u9E2D\u4EEC\u7B11\u4ED6\u957F\u5F97\u602A" }, { - t0: 143.448, - t1: 145.272, - text: "\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929" + t0: 64.269, + t1: 65.052, + text: "\u5FFD\u7136" + }, + { + t0: 65.052, + t1: 65.574, + text: "\u7830\uFF01" + }, + { + t0: 65.574, + t1: 66.096, + text: "\u7830\uFF01" + }, + { + t0: 66.096, + t1: 68.183, + text: "\u730E\u4EBA\u7684\u67AA\u58F0\u54CD\u8D77" + }, + { + t0: 68.183, + t1: 70.271, + text: "\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7" + }, + { + t0: 70.271, + t1: 72.097, + text: "\u4E00\u52A8\u4E5F\u4E0D\u6562\u52A8" } ], cue: { - type: "fly" + type: "marsh" }, sfx: [ { - at: 0.4, - type: "flap" + at: 3.4, + type: "shot" }, { - at: 1, - type: "rise" + at: 3.9, + type: "shot" + }, + { + at: 4.1, + type: "scatter" } ] }, { - id: "ending", - start: 146.3, - dur: 10, - voice_start: 146.8, - voice_dur: 8.376, + id: "autumn", + start: 73, + dur: 15.8, + voice_start: 73.5, + voice_dur: 14.4, subs: [ { - t0: 146.8, - t1: 148.429, - text: "\u53EA\u8981\u5FC3\u91CC\u6709\u68A6" + t0: 73.5, + t1: 74.7, + text: "\u79CB\u5929\u5230\u4E86" }, { - t0: 148.429, - t1: 149.592, - text: "\u4E0D\u6015\u98CE\u96EA" + t0: 74.7, + t1: 76.38, + text: "\u6811\u53F6\u53D8\u9EC4\u53D8\u7EA2" }, { - t0: 149.592, - t1: 150.988, - text: "\u4E5F\u4E0D\u6015\u5632\u7B11" + t0: 76.38, + t1: 77.58, + text: "\u4E00\u5929\u508D\u665A" }, { - t0: 150.988, - t1: 152.151, - text: "\u603B\u6709\u4E00\u5929" + t0: 77.58, + t1: 80.7, + text: "\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7" }, { - t0: 152.151, - t1: 155.176, - text: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180" + t0: 80.7, + t1: 82.14, + text: "\u8116\u5B50\u957F\u957F\u7684" + }, + { + t0: 82.14, + t1: 83.82, + text: "\u7FC5\u8180\u53C8\u5927\u53C8\u7F8E" + }, + { + t0: 83.82, + t1: 85.74, + text: "\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC" + }, + { + t0: 85.74, + t1: 87.9, + text: "\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80" } ], cue: { - type: "ending" + type: "autumn" }, sfx: [ { - at: 6.2, - type: "seal" + at: 2.4, + type: "call" } ] - } - ] - }; - - // src/film/core.ts - var core_exports = {}; - __export(core_exports, { - FONT_BRUSH: () => FONT_BRUSH, - FONT_KAI: () => FONT_KAI, - INK: () => INK, - PAPER: () => PAPER, - SEAL: () => SEAL, - backOut: () => backOut, - blobLoop: () => blobLoop, - blot: () => blot, - brush: () => brush, - clamp: () => clamp, - ctx2d: () => ctx2d, - easeIn: () => easeIn, - easeInOut: () => easeInOut, - easeOut: () => easeOut, - hash: () => hash, - inv: () => inv, - lerp: () => lerp, - loadFonts: () => loadFonts, - makeCanvas: () => makeCanvas, - mixHex: () => mixHex, - mountains: () => mountains, - noise1: () => noise1, - nz: () => nz, - outline: () => outline, - paper: () => paper, - rgba: () => rgba, - rng: () => rng, - seal: () => seal, - smooth: () => smooth, - spline: () => spline, - spring: () => spring, - stroke: () => stroke, - subPath: () => subPath, - tracePath: () => tracePath, - wash: () => wash - }); - function rng(seed) { - let a = seed >>> 0 || 1; - return () => { - a = a + 1831565813 >>> 0; - let t = a; - t = Math.imul(t ^ t >>> 15, t | 1); - t ^= t + Math.imul(t ^ t >>> 7, t | 61); - return ((t ^ t >>> 14) >>> 0) / 4294967296; - }; - } - function hash(n) { - const s = Math.sin(n * 127.1 + 311.7) * 43758.5453; - return s - Math.floor(s); - } - function noise1(x, seed = 0) { - const i = Math.floor(x), f = x - i; - const a = hash(i + seed * 101.3), b = hash(i + 1 + seed * 101.3); - const u = f * f * (3 - 2 * f); - return a + (b - a) * u; - } - var nz = (x, seed = 0) => noise1(x, seed) * 2 - 1; - var clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x)); - var lerp = (a, b, t) => a + (b - a) * t; - var inv = (a, b, x) => clamp((x - a) / (b - a)); - var smooth = (x) => { - x = clamp(x); - return x * x * (3 - 2 * x); - }; - var easeOut = (x) => 1 - Math.pow(1 - clamp(x), 3); - var easeIn = (x) => Math.pow(clamp(x), 3); - var easeInOut = (x) => { - x = clamp(x); - return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; - }; - var backOut = (x) => { - x = clamp(x); - const c1 = 1.70158, c3 = c1 + 1; - return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2); - }; - var spring = (x) => { - x = Math.max(0, x); - return 1 - Math.exp(-6 * x) * Math.cos(11 * x); - }; - var INK = "#1d1a17"; - var PAPER = "#f6efdf"; - var SEAL = "#b8322a"; - function rgba(hex, a) { - const n = parseInt(hex.slice(1), 16); - return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`; - } - function mixHex(a, b, t) { - const x = parseInt(a.slice(1), 16), y = parseInt(b.slice(1), 16); - const c2 = (s) => Math.round(lerp(x >> s & 255, y >> s & 255, clamp(t))); - return "#" + (1 << 24 | c2(16) << 16 | c2(8) << 8 | c2(0)).toString(16).slice(1); - } - function makeCanvas(w, h) { - if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(w, h); - const c2 = document.createElement("canvas"); - c2.width = w; - c2.height = h; - return c2; - } - function ctx2d(c2) { - return c2.getContext("2d"); - } - var paperCache = /* @__PURE__ */ new Map(); - function paper(W2, H2) { - const key = W2 + "x" + H2; - const hit = paperCache.get(key); - if (hit) return hit; - const c2 = makeCanvas(W2, H2), g2 = ctx2d(c2), r = rng(20261017); - g2.fillStyle = PAPER; - g2.fillRect(0, 0, W2, H2); - const u = Math.min(W2, H2) / 1080; - for (let i = 0; i < 46; i++) { - const x = r() * W2, y = r() * H2, rad = (120 + r() * 420) * u; - const gr = g2.createRadialGradient(x, y, 0, x, y, rad); - const dark = r() < 0.55; - gr.addColorStop(0, dark ? "rgba(170,140,90,0.028)" : "rgba(255,252,240,0.08)"); - gr.addColorStop(1, "rgba(0,0,0,0)"); - g2.fillStyle = gr; - g2.fillRect(x - rad, y - rad, rad * 2, rad * 2); - } - g2.strokeStyle = "rgba(150,125,85,0.035)"; - g2.lineWidth = Math.max(1, u); - for (let y = 0; y < H2; y += 9 * u) { - g2.beginPath(); - g2.moveTo(0, y + r() * 2); - g2.lineTo(W2, y + r() * 2); - g2.stroke(); - } - const nf = Math.round(W2 * H2 / 2600); - for (let i = 0; i < nf; i++) { - const x = r() * W2, y = r() * H2, len = (6 + r() * 26) * u, ang = r() * Math.PI * 2; - g2.strokeStyle = r() < 0.7 ? `rgba(150,120,75,${0.05 + r() * 0.08})` : `rgba(255,252,240,${0.2 + r() * 0.25})`; - g2.lineWidth = (0.4 + r() * 0.9) * u; - g2.beginPath(); - g2.moveTo(x, y); - g2.quadraticCurveTo(x + Math.cos(ang + 0.6) * len * 0.5, y + Math.sin(ang + 0.6) * len * 0.5, x + Math.cos(ang) * len, y + Math.sin(ang) * len); - g2.stroke(); - } - try { - const id = g2.getImageData(0, 0, W2, H2), d = id.data; - let s = 12345; - for (let i = 0; i < d.length; i += 4) { - s = s * 1103515245 + 12345 & 2147483647; - const n = (s >> 8) % 15 - 7; - d[i] += n; - d[i + 1] += n; - d[i + 2] += n; - } - g2.putImageData(id, 0, 0); - } catch { - } - const vg = g2.createRadialGradient(W2 / 2, H2 / 2, Math.min(W2, H2) * 0.35, W2 / 2, H2 / 2, Math.hypot(W2, H2) * 0.62); - vg.addColorStop(0, "rgba(0,0,0,0)"); - vg.addColorStop(1, "rgba(120,90,50,0.13)"); - g2.fillStyle = vg; - g2.fillRect(0, 0, W2, H2); - paperCache.set(key, c2); - if (paperCache.size > 4) paperCache.delete(paperCache.keys().next().value); - return c2; - } - function spline(pts, seg = 8, closed = false) { - const out = []; - const n = pts.length; - if (n < 3) return pts.slice(); - const get = (i) => closed ? pts[(i + n) % n] : pts[Math.max(0, Math.min(n - 1, i))]; - const last = closed ? n : n - 1; - for (let i = 0; i < last; i++) { - const p0 = get(i - 1), p1 = get(i), p2 = get(i + 1), p3 = get(i + 2); - for (let j = 0; j < seg; j++) { - const t = j / seg, t2 = t * t, t3 = t2 * t; - out.push([ - 0.5 * (2 * p1[0] + (-p0[0] + p2[0]) * t + (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 + (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3), - 0.5 * (2 * p1[1] + (-p0[1] + p2[1]) * t + (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 + (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3) - ]); - } - } - if (!closed) out.push(pts[n - 1]); - return out; - } - function subPath(pts, a, b) { - if (pts.length < 2) return pts; - const L = [0]; - for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); - const tot = L[L.length - 1], A = a * tot, B = b * tot, out = []; - const at2 = (d) => { - for (let i = 1; i < L.length; i++) if (L[i] >= d) { - const f = (d - L[i - 1]) / Math.max(1e-6, L[i] - L[i - 1]); - return [lerp(pts[i - 1][0], pts[i][0], f), lerp(pts[i - 1][1], pts[i][1], f)]; - } - return pts[pts.length - 1]; - }; - out.push(at2(A)); - for (let i = 0; i < pts.length; i++) if (L[i] > A && L[i] < B) out.push(pts[i]); - out.push(at2(B)); - return out; - } - function tracePath(g2, pts, close = false) { - g2.beginPath(); - g2.moveTo(pts[0][0], pts[0][1]); - for (let i = 1; i < pts.length; i++) g2.lineTo(pts[i][0], pts[i][1]); - if (close) g2.closePath(); - } - function brush(g2, pts0, w, o = {}) { - const grow = o.grow ?? 1; - if (grow <= 0 || pts0.length < 2) return; - const pts = grow < 1 ? subPath(pts0, 0, grow) : pts0; - const n = pts.length; - const L = [0]; - for (let i = 1; i < n; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); - const tot = Math.max(1e-6, L[n - 1]); - const tin = o.taperIn ?? 0.18, tout = o.taperOut ?? 0.35, jit = o.jitter ?? 0.18, seed = o.seed ?? 1; - const left = [], right = []; - for (let i = 0; i < n; i++) { - const s = L[i] / tot; - const a = pts[Math.max(0, i - 1)], b = pts[Math.min(n - 1, i + 1)]; - let dx = b[0] - a[0], dy = b[1] - a[1]; - const d = Math.hypot(dx, dy) || 1; - dx /= d; - dy /= d; - let prof = Math.min(1, 0.35 + 0.65 * smooth(s / tin)) * Math.min(1, Math.pow(clamp((1 - s) / tout), 0.7) * 0.92 + 0.08); - if (o.press) prof *= 1 + o.press * Math.exp(-s * 9); - const ww = w / 2 * prof * (1 + jit * nz(s * 7 + seed * 3.1, seed)); - left.push([pts[i][0] - dy * ww, pts[i][1] + dx * ww]); - right.push([pts[i][0] + dy * ww, pts[i][1] - dx * ww]); - } - g2.fillStyle = rgba(o.color ?? INK, o.alpha ?? 0.92); - g2.beginPath(); - g2.moveTo(left[0][0], left[0][1]); - for (let i = 1; i < n; i++) g2.lineTo(left[i][0], left[i][1]); - for (let i = n - 1; i >= 0; i--) g2.lineTo(right[i][0], right[i][1]); - g2.closePath(); - g2.fill(); - if ((o.alpha ?? 0.92) > 0.5 && w > 3) { - g2.fillStyle = rgba(o.color ?? INK, 0.12); - g2.beginPath(); - g2.arc(pts[0][0], pts[0][1], w / 2 * 0.55, 0, Math.PI * 2); - g2.fill(); - } - if (o.dry && o.dry > 0) { - g2.strokeStyle = rgba(PAPER, 0.75 * o.dry); - const lines = 4; - for (let k = 0; k < lines; k++) { - const off = (k + 0.5) / lines - 0.5; - g2.lineWidth = Math.max(0.6, w * 0.07); - g2.beginPath(); - let started = false; - for (let i = Math.floor(n * (0.45 + 0.1 * hash(seed + k))); i < n; i++) { - const sx = lerp(left[i][0], right[i][0], 0.5 + off * 0.8), sy = lerp(left[i][1], right[i][1], 0.5 + off * 0.8); - if (hash(i * 0.37 + k * 13 + seed) < 0.18) { - started = false; - continue; + }, + { + id: "winter", + start: 88.8, + dur: 13.433333333333334, + voice_start: 89.3, + voice_dur: 12.024, + subs: [ + { + t0: 89.3, + t1: 90.502, + text: "\u51AC\u5929\u6765\u4E86" + }, + { + t0: 90.502, + t1: 92.186, + text: "\u96EA\u82B1\u7EB7\u7EB7\u626C\u626C" + }, + { + t0: 92.186, + t1: 93.629, + text: "\u6E56\u9762\u7ED3\u4E86\u51B0" + }, + { + t0: 93.629, + t1: 96.033, + text: "\u4E11\u5C0F\u9E2D\u51BB\u5F97\u7F29\u6210\u4E00\u56E2" + }, + { + t0: 96.033, + t1: 96.755, + text: "\u53EF\u662F" + }, + { + t0: 96.755, + t1: 97.957, + text: "\u4ED6\u54AC\u7740\u7259" + }, + { + t0: 97.957, + t1: 101.324, + text: "\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A" + } + ], + cue: { + type: "winter" + }, + sfx: [ + { + at: 0.2, + type: "wind" + }, + { + at: 3, + type: "ice" + } + ] + }, + { + id: "spring", + start: 102.2333, + dur: 12.366666666666667, + voice_start: 102.7333, + voice_dur: 10.968, + subs: [ + { + t0: 102.733, + t1: 104.519, + text: "\u6625\u5929\u7EC8\u4E8E\u6765\u4E86\uFF01" + }, + { + t0: 104.519, + t1: 105.794, + text: "\u67F3\u6811\u53D1\u82BD" + }, + { + t0: 105.794, + t1: 107.07, + text: "\u6843\u82B1\u76DB\u5F00" + }, + { + t0: 107.07, + t1: 109.365, + text: "\u4E11\u5C0F\u9E2D\u6247\u4E86\u6247\u7FC5\u8180" + }, + { + t0: 109.365, + t1: 109.875, + text: "\u54A6\uFF1F" + }, + { + t0: 109.875, + t1: 112.171, + text: "\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B" + }, + { + t0: 112.171, + t1: 113.701, + text: "\u4ED6\u98DE\u8D77\u6765\u4E86\uFF01" + } + ], + cue: { + type: "spring" + }, + sfx: [ + { + at: 0.2, + type: "chime" + }, + { + at: 4.8, + type: "rise" + } + ] + }, + { + id: "reflection", + start: 114.6, + dur: 13.2, + voice_start: 115.1, + voice_dur: 11.784, + subs: [ + { + t0: 115.1, + t1: 116.686, + text: "\u4ED6\u843D\u5230\u6E56\u9762\u4E0A" + }, + { + t0: 116.686, + t1: 118.952, + text: "\u51E0\u53EA\u767D\u5929\u9E45\u5411\u4ED6\u6E38\u6765" + }, + { + t0: 118.952, + t1: 120.086, + text: "\u4ED6\u4F4E\u4E0B\u5934" + }, + { + t0: 120.086, + t1: 121.898, + text: "\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71" + }, + { + t0: 121.898, + t1: 124.391, + text: "\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50" + }, + { + t0: 124.391, + t1: 126.884, + text: "\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\uFF01" + } + ], + cue: { + type: "reflection" + }, + sfx: [ + { + at: 0.6, + type: "splash" + }, + { + at: 5.6, + type: "reveal" + } + ] + }, + { + id: "accepted", + start: 127.8, + dur: 11.5, + voice_start: 128.3, + voice_dur: 10.104, + subs: [ + { + t0: 128.3, + t1: 129.945, + text: "\u5929\u9E45\u4EEC\u56F4\u7740\u4ED6" + }, + { + t0: 129.945, + t1: 132.53, + text: "\u7528\u5634\u8F7B\u8F7B\u68B3\u7406\u4ED6\u7684\u7FBD\u6BDB" + }, + { + t0: 132.53, + t1: 135.114, + text: "\u5CB8\u8FB9\u7684\u5B69\u5B50\u4EEC\u62CD\u7740\u624B\u558A" + }, + { + t0: 135.114, + t1: 135.819, + text: "\u5FEB\u770B" + }, + { + t0: 135.819, + t1: 138.404, + text: "\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\uFF01" + } + ], + cue: { + type: "accepted" + }, + sfx: [ + { + at: 3.6, + type: "cheer" + } + ] + }, + { + id: "fly", + start: 139.3, + dur: 7, + voice_start: 139.8, + voice_dur: 5.472, + subs: [ + { + t0: 139.8, + t1: 141.852, + text: "\u4ED6\u5F20\u5F00\u96EA\u767D\u7684\u7FC5\u8180" + }, + { + t0: 141.852, + t1: 143.448, + text: "\u548C\u4F19\u4F34\u4EEC\u4E00\u8D77" + }, + { + t0: 143.448, + t1: 145.272, + text: "\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929" + } + ], + cue: { + type: "fly" + }, + sfx: [ + { + at: 0.4, + type: "flap" + }, + { + at: 1, + type: "rise" + } + ] + }, + { + id: "ending", + start: 146.3, + dur: 10, + voice_start: 146.8, + voice_dur: 8.376, + subs: [ + { + t0: 146.8, + t1: 148.429, + text: "\u53EA\u8981\u5FC3\u91CC\u6709\u68A6" + }, + { + t0: 148.429, + t1: 149.592, + text: "\u4E0D\u6015\u98CE\u96EA" + }, + { + t0: 149.592, + t1: 150.988, + text: "\u4E5F\u4E0D\u6015\u5632\u7B11" + }, + { + t0: 150.988, + t1: 152.151, + text: "\u603B\u6709\u4E00\u5929" + }, + { + t0: 152.151, + t1: 155.176, + text: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180" } - if (!started) { - g2.moveTo(sx, sy); - started = true; - } else g2.lineTo(sx, sy); - } - g2.stroke(); - } - } - } - function stroke(g2, ctrl, w, o = {}) { - brush(g2, spline(ctrl, 10), w, o); - } - function outline(g2, loop, w, o = {}) { - const pts = spline(loop, 8, true); - pts.push(pts[0]); - const br = o.breaks ?? [0, 0.36, 0.7, 1]; - for (let i = 0; i < br.length - 1; i++) { - const a = br[i] + 0.012, b = br[i + 1] - 0.012; - if (b > a) brush(g2, subPath(pts, a, b), w, { taperIn: 0.12, taperOut: 0.25, ...o, seed: (o.seed ?? 1) + i * 7 }); - } - } - function wash(g2, loop, color, alpha = 0.6, o = {}) { - const pts = spline(loop, 8, o.closed ?? true); - g2.save(); - g2.translate(o.dx ?? 0, o.dy ?? 0); - tracePath(g2, pts, true); - g2.fillStyle = rgba(color, alpha); - g2.fill(); - if ((o.edge ?? 1) > 0) { - g2.strokeStyle = rgba(color, Math.min(1, alpha * 0.5 * (o.edge ?? 1))); - g2.lineWidth = o.lw ?? 2.2; - g2.lineJoin = "round"; - g2.stroke(); - } - g2.restore(); - } - function blot(g2, x, y, r, color, a, sx = 1, sy = 1) { - if (r <= 0 || a <= 0) return; - g2.save(); - g2.translate(x, y); - g2.scale(sx, sy); - const gr = g2.createRadialGradient(0, 0, 0, 0, 0, r); - gr.addColorStop(0, rgba(color, a)); - gr.addColorStop(0.55, rgba(color, a * 0.6)); - gr.addColorStop(1, rgba(color, 0)); - g2.fillStyle = gr; - g2.fillRect(-r, -r, r * 2, r * 2); - g2.restore(); - } - function blobLoop(cx, cy, r, seed, rough = 0.18, n = 28) { - const out = []; - for (let i = 0; i < n; i++) { - const a = i / n * Math.PI * 2; - const rr = r * (1 + rough * nz(i * 0.7 + seed, seed) + rough * 0.5 * nz(i * 2.3 + seed * 3, seed + 1)); - out.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]); - } - return out; - } - function mountains(g2, W2, baseY, height, color, alpha, seed, o = {}) { - const f = o.freq ?? 2.2; - const pts = []; - const steps = 60; - for (let i = 0; i <= steps; i++) { - const x = i / steps * W2; - const v = 0.55 * noise1(i / steps * f + seed, seed) + 0.3 * noise1(i / steps * f * 2.7 + seed * 2, seed + 1) + 0.15 * noise1(i / steps * f * 7 + seed * 5, seed + 2); - pts.push([x, baseY - height * Math.pow(v, 1.4) * 1.6]); - } - const top = Math.min(...pts.map((p) => p[1])); - const gr = g2.createLinearGradient(0, top, 0, baseY); - gr.addColorStop(0, rgba(color, alpha)); - gr.addColorStop(0.6, rgba(color, alpha * 0.55)); - gr.addColorStop(1, rgba(color, 0)); - g2.fillStyle = gr; - g2.beginPath(); - g2.moveTo(0, baseY + 2); - for (const p of pts) g2.lineTo(p[0], p[1]); - g2.lineTo(W2, baseY + 2); - g2.closePath(); - g2.fill(); - if ((o.soft ?? 0) < 1) { - const r = rng(seed * 991); - for (let k = 0; k < 6; k++) { - const a = r() * 0.85, b = a + 0.06 + r() * 0.12; - brush(g2, subPath(pts, a, Math.min(1, b)), 2.5 + r() * 3, { color, alpha: alpha * 0.9, seed: seed + k, jitter: 0.4, dry: 0.4 }); - } - } - } - function seal(g2, x, y, size, text, o = {}) { - const a = o.alpha ?? 1; - if (a <= 0) return; - g2.save(); - g2.translate(x, y); - const s = size; - g2.globalAlpha = a; - const loop = [[-s / 2, -s / 2], [s / 2, -s / 2], [s / 2, s / 2], [-s / 2, s / 2]]; - g2.fillStyle = SEAL; - g2.beginPath(); - if (o.round) g2.arc(0, 0, s / 2, 0, Math.PI * 2); - else { - g2.moveTo(loop[0][0], loop[0][1]); - for (const p of loop.slice(1)) g2.lineTo(p[0], p[1]); - g2.closePath(); - } - g2.fill(); - g2.fillStyle = PAPER; - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const cols = text.length; - const rows = Math.max(...text.map((c2) => c2.length)); - const cell = s * 0.84 / Math.max(cols, rows); - g2.font = `${cell * 0.95}px ${o.font ?? FONT_BRUSH}`; - for (let c2 = 0; c2 < cols; c2++) { - const col = text[c2]; - for (let r2 = 0; r2 < col.length; r2++) { - const cx = (cols - 1) / 2 * cell - c2 * cell; - const cy = (r2 - (col.length - 1) / 2) * cell * (rows / col.length); - g2.fillText(col[r2], cx, cy + cell * 0.04); + ], + cue: { + type: "ending" + }, + sfx: [ + { + at: 6.2, + type: "seal" + } + ] } - } - const r = rng(Math.round(x * 7 + y * 13)); - g2.fillStyle = PAPER; - for (let i = 0; i < 26; i++) { - g2.globalAlpha = a * (0.25 + r() * 0.5); - g2.beginPath(); - g2.arc((r() - 0.5) * s, (r() - 0.5) * s, r() * s * 0.025 + 0.5, 0, Math.PI * 2); - g2.fill(); - } - g2.restore(); - } - var FONT_KAI = '"WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; - var FONT_BRUSH = '"MaShanZhengFilm", "WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; - var fontsPromise = null; - function loadFonts(base) { - if (fontsPromise) return fontsPromise; - if (typeof FontFace === "undefined") return fontsPromise = Promise.resolve(); - const list = [["WenKaiFilm", base + "film/fonts/wenkai.woff2"], ["MaShanZhengFilm", base + "film/fonts/mashanzheng.woff2"]]; - fontsPromise = Promise.all(list.map(async ([fam, url]) => { - try { - const f = new FontFace(fam, `url(${url})`); - await f.load(); - document.fonts.add(f); - } catch (e) { - console.warn("font load failed", fam, e); + ] + }; + + // src/films/duckling/live.json + var live_default = [ + { id: "01-egg", scene: "bigegg", from: 2.2, dur: 3, cover: 2.4, caption: "\u6700\u5927\u7684\u86CB", index: "\u5176\u4E00", xhs: "\u6700\u540E\u4E00\u9897\u86CB\u6700\u5927\uFF0C\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u6765\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19\u3002" }, + { id: "02-laugh", scene: "bullied", from: 3, dur: 3, cover: 1, caption: "\u4E11\u5C0F\u9E2D", index: "\u5176\u4E8C", xhs: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6\uFF0C\u5927\u5BB6\u90FD\u53EB\u4ED6\u300C\u4E11\u5C0F\u9E2D\u300D\u3002" }, + { id: "03-swans", scene: "autumn", from: 4.6, dur: 3, cover: 1.6, caption: "\u4EF0\u671B", index: "\u5176\u4E09", xhs: "\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u98DE\u8FC7\uFF0C\u4ED6\u671B\u7740\u5929\u7A7A\uFF0C\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80\u3002" }, + { id: "04-snow", scene: "winter", from: 6.4, dur: 3, cover: 1.6, caption: "\u5BD2\u51AC", index: "\u5176\u56DB", xhs: "\u96EA\u591C\u91CC\u6E56\u9762\u7ED3\u4E86\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\uFF0C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u3002" }, + { id: "05-reflect", scene: "reflection", from: 5.4, dur: 3, cover: 2.4, caption: "\u5012\u5F71", index: "\u5176\u4E94", xhs: "\u4ED6\u4F4E\u4E0B\u5934\uFF0C\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71\u2014\u2014\u90A3\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u3002" }, + { id: "06-fly", scene: "fly", from: 2.6, dur: 3, cover: 1.6, caption: "\u5C55\u7FC5", index: "\u5176\u516D", xhs: "\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002\u5F20\u5F00\u7FC5\u8180\uFF0C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u3002" } + ]; + + // src/films/duckling/qa.json + var qa_default = { + status: "done", + generated: "2026-10-17", + meta: { + w: 1080, + h: 1920, + fps: 30, + dur: 156.3, + audio: true + }, + verdict: "PASS", + results: [ + { + level: "PASS", + check: "\u7AD6\u5C4F \u5206\u8FA8\u7387", + msg: "1080\xD71920\uFF08\u65F6\u95F4\u8F74 1080\xD71920\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u5E27\u7387", + msg: "30.0 fps" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u65F6\u957F", + msg: "156.30s\uFF08\u65F6\u95F4\u8F74 156.30s\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u97F3\u8F68", + msg: "\u6709" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u54CD\u5EA6", + msg: "-14.0 LUFS\uFF08\u76EE\u6807 \u221214\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u771F\u5CF0\u503C", + msg: "-1.4 dBTP\uFF08\u5E94 \u2264 \u22121\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u9ED1\u573A", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u51BB\u7ED3\u753B\u9762", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@title", + msg: "OCR \u8BFB\u51FA 16 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@summer", + msg: "OCR \u8BFB\u51FA 37 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 88%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@hatch", + msg: "OCR \u8BFB\u51FA 31 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@bigegg", + msg: "OCR \u8BFB\u51FA 33 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@bullied", + msg: "OCR \u8BFB\u51FA 35 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@leave", + msg: "OCR \u8BFB\u51FA 21 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@marsh", + msg: "OCR \u8BFB\u51FA 14 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 86%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@autumn", + msg: "OCR \u8BFB\u51FA 20 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@winter", + msg: "OCR \u8BFB\u51FA 40 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 91%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@spring", + msg: "OCR \u8BFB\u51FA 22 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "INFO", + check: "\u7AD6\u5C4F \u6587\u5B57@reflection", + msg: "OCR \u8BFB\u51FA 47 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 53%\uFF08\u68C0\u67E5\u7A97\u53E3\u540C\u65F6\u8BA1\u5165\u4E86\u4E0A\u4E00\u53E5\u5B57\u5E55\uFF1B\u5F53\u524D\u5B57\u5E55\u300C\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u300D\u5DF2\u88AB\u8BFB\u51FA\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@accepted", + msg: "OCR \u8BFB\u51FA 33 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@fly", + msg: "OCR \u8BFB\u51FA 11 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@ending", + msg: "OCR \u8BFB\u51FA 28 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5206\u8FA8\u7387", + msg: "1920\xD71080\uFF08\u65F6\u95F4\u8F74 1920\xD71080\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5E27\u7387", + msg: "30.0 fps" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u65F6\u957F", + msg: "156.30s\uFF08\u65F6\u95F4\u8F74 156.30s\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u97F3\u8F68", + msg: "\u6709" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u54CD\u5EA6", + msg: "-14.0 LUFS\uFF08\u76EE\u6807 \u221214\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u771F\u5CF0\u503C", + msg: "-1.4 dBTP\uFF08\u5E94 \u2264 \u22121\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u9ED1\u573A", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u51BB\u7ED3\u753B\u9762", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@summer", + msg: "\u300C\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB\u300DOCR \u547D\u4E2D 88%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.5:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@hatch", + msg: "\u300C\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.4:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@bigegg", + msg: "\u300C\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.7:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@bullied", + msg: "\u300C\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 13.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@leave", + msg: "\u300C\u79BB\u5F00\u4E86\u5BB6\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 3.5:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@marsh", + msg: "\u300C\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7\u300DOCR \u547D\u4E2D 0%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 11.6:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@autumn", + msg: "\u300C\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 11.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@winter", + msg: "\u300C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u300DOCR \u547D\u4E2D 91%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.9:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@spring", + msg: "\u300C\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@reflection", + msg: "\u300C\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u300DOCR \u547D\u4E2D 53%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.1:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@accepted", + msg: "\u300C\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\u300DOCR \u547D\u4E2D 90%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.9:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@fly", + msg: "\u300C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 10.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@ending", + msg: "\u300C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 13.1:1" + }, + { + level: "INFO", + check: "\u8BF4\u660E", + msg: "\u6A2A\u5C4F\u5B57\u5E55\u4F4D\u4E8E\u753B\u9762\u5E95\u90E8 ~92%\uFF0Cvibe qa \u7684 OCR \u53EA\u626B 60\u201384% \u9AD8\u5EA6\u5E26\uFF0C\u6545\u6A2A\u5C4F\u6539\u7528\u300C\u5B57\u5E55\u5E26 OCR + \u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6\uFF08\u22657:1\uFF09\u300D\u8865\u5145\u68C0\u67E5\u3002" + }, + { + level: "PASS", + check: "\u786E\u5B9A\u6027", + msg: "vibe det\uFF1A\u540C\u4E00\u5E27\u4E71\u5E8F\u91CD\u6E32\u9010\u50CF\u7D20\u4E00\u81F4" + }, + { + level: "PASS", + check: "\u5B9E\u51B5\u56FE", + msg: "6 \u7EC4 JPG+MOV\uFF1Acontent identifier \u4E00\u81F4\u3001still-image-time \u8BFB\u56DE\u6B63\u786E\u3001MOV \u5B8C\u6574\u89E3\u7801\u3001\u54C8\u5E0C\u6821\u9A8C\u901A\u8FC7\uFF08PhotoKit \u672C\u673A\u52A0\u8F7D\uFF1ALinux \u73AF\u5883\u4E0D\u53EF\u7528\uFF09" } - })).then(() => void 0); - return fontsPromise; - } + ] + }; // src/film/actors.ts var actors_exports = {}; @@ -1198,13 +1608,13 @@ const sgn = k === 0 ? 1 : -1; const swing = walk ? Math.sin(ph + (k ? Math.PI : 0)) * 0.35 : 0; const hip = [0.02 + 0.08 * sgn, bodyRy - 0.04]; - const foot = [hip[0] + Math.sin(swing) * legLen, hip[1] + Math.cos(swing) * legLen + bob - (walk && Math.sin(ph + (k ? Math.PI : 0)) > 0 ? 0.03 : 0)]; - brush(g2, [hip, lp(hip, foot, 0.5), foot], iw * 1.3, { color: "#c8731f", alpha: 0.95, taperOut: 0.1, seed: seed + k }); + const foot2 = [hip[0] + Math.sin(swing) * legLen, hip[1] + Math.cos(swing) * legLen + bob - (walk && Math.sin(ph + (k ? Math.PI : 0)) > 0 ? 0.03 : 0)]; + brush(g2, [hip, lp(hip, foot2, 0.5), foot2], iw * 1.3, { color: "#c8731f", alpha: 0.95, taperOut: 0.1, seed: seed + k }); g2.fillStyle = rgba("#e08a2c", 0.9); g2.beginPath(); - g2.moveTo(foot[0] - 0.04, foot[1]); - g2.lineTo(foot[0] + 0.11, foot[1] + 0.012); - g2.lineTo(foot[0] + 0.02, foot[1] - 0.035); + g2.moveTo(foot2[0] - 0.04, foot2[1]); + g2.lineTo(foot2[0] + 0.11, foot2[1] + 0.012); + g2.lineTo(foot2[0] + 0.02, foot2[1] - 0.035); g2.closePath(); g2.fill(); } @@ -1756,10 +2166,10 @@ g2.arc(x, y, r * 0.18, 0, Math.PI * 2); g2.fill(); } - function leaf(g2, x, y, s, rot, color, alpha = 0.85) { + function leaf(g2, x, y, s, rot2, color, alpha = 0.85) { g2.save(); g2.translate(x, y); - g2.rotate(rot); + g2.rotate(rot2); g2.fillStyle = rgba(color, alpha); g2.beginPath(); g2.moveTo(-s, 0); @@ -1796,7 +2206,7 @@ g2.stroke(); } - // src/film/cues.ts + // src/films/duckling/cues.ts var CUE = { title: { drop: 0.32, write: 1.1, seal: 4.55 }, hatch: { first: 0.4, gap: 0.75 }, @@ -1812,7 +2222,7 @@ ending: { end: 8.5 } }; - // src/film/scenes.ts + // src/films/duckling/scenes.ts var UGLY = "#8d8a84"; var UGLY_BEAK = "#9c7f5c"; var DUCKY = "#ecc75a"; @@ -1942,1735 +2352,4911 @@ const [nx, ny] = nestPos(s); blot(g2, nx, ny + 30 * u, 380 * u, "#7a8a50", 0.18, 1.3, 0.35); } - var SCENES = { + var SCENES2 = { title: { label: "\u7247\u5934", draw(s) { - const { g: g2, W: W2, H: H2, u, t, portrait } = s; - const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.43; - if (t < 0.32) { - const y = lerp(-30 * u, cy, easeIn(t / 0.32)); - g2.fillStyle = rgba(INK, 0.95); - g2.beginPath(); - g2.ellipse(cx, y, 9 * u, 15 * u, 0, 0, Math.PI * 2); - g2.fill(); + const { g: g2, W: W2, H: H2, u, t, portrait } = s; + const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.43; + if (t < 0.32) { + const y = lerp(-30 * u, cy, easeIn(t / 0.32)); + g2.fillStyle = rgba(INK, 0.95); + g2.beginPath(); + g2.ellipse(cx, y, 9 * u, 15 * u, 0, 0, Math.PI * 2); + g2.fill(); + } + const p = easeOut(inv(0.3, 2.4, t)); + if (p > 0) { + const R = (portrait ? 420 : 350) * u * p; + wash(g2, blobLoop(cx, cy, R, 5, 0.05, 32), "#a39c90", 0.14 + 0.18 * (1 - p), { edge: 1.4 }); + blot(g2, cx, cy, R * 0.85, "#8a8276", 0.12 * p); + particles(10, 77, (r, i) => { + const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(0.3, 0.8, t)); + const rr = (3 + r() * 8) * u; + g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + g2.beginPath(); + g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, rr, 0, Math.PI * 2); + g2.fill(); + void i; + }); + } + const a = smooth(inv(0.8, 2.6, t)); + if (a > 0) { + g2.save(); + g2.globalAlpha = a; + const by = portrait ? H2 * 0.9 : H2 * 0.95; + lotusLeaf(g2, W2 * 0.1, by, 150 * u, 3); + lotusLeaf(g2, W2 * 0.24, by + 30 * u, 110 * u, 5, "#7fa070"); + for (let i = 0; i < 9; i++) reed(g2, W2 * (0.8 + i * 0.025), by + 20 * u, (260 + hash(i) * 200) * u * (portrait ? 1.2 : 1), t, 40 + i); + g2.restore(); + } + const fs = (portrait ? 230 : 210) * u; + writeText(g2, "\u4E11\u5C0F\u9E2D", cx, cy, fs, 1.1, 0.45, t, { vertical: portrait }); + const sa = smooth(inv(2.9, 3.6, t)); + if (sa > 0) { + g2.save(); + g2.globalAlpha = sa; + g2.font = `${46 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; + g2.fillText("\u5B89 \u5F92 \u751F \u7AE5 \u8BDD", cx, sy); + brush(g2, [[cx - 170 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 170 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); + g2.restore(); + } + const sp = inv(4.55, 4.85, t); + if (sp > 0) { + const sc = lerp(1.5, 1, easeOut(sp)); + const sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; + g2.save(); + g2.translate(sx, sy); + g2.scale(sc, sc); + g2.rotate(-0.04); + seal(g2, 0, 0, 92 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + const wy = portrait ? H2 * 0.74 : H2 * 0.86; + const wp = inv(2, 6.6, t); + if (wp > 0) { + const x0 = lerp(-120 * u, W2 * 0.62, wp); + bird(g2, { x: x0, y: wy, s: 70 * u, t, walk: 2.4, body: DUCKY, seed: 3 }); + bird(g2, { x: x0 - 120 * u, y: wy + 6 * u, s: 92 * u, t: t + 0.2, walk: 2, body: UGLY, beak: UGLY_BEAK, fluff: 1, sw: 0.1, seed: 9, neckUp: -0.2 }); + } + }, + enterAt: [0.5, 0.45] + }, + summer: { + label: "\u590F\u65E5\u4E61\u6751", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X, portrait } = s; + sky(g2, W2, gy, "#9fbccb", 0.2, "#ffffff", 0); + sun(g2, X(portrait ? -0.55 : -0.7), H2 * (portrait ? 0.13 : 0.17), 58 * u); + mountains(g2, W2, gy - 110 * u, 150 * u, "#7d8a8c", 0.32, 3); + mountains(g2, W2, gy - 40 * u, 110 * u, "#56645c", 0.42, 7); + band(g2, W2, gy - 70 * u, gy + 40 * u, "#d9b45a", 0.42, 21, 14 * u); + particles(260, 5, (r) => { + const x = r() * W2, y = gy - 60 * u + r() * 90 * u, h = (14 + r() * 26) * u; + brush(g2, [[x, y + h], [x + 2 * u, y + h * 0.5], [x + 4 * u, y]], 2.4 * u, { color: r() < 0.5 ? "#b88a2e" : "#d8a940", alpha: 0.55, seed: Math.floor(x), taperIn: 0.05 }); + }); + band(g2, W2, gy + 35 * u, H2, "#9fb07a", 0.3, 31); + const px = X(-0.15), py = gy + 190 * u, rx = Math.min(W2 * 0.45, 720 * u), ry = 120 * u; + wash(g2, blobLoop(px, py, 1, 4, 0.04, 26).map(([x, y]) => [px + (x - px) * rx, py + (y - py) * ry]), "#94adb5", 0.4, { edge: 1.5 }); + strokesH(g2, W2 * 0.9, py - ry * 0.6, py + ry * 0.7, 18, "#4f6670", 0.35, 41, u); + lotusLeaf(g2, px - rx * 0.6, py + 30 * u, 70 * u, 1); + lotusLeaf(g2, px - rx * 0.35, py + 70 * u, 55 * u, 2); + lotusLeaf(g2, px + rx * 0.1, py + 10 * u, 60 * u, 3); + blossom(g2, px - rx * 0.48, py - 30 * u, 22 * u, 4, "#e7a0b0"); + tree(g2, X(portrait ? -0.95 : -0.88), gy + 40 * u, 560 * u, 17, { + color: "#3a3128", + depth: 4, + leaves: (x, y, l) => { + blot(g2, x, y, l * 1.4, "#5f8a4c", 0.32); + blot(g2, x + l * 0.4, y - l * 0.3, l * 0.9, "#86a85e", 0.3); + } + }); + }, + draw(s) { + const { g: g2, W: W2, u, t, gy, X } = s; + const k = ks(s); + cloud(g2, X(-0.2) + t * 14 * u, gy - 430 * u, 420 * u, 2, 0.3); + cloud(g2, X(0.5) + t * 9 * u, gy - 520 * u, 300 * u, 5, 0.25); + const nx = X(0.55), ny = gy + 95 * u; + for (let i = 0; i < 12; i++) reed(g2, nx - 220 * u + i * 42 * u + hash(i) * 20 * u, ny - 10 * u + hash(i + 3) * 30 * u, (260 + hash(i * 7) * 180) * u, t, 70 + i); + nest(g2, nx, ny + 10 * u, 270 * k, 3); + egg(g2, nx + 95 * k, ny + 4 * u, 30 * k, { seed: 2 }); + bird(g2, { x: nx, y: ny - 8 * u, s: 230 * k, t, sw: 0.34, body: "#f7f2e6", swim: true, face: -1, seed: 5, neckUp: 0.15 + 0.15 * Math.sin(t * 0.8), look: 0.15 * Math.sin(t * 0.5), fluff: 0 }); + egg(g2, nx - 100 * k, ny + 12 * u, 28 * k, { seed: 4 }); + for (let i = 0; i < 5; i++) reed(g2, nx - 160 * u + i * 85 * u, ny + 60 * u, (150 + hash(i * 11) * 90) * u, t, 90 + i, { alpha: 0.85 }); + const dx = X(0.05) + 280 * u * Math.sin(t * 0.55), dy = gy - 140 * u + 60 * u * Math.sin(t * 1.3); + g2.save(); + g2.translate(dx, dy); + g2.rotate(0.15 * Math.sin(t)); + brush(g2, [[-28 * u, 0], [0, 0], [26 * u, 1 * u]], 6 * u, { color: "#b8322a", alpha: 0.9, taperIn: 0.05, taperOut: 0.9 }); + g2.fillStyle = rgba("#8aa0a8", 0.35); + for (const sgn of [-1, 1]) for (const off of [0, 9 * u]) { + g2.beginPath(); + g2.ellipse(2 * u - off, sgn * (13 * u + 3 * u * Math.sin(t * 40)), 7 * u, 16 * u, sgn * 0.3, 0, Math.PI * 2); + g2.fill(); + } + g2.restore(); + for (let b = 0; b < 2; b++) { + const bx = X(-0.5 + b * 0.35) + 120 * u * Math.sin(t * 0.7 + b * 2), by = gy - 60 * u + 50 * u * Math.sin(t * 1.1 + b); + const f = Math.abs(Math.sin(t * 9 + b)); + g2.fillStyle = rgba(b ? "#e0a040" : "#f1f0ea", 0.85); + for (const sgn of [-1, 1]) { + g2.beginPath(); + g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.fill(); + } + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = 1.5 * u; + for (const sgn of [-1, 1]) { + g2.beginPath(); + g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.stroke(); + } + } + void W2; + }, + cam(s) { + const p = easeInOut(inv(0, s.D, s.t)); + return cam(s, lerp(1, 1.4, p), lerp(s.W / 2, s.X(0.52), p), lerp(s.H / 2, s.gy + 20 * s.u, p)); + }, + enterAt: [0.5, 0.4] + }, + hatch: { + label: "\u7834\u58F3", + bg: bgNestSide, + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks(s); + const [nx, ny] = nestPos(s); + bird(g2, { x: X(-0.6), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: -0.25 + 0.1 * Math.sin(t * 0.7), fluff: 0 }); + nest(g2, nx, ny + 10 * u, 470 * k, 8); + const eggs = [[-150, -6], [-70, 8], [110, 10], [180, -6]]; + egg(g2, nx + 20 * k, ny - 2 * u, 62 * k, { color: "#e6e3d6", seed: 99, wobble: t > s.D - 2.5 ? 0.04 * Math.sin(t * 13) : 0 }); + const targets = [[X(-0.38), ny + 150 * u], [X(-0.2), ny + 175 * u], [X(0.32), ny + 165 * u], [X(0.5), ny + 140 * u]]; + eggs.forEach(([ex, ey], i) => { + const x = nx + ex * k, y = ny + ey * u; + const tc = 0.4 + i * 0.75; + const crack = inv(tc, tc + 0.35, t); + const open = easeOut(inv(tc + 0.55, tc + 1.05, t)); + egg(g2, x, y, 40 * k, { crack, open, seed: 10 + i, wobble: crack > 0 && open === 0 ? 0.08 * Math.sin(t * 30) : 0 }); + if (open > 0) { + const em = spring(inv(tc + 0.55, tc + 1.3, t)); + const hop = inv(tc + 2, tc + 3, t); + const [tx, ty] = targets[i]; + const bx = lerp(x, tx, easeInOut(hop)), by = lerp(y + 10 * u, ty, easeInOut(hop)) - Math.sin(Math.PI * hop) * 70 * u; + const peep = (t - tc) % 2.3; + bird(g2, { + x: bx, + y: by + (1 - em) * 40 * u, + s: 88 * k * lerp(0.6, 1, em), + t, + body: DUCKY, + seed: 20 + i, + face: i < 2 ? -1 : 1, + walk: hop > 0 && hop < 1 ? 3 : 0, + mouth: peep > 1 && peep < 1.25 ? 1 : 0, + squash: (1 - em) * 0.2, + neckUp: 0.15, + legs: hop > 0 + }); + if (hop >= 1 && peep > 1 && peep < 1.5) soundMarks(g2, bx + (i < 2 ? -1 : 1) * 40 * u, by - 95 * u, 40 * u, 1 - (peep - 1) / 0.5, i, i < 2 ? -1 : 1); + } + }); + }, + cam(s) { + const [nx, ny] = nestPos(s); + return cam(s, lerp(1.3, 1.42, easeInOut(s.t / s.D)), nx - 20 * s.u, ny + 30 * s.u); + }, + enterAt: [0.55, 0.65] + }, + bigegg: { + label: "\u6700\u5927\u7684\u86CB", + bg: bgNestSide, + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks(s); + const [nx, ny] = nestPos(s); + const C = CUE.bigegg; + const surprise = smooth(inv(C.surprise, C.surprise + 0.3, t)) * (1 - smooth(inv(C.surprise + 2.2, C.surprise + 3.2, t))); + bird(g2, { x: X(-0.62), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: lerp(-0.2, 0.75, surprise), fluff: 0 }); + nest(g2, nx, ny + 10 * u, 470 * k, 8); + [[-150, -6], [-70, 8], [110, 10], [180, -6]].forEach(([ex2, ey2], i) => egg(g2, nx + ex2 * k, ny + ey2 * u, 40 * k, { open: 1, seed: 10 + i })); + const amp = 0.12 * smooth(inv(0.3, C.crackFrom, t)); + const wob = Math.sin(t * 14) * amp; + const ex = nx + 20 * k, ey = ny - 2 * u; + const crack = inv(C.crackFrom, C.open - 0.05, t), open = easeOut(inv(C.open, C.open + 0.5, t)); + egg(g2, ex, ey - (open ? 0 : Math.abs(Math.sin(t * 7)) * 8 * u * amp * 6), 62 * k, { color: "#e6e3d6", crack, open, seed: 99, wobble: open ? 0 : wob }); + const back = inv(C.walk, C.walk + 2.6, t); + if (open > 0) { + const em = spring(inv(C.open + 0.1, C.open + 1, t)); + const ux = lerp(ex, X(-0.02), easeInOut(back)), uy = lerp(ey + 8 * u, ny + 175 * u, easeInOut(back)) - Math.sin(Math.PI * clamp(back * 3)) * 40 * u; + bird(g2, { + x: ux, + y: uy + (1 - em) * 60 * u, + s: 132 * k * lerp(0.7, 1, em), + t, + sw: 0.12, + body: UGLY, + beak: UGLY_BEAK, + fluff: 1.1, + seed: 7, + face: -1, + squash: (1 - em) * 0.25, + legs: back > 0, + walk: back > 0 && back < 1 ? 1.6 : 0, + tilt: t > C.question && t < C.walk ? 0.18 * Math.sin((t - C.question) * 2) : 0, + neckUp: t > C.question && t < C.walk ? 0.2 : 0 + }); + question(g2, ux + 10 * u, uy - 230 * u * k, 90 * u, smooth(inv(C.question, C.question + 0.3, t)) * (1 - smooth(inv(C.walk - 0.4, C.walk - 0.1, t)))); + } + for (let i = 0; i < 4; i++) { + const recoil = surprise * 0.3 + easeInOut(back) * 0.5; + const x = X(-0.42) + i * 70 * u - recoil * 60 * u; + const y = ny + 150 * u + i % 2 * 22 * u; + bird(g2, { x, y, s: 86 * k, t: t + i * 0.3, body: DUCKY, seed: 30 + i, face: 1, neckUp: 0.25 - surprise * 0.2, tilt: -0.2 * surprise, walk: back > 0 && back < 1 ? 1.2 : 0 }); + exclaim(g2, x + 10 * u, y - 150 * u, 46 * u, smooth(inv(C.surprise, C.surprise + 0.15, t)) * (1 - smooth(inv(C.surprise + 1.4, C.surprise + 1.8, t))), i); + } + }, + cam(s) { + const [nx, ny] = nestPos(s); + const zin = easeInOut(inv(0, CUE.bigegg.open + 0.6, s.t)), zout = easeInOut(inv(CUE.bigegg.walk - 0.4, CUE.bigegg.walk + 2.4, s.t)); + return cam(s, lerp(lerp(1.35, 1.9, zin), 1.35, zout), lerp(nx + 20 * s.u, s.X(-0.18), zout), ny + lerp(-30, 70, zout) * s.u); + }, + enterAt: [0.55, 0.62] + }, + bullied: { + label: "\u88AB\u5632\u7B11", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#c9c2b0", 0.18, "#ffffff", 0); + const rx = X(0.78), ry = gy - 360 * u; + wash(g2, [[rx - 260 * u, ry + 40 * u], [rx + 400 * u, ry - 40 * u], [rx + 420 * u, ry + 70 * u], [rx - 240 * u, ry + 120 * u]], "#4a4a4f", 0.6); + for (let i = 0; i < 12; i++) brush(g2, [[rx - 240 * u + i * 55 * u, ry + 115 * u - i * 6 * u], [rx - 250 * u + i * 55 * u, ry + 40 * u - i * 6 * u]], 4 * u, { alpha: 0.5, seed: i }); + wash(g2, [[rx - 200 * u, ry + 115 * u], [rx + 400 * u, ry + 65 * u], [rx + 400 * u, gy - 10 * u], [rx - 200 * u, gy - 10 * u]], "#b3a48a", 0.4); + const hx = X(-0.78), hy = gy - 10 * u; + wash(g2, [[hx - 150 * u, hy], [hx - 110 * u, hy - 150 * u], [hx, hy - 210 * u], [hx + 110 * u, hy - 150 * u], [hx + 150 * u, hy]], "#d6b060", 0.55); + particles(60, 12, (r) => { + const a = r() * Math.PI - Math.PI, rr = r() * 140 * u; + const x = hx + Math.cos(a) * rr, y = hy - 20 * u + Math.sin(a) * rr; + brush(g2, [[x, y], [x + 18 * u * (r() - 0.5), y - 25 * u]], 2 * u, { color: "#8a6a2a", alpha: 0.6 }); + }); + band(g2, W2, gy - 20 * u, H2, "#c9b48a", 0.32, 51); + fence(g2, -20 * u, W2 + 20 * u, gy + 10 * u, 160 * u, 5); + strokesH(g2, W2, gy + 20 * u, H2, 34, "#8a7450", 0.3, 13, u); + }, + draw(s) { + const { g: g2, u, t, gy, X } = s; + const k = ks(s); + const C = CUE.bullied; + const laugh = t > C.laugh1[0] && t < C.laugh1[1] || t > C.laugh2[0] && t < C.laugh2[1]; + const ga = inv(C.girlIn, C.girlIn + 0.9, t); + if (ga > 0) child(g2, lerp(X(-1.15), X(-0.78), easeOut(ga)), gy + 200 * u, 360 * k, t, { face: 1, pose: t > C.shoo ? "shoo" : "basket", dress: "#b8483e", seed: 4 }); + for (let i = 0; i < 3; i++) { + const x = X(-0.52) + i * 85 * u, y = gy + 150 * u + i % 2 * 20 * u; + const b = laugh ? Math.abs(Math.sin(t * 9 + i)) : 0; + bird(g2, { x, y, s: 92 * k, t: t + i, body: DUCKY, seed: 40 + i, face: 1, mouth: laugh ? 0.4 + 0.6 * b : 0, squash: 0.06 * b, tilt: laugh ? -0.15 : 0, neckUp: laugh ? 0.3 : 0 }); + if (laugh) soundMarks(g2, x + 50 * u, y - 120 * u, 36 * u, 0.8, i); } - const p = easeOut(inv(0.3, 2.4, t)); - if (p > 0) { - const R = (portrait ? 420 : 350) * u * p; - wash(g2, blobLoop(cx, cy, R, 5, 0.05, 32), "#a39c90", 0.14 + 0.18 * (1 - p), { edge: 1.4 }); - blot(g2, cx, cy, R * 0.85, "#8a8276", 0.12 * p); - particles(10, 77, (r, i) => { - const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(0.3, 0.8, t)); - const rr = (3 + r() * 8) * u; - g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + const ra = inv(C.roosterIn, C.roosterIn + 0.6, t); + const pecks = C.pecks; + let peck = 0, pushed = 0; + for (const p of pecks) { + const q2 = inv(p - 0.12, p + 0.12, t); + peck = Math.max(peck, Math.sin(Math.PI * q2)); + if (t > p) pushed += 1; + } + const ux = X(0.1) - pushed * 30 * u; + const cower = t > C.shoo ? 0.2 : 0.08; + bird(g2, { x: ux, y: gy + 170 * u, s: 140 * k, t, sw: 0.16, body: UGLY, beak: UGLY_BEAK, fluff: 1.1, seed: 7, face: t > C.girlIn ? -1 : 1, neckUp: -0.45, shiver: t > C.pecks[0] ? 1 : 0.4, squash: cower }); + tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(1.2, 1.5, t)) * (1 - smooth(inv(3.4, 3.7, t)))); + tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(7.6, 7.9, t)) * (1 - smooth(inv(9.6, 10, t)))); + if (ra > 0) { + const rx = lerp(X(1.3), X(0.42), easeOut(ra)) - (t > C.pecks[3] + 0.3 ? (t - C.pecks[3] - 0.3) * 20 * u : 0); + rooster(g2, rx, gy + 175 * u, 240 * k, t, { face: -1, peck, flap: t > C.roosterIn + 0.6 && t < C.pecks[3] + 0.5 ? t * 18 : 0 }); + } + pecks.forEach((p, j) => { + const q2 = t - p; + if (q2 < 0 || q2 > 2) return; + for (let m = 0; m < 3; m++) { + const x = ux + 30 * u + (m - 1) * 30 * u + Math.sin(q2 * 4 + m) * 20 * u; + const y = gy + 40 * u - 80 * u * easeOut(q2 * 2) + q2 * q2 * 40 * u; + leaf(g2, x, y, 10 * u, q2 * 3 + m + j, "#9a968e", 0.8 * (1 - q2 / 2)); + } + }); + }, + cam(s) { + return cam(s, lerp(1.28, 1.36, easeInOut(s.t / s.D)), s.X(-0.08), s.gy + 110 * s.u); + }, + enterAt: [0.3, 0.6] + }, + leave: { + label: "\u79BB\u5BB6", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#b9a48a", 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 70 * u, 140 * u, "#8a8478", 0.3, 23); + mountains(g2, W2, gy - 20 * u, 90 * u, "#6a665e", 0.35, 29); + band(g2, W2, gy - 20 * u, H2, "#b8ad8a", 0.28, 61); + const path = [[X(-0.75), H2 + 20 * u], [X(-0.42), gy + 170 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 30 * u], [X(0.68), gy - 25 * u]]; + const pts = spline(path, 10); + for (let i = 0; i < pts.length - 1; i++) { + const f = i / pts.length; + brush(g2, [pts[i], pts[i + 1]], lerp(150, 16, f) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + } + tree(g2, X(0.8), gy - 10 * u, 380 * u, 33, { depth: 4, leaves: (x, y, l) => blot(g2, x, y, l * 0.8, "#8a7a50", 0.25) }); + fence(g2, -20 * u, X(-0.12), gy + 120 * u, 170 * u, 8, X(-0.42), 120 * u); + strokesH(g2, W2, gy + 30 * u, H2, 30, "#7a6a48", 0.28, 15, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X, D: D2 } = s; + const p = Math.pow(inv(CUE.leave.walk, D2 + 1.2, t), 1.15); + const path = spline([[X(-0.62), gy + 200 * u], [X(-0.42), gy + 175 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 32 * u], [X(0.62), gy - 20 * u]], 12); + const idx = Math.min(path.length - 1, Math.floor(p * (path.length - 1))); + const [bx, by] = path[idx]; + const sc = lerp(150, 52, p) * ks(s); + bird(g2, { x: bx, y: by, s: sc, t, sw: 0.18, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: 1, walk: t > CUE.leave.walk ? 1.7 : 0, neckUp: -0.35, shiver: t < CUE.leave.walk ? 0.4 : 0 }); + tear(g2, bx + 45 * u * (sc / 150), by - 95 * u * (sc / 150) + t * 25 % 25 * u, 9 * u, smooth(inv(0.3, 0.6, t)) * (1 - smooth(inv(2.2, 2.6, t)))); + for (let i = 0; i < 14; i++) grass(g2, i / 14 * W2 + hash(i) * 50 * u, H2 - hash(i * 3) * 120 * u, 70 * u, t, 200 + i, "#5d6a44", 0.6, 2); + fallingThings(g2, W2, H2, t, 14, 8, u, (x, y, rot2, r) => leaf(g2, x, y * 0.8, (8 + r() * 6) * u, rot2, r() < 0.5 ? "#b08a4a" : "#8a7a50", 0.75), 50); + }, + cam(s) { + return cam(s, lerp(1.08, 1.16, s.t / s.D), s.W / 2, s.gy + 60 * s.u); + }, + enterAt: [0.25, 0.7] + }, + marsh: { + label: "\u6CBC\u6CFD", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy } = s; + sky(g2, W2, gy, "#7e878a", 0.25, "#ffffff", 0.02); + mountains(g2, W2, gy - 50 * u, 100 * u, "#6e7672", 0.3, 37, { soft: 1 }); + for (let i = 0; i < 46; i++) reed(g2, i / 46 * W2 + hash(i) * 30 * u, gy - 10 * u, (90 + hash(i * 3) * 90) * u, 0, 500 + i, { color: "#5d6658", alpha: 0.4, wind: 0.2 }); + band(g2, W2, gy - 15 * u, H2, "#8fa3a6", 0.32, 71, 6 * u); + particles(8, 19, (r) => blot(g2, r() * W2, gy + r() * (H2 - gy), (80 + r() * 160) * u, "#5d6a62", 0.12, 2.2, 0.4)); + strokesH(g2, W2, gy + 20 * u, H2, 36, "#3f4c52", 0.35, 17, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const M = CUE.marsh; + water(g2, 0, W2, gy + 10 * u, H2 - gy, t, 3, { alpha: 0.3, lines: 12 }); + for (let i = 0; i < 12; i++) reed(g2, X(0.62) + i * 38 * u, gy + 40 * u + hash(i) * 30 * u, (280 + hash(i * 5) * 160) * u, t, 120 + i, { wind: t > M.shots[0] ? 1 + 2 * Math.exp(-(t - M.shots[0]) * 2) : 1 }); + for (const [ts, fx, fy] of [[M.shots[0], 0.86, -130], [M.shots[1], 0.72, -170]]) { + const q2 = t - ts; + if (q2 > 0 && q2 < 0.09) { + g2.fillStyle = rgba("#e0a040", 0.9); g2.beginPath(); - g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, rr, 0, Math.PI * 2); + g2.arc(X(fx), gy + fy * u, 26 * u, 0, Math.PI * 2); g2.fill(); - void i; - }); + } + if (q2 > 0) smoke(g2, X(fx) - q2 * 30 * u, gy + fy * u - q2 * 25 * u, (30 + 110 * easeOut(q2 / 2)) * u, 0.6 * Math.max(0, 1 - q2 / 4), Math.floor(ts * 10)); } - const a = smooth(inv(0.8, 2.6, t)); - if (a > 0) { - g2.save(); - g2.globalAlpha = a; - const by = portrait ? H2 * 0.9 : H2 * 0.95; - lotusLeaf(g2, W2 * 0.1, by, 150 * u, 3); - lotusLeaf(g2, W2 * 0.24, by + 30 * u, 110 * u, 5, "#7fa070"); - for (let i = 0; i < 9; i++) reed(g2, W2 * (0.8 + i * 0.025), by + 20 * u, (260 + hash(i) * 200) * u * (portrait ? 1.2 : 1), t, 40 + i); - g2.restore(); + for (let i = 0; i < 3; i++) { + const takeoff = easeIn(inv(M.scatter + i * 0.12, M.scatter + 2.1 + i * 0.12, t)); + const bx = X(0.2 + i * 0.16) + takeoff * 380 * u, by = gy + (110 + i * 25) * u - takeoff * (gy + 300 * u); + const laugh = t > M.laugh[0] && t < M.laugh[1]; + const o = { x: bx, y: by, s: 140 * k, t: t + i, sw: 0.32, body: "#9b7b55", head: "#3d6b4a", beak: "#d8b03a", seed: 60 + i, face: takeoff > 0 ? 1 : -1, mouth: laugh ? 0.5 + 0.5 * Math.abs(Math.sin(t * 9 + i)) : 0, fluff: 0 }; + if (takeoff > 0) bird(g2, { ...o, legs: false, wing: 1, flap: t * 16 + i, neckUp: -0.7 }); + else { + swimBird(g2, o); + if (laugh) soundMarks(g2, bx - 70 * u, by - 150 * u, 34 * u, 0.8, i, -1); + } + } + particles(7, 33, (r, i) => { + const q2 = t - M.scatter + 0.1 - r() * 0.4; + if (q2 <= 0) return; + farBird(g2, X(0.8) - q2 * (120 + r() * 120) * u, gy - 120 * u - q2 * (150 + r() * 120) * u, (18 + r() * 12) * u, t * 14 + i); + }); + const hide = easeInOut(inv(M.hide, M.hide + 1.4, t)); + const startle = Math.exp(-Math.max(0, t - M.shots[0]) * 5) * (t > M.shots[0] ? 1 : 0); + const ux = lerp(X(-0.08), X(-0.55), hide), uy = gy + 175 * u; + swimBird(g2, { x: ux, y: uy, s: 150 * k, t, sw: 0.24, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: hide > 0 && hide < 1 ? -1 : 1, neckUp: lerp(0, -0.6, hide), tuck: 0.3 * hide, shiver: t > M.shots[0] ? 1 : 0, squash: startle * 0.2 }); + exclaim(g2, ux + 20 * u, uy - 220 * u, 50 * u, startle > 0.05 ? 1 : 0, 3); + for (let i = 0; i < 9; i++) reed(g2, X(-0.78) + i * 40 * u + hash(i * 9) * 20 * u, H2 + 20 * u, (420 + hash(i * 13) * 220) * u, t, 150 + i, { color: "#2f3b2c", alpha: 0.85, wind: t > M.shots[0] ? 1 + 1.5 * Math.exp(-(t - M.shots[0])) : 1 }); + }, + cam(s) { + const { t, u } = s; + let dx = 0; + for (const ts of CUE.marsh.shots) { + const q2 = t - ts; + if (q2 > 0) dx += Math.sin(q2 * 90) * 10 * u * Math.exp(-q2 * 9); + } + const p = easeInOut(inv(9.2, s.D, t)); + return cam(s, lerp(1.15, 1.55, p), lerp(s.X(0.1), s.X(-0.52), p), s.gy + lerp(90, 120, p) * u, { dx, dy: dx * 0.5 }); + }, + enterAt: [0.5, 0.6] + }, + autumn: { + label: "\u79CB\u5929\u7684\u5927\u9E1F", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#e3a868", 0.12, "#e8b878", 0.38); + sun(g2, X(0.3), gy - 150 * u, 72 * u, "#c8463a", 0.85); + mountains(g2, W2, gy - 40 * u, 170 * u, "#8a7a6a", 0.32, 41); + mountains(g2, W2, gy, 100 * u, "#5e5248", 0.45, 47); + band(g2, W2, gy, gy + 190 * u, "#b8a990", 0.32, 81, 4 * u); + particles(9, 3, (r) => { + const y = gy + 20 * u + r() * 120 * u, w = (80 - (y - gy) / u * 0.3) * u; + brush(g2, [[X(0.3) - w, y], [X(0.3), y + 2 * u], [X(0.3) + w, y]], 5 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); + }); + band(g2, W2, gy + 185 * u, H2, "#b8925a", 0.32, 83); + strokesH(g2, W2, gy + 200 * u, H2, 30, "#7a5a30", 0.3, 19, u); + tree(g2, X(-0.88), gy + 260 * u, 640 * u, 9, { + depth: 5, + color: "#3a2a20", + leaves: (x, y, l) => { + blot(g2, x, y, l * 1.3, "#c0582e", 0.35); + blot(g2, x + l * 0.5, y - l * 0.2, l * 0.9, "#e0a040", 0.3); + } + }); + for (let i = 0; i < 10; i++) reed(g2, X(0.7) + i * 30 * u, gy + 200 * u, (180 + hash(i) * 120) * u, 0, 600 + i, { color: "#8a6a3a", alpha: 0.7 }); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X, portrait } = s; + const k = ks(s); + fallingThings(g2, W2, H2, t, 22, 12, u, (x, y, rot2, r) => leaf(g2, x * 0.55, y, (9 + r() * 7) * u, rot2, ["#c0582e", "#d98a3a", "#b8402a", "#e0a040"][Math.floor(r() * 4)], 0.85), 55); + const sp = (W2 + 1400 * u) / 11.5; + let lead = 0; + for (let i = 0; i < 5; i++) { + const x = W2 + 160 * u + i * 170 * u - (t - CUE.autumn.swans) * sp; + const y = H2 * (portrait ? 0.2 : 0.17) + i * (portrait ? 70 : 52) * u + 10 * u * Math.sin(t * 1.3 + i); + if (i === 0) lead = x; + if (x < -300 * u || x > W2 + 300 * u) continue; + bird(g2, { x, y, s: 105 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 4.2 + i * 0.9, face: -1, neckUp: -0.85, seed: 80 + i }); + } + const lookUp = smooth(inv(CUE.autumn.lookUp, CUE.autumn.lookUp + 1, t)); + const turn = lead < X(0.42); + const walkP = inv(CUE.autumn.walk, CUE.autumn.walk + 2.2, t); + const ux = X(0.42) - easeInOut(walkP) * 110 * u; + bird(g2, { x: ux, y: gy + 230 * u, s: 168 * k, t, sw: 0.42, body: UGLY, beak: UGLY_BEAK, fluff: 0.7, seed: 7, face: turn ? -1 : 1, neckUp: lookUp * 0.95, walk: walkP > 0 && walkP < 1 ? 1.4 : 0 }); + }, + cam(s) { + return cam(s, lerp(1.04, 1.12, easeInOut(s.t / s.D)), lerp(s.X(0.15), s.X(0.05), easeInOut(s.t / s.D)), s.H * 0.48); + }, + enterAt: [0.8, 0.3] + }, + winter: { + label: "\u5BD2\u51AC", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#5f6a74", 0.35, "#c9d0d4", 0.1); + mountains(g2, W2, gy - 30 * u, 160 * u, "#5a6268", 0.3, 53); + particles(10, 5, (r) => blot(g2, r() * W2, gy - 120 * u - r() * 60 * u, (50 + r() * 60) * u, "#ffffff", 0.45, 2, 0.5)); + band(g2, W2, gy - 10 * u, gy + 300 * u, "#c9d4d8", 0.5, 91, 5 * u); + particles(10, 77, (r, i) => { + const x = r() * W2, y = gy + 20 * u + r() * 250 * u; + brush(g2, [[x, y], [x + (60 + r() * 120) * u, y + (r() - 0.5) * 20 * u]], 1.6 * u, { color: "#6a7a86", alpha: 0.35, seed: i }); + }); + band(g2, W2, gy + 290 * u, H2, "#ffffff", 0.6, 93, 14 * u); + for (const [fx, h, sd] of [[-0.86, 520, 3], [0.88, 460, 7]]) { + tree(g2, X(fx), gy + 40 * u, h * u, sd, { depth: 5, color: "#2a2622", leaves: (x, y, l) => blot(g2, x, y - 3 * u, l * 0.35, "#ffffff", 0.85, 1.6, 0.6) }); } - const fs = (portrait ? 230 : 210) * u; - writeText(g2, "\u4E11\u5C0F\u9E2D", cx, cy, fs, 1.1, 0.45, t, { vertical: portrait }); - const sa = smooth(inv(2.9, 3.6, t)); - if (sa > 0) { - g2.save(); - g2.globalAlpha = sa; - g2.font = `${46 * u}px ${FONT_KAI}`; - g2.fillStyle = rgba(INK, 0.85); - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; - g2.fillText("\u5B89 \u5F92 \u751F \u7AE5 \u8BDD", cx, sy); - brush(g2, [[cx - 170 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 170 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); - g2.restore(); + for (let i = 0; i < 12; i++) reed(g2, X(-0.5) + i * 26 * u, gy + 290 * u, (140 + hash(i) * 90) * u, 0, 700 + i, { color: "#6a6458", alpha: 0.65, wind: 0.6 }); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const night = 0.34 * smooth(inv(4, 6.5, t)) - 0.24 * smooth(inv(8.2, 9, t)) + 0.24 * smooth(inv(9.6, 10.6, t)) - 0.3 * smooth(inv(11.8, 13.2, t)); + if (night > 0) { + g2.fillStyle = rgba("#26303c", night); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + blot(g2, X(0.6), H2 * 0.16, 70 * u, "#fffbe8", night * 2.2); + g2.fillStyle = rgba("#fffbe8", night * 2.4); + g2.beginPath(); + g2.arc(X(0.6), H2 * 0.16, 30 * u, 0, Math.PI * 2); + g2.fill(); } - const sp = inv(4.55, 4.85, t); - if (sp > 0) { - const sc = lerp(1.5, 1, easeOut(sp)); - const sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; - g2.save(); - g2.translate(sx, sy); - g2.scale(sc, sc); - g2.rotate(-0.04); - seal(g2, 0, 0, 92 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); - g2.restore(); + const ux = X(-0.04), uy = gy + 150 * u; + for (let i = 0; i < 6; i++) { + const a = i / 6 * Math.PI * 2 + 0.4, L = (150 + hash(i) * 160) * u; + const pts = [[ux + Math.cos(a) * 60 * u, uy + 10 * u + Math.sin(a) * 15 * u]]; + for (let j = 1; j < 5; j++) pts.push([pts[0][0] + Math.cos(a + nz(j, i) * 0.4) * L * j / 4, pts[0][1] + Math.sin(a + nz(j, i) * 0.4) * L * j / 4 * 0.25]); + brush(g2, pts, 2.2 * u, { color: "#3e4c58", alpha: 0.75, grow: easeOut(inv(CUE.winter.ice + i * 0.05, CUE.winter.ice + 0.7 + i * 0.05, t)), taperIn: 0.05, seed: i }); } - const wy = portrait ? H2 * 0.74 : H2 * 0.86; - const wp = inv(2, 6.6, t); - if (wp > 0) { - const x0 = lerp(-120 * u, W2 * 0.62, wp); - bird(g2, { x: x0, y: wy, s: 70 * u, t, walk: 2.4, body: DUCKY, seed: 3 }); - bird(g2, { x: x0 - 120 * u, y: wy + 6 * u, s: 92 * u, t: t + 0.2, walk: 2, body: UGLY, beak: UGLY_BEAK, fluff: 1, sw: 0.1, seed: 9, neckUp: -0.2 }); + bird(g2, { x: ux, y: uy, s: 175 * k, t, sw: 0.5, body: UGLY, beak: UGLY_BEAK, fluff: 0.8, seed: 7, face: 1, swim: true, tuck: 1, shiver: 1, snow: smooth(inv(0, s.D, t)), noBlink: true }); + for (let i = 0; i < 4; i++) { + const q2 = (t * 0.45 + i * 0.27) % 1; + const y = H2 * (0.2 + 0.18 * i); + brush(g2, [[lerp(-300, W2 + 300, q2) * 1, y], [lerp(-300, W2 + 300, q2) + 180 * u, y + 6 * u], [lerp(-300, W2 + 300, q2) + 360 * u, y]], 3 * u, { color: "#d8dde0", alpha: 0.5, dry: 0.9, seed: i }); } + fallingThings(g2, W2, H2, t, 170, 31, u, (x, y, _r, r) => { + const rr = (2 + r() * 5) * u; + g2.fillStyle = rgba("#ffffff", 0.92); + g2.beginPath(); + g2.arc(x, y, rr, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba("#8a96a0", 0.35); + g2.lineWidth = 0.8 * u; + g2.stroke(); + }, 95); }, - enterAt: [0.5, 0.45] + cam(s) { + return cam(s, lerp(1.2, 1.5, easeInOut(s.t / s.D)), s.X(-0.04), s.gy + 70 * s.u); + }, + enterAt: [0.5, 0.2] }, - summer: { - label: "\u590F\u65E5\u4E61\u6751", + spring: { + label: "\u6625\u56DE", bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X, portrait } = s; - sky(g2, W2, gy, "#9fbccb", 0.2, "#ffffff", 0); - sun(g2, X(portrait ? -0.55 : -0.7), H2 * (portrait ? 0.13 : 0.17), 58 * u); - mountains(g2, W2, gy - 110 * u, 150 * u, "#7d8a8c", 0.32, 3); - mountains(g2, W2, gy - 40 * u, 110 * u, "#56645c", 0.42, 7); - band(g2, W2, gy - 70 * u, gy + 40 * u, "#d9b45a", 0.42, 21, 14 * u); - particles(260, 5, (r) => { - const x = r() * W2, y = gy - 60 * u + r() * 90 * u, h = (14 + r() * 26) * u; - brush(g2, [[x, y + h], [x + 2 * u, y + h * 0.5], [x + 4 * u, y]], 2.4 * u, { color: r() < 0.5 ? "#b88a2e" : "#d8a940", alpha: 0.55, seed: Math.floor(x), taperIn: 0.05 }); + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#bcd3c8", 0.2, "#ffffff", 0); + sun(g2, X(0.5), H2 * 0.15, 50 * u, "#d65a40", 0.7); + mountains(g2, W2, gy - 40 * u, 150 * u, "#86a87a", 0.35, 61); + mountains(g2, W2, gy, 90 * u, "#5e8a50", 0.4, 67); + band(g2, W2, gy - 5 * u, H2, "#a9c48a", 0.35, 101); + const pts = spline([[X(-1.1), gy + 120 * u], [X(-0.3), gy + 60 * u], [X(0.3), gy + 160 * u], [X(1.1), gy + 90 * u]], 12); + brush(g2, pts, 70 * u, { color: "#9fc0c8", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.2 }); + brush(g2, pts.map(([x, y]) => [x, y - 10 * u]), 6 * u, { color: "#5e7a86", alpha: 0.3, dry: 0.7, taperIn: 0, taperOut: 0 }); + strokesH(g2, W2, gy + 200 * u, H2, 26, "#5e8a50", 0.3, 23, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const C = CUE.spring; + willow(g2, X(-0.9), gy + 220 * u, 620 * u, t, 3, "#8fb86b", 0.15 + 0.85 * smooth(inv(C.buds, C.buds + 1.4, t))); + const bloom = smooth(inv(C.bloom, C.bloom + 1.3, t)); + tree(g2, X(0.86), gy + 220 * u, 520 * u, 21, { depth: 4, color: "#3a2c26", t, leaves: (x, y, l) => { + if (bloom > 0) { + blossom(g2, x, y, l * 0.28 * bloom, Math.floor(x), "#ec9aa8", 0.75); + blossom(g2, x + l * 0.3, y - l * 0.2, l * 0.2 * bloom, Math.floor(y), "#f4b8c0", 0.7); + } else blot(g2, x, y, l * 0.12, "#b86a74", 0.5); + } }); + for (let i = 0; i < 2; i++) farBird(g2, X(-0.2 + i * 0.4) + Math.sin(t * 0.5 + i) * 100 * u, H2 * 0.25 + i * 40 * u, 20 * u, t * 9 + i, "#3a3a3a"); + const spread = easeOut(inv(C.spread, C.spread + 0.9, t)); + const lift = easeIn(inv(C.lift, C.lift + 4.2, t)); + const fx = X(-0.12) + lift * W2 * 0.55, fy = gy + 170 * u - lift * (gy + 260 * u); + const flapRate = t < C.flapHard ? 2.6 : t < C.lift ? lerp(3, 6, inv(C.flapHard, C.lift, t)) : 4.6; + bird(g2, { + x: fx, + y: fy, + s: 205 * k, + t, + sw: 0.8, + body: "#b5b1a9", + beak: "#b88a5c", + fluff: 0.2, + seed: 7, + face: 1, + wing: spread, + flap: t * flapRate * 1 + (t > 3.4 ? Math.sin(t) : 0), + legs: lift < 0.05, + neckUp: lift > 0 ? -0.6 * smooth(lift * 4) : lerp(0, 0.3, spread), + look: t < C.spread ? 0.2 * Math.sin(t * 1.5) : 0 }); - band(g2, W2, gy + 35 * u, H2, "#9fb07a", 0.3, 31); - const px = X(-0.15), py = gy + 190 * u, rx = Math.min(W2 * 0.45, 720 * u), ry = 120 * u; - wash(g2, blobLoop(px, py, 1, 4, 0.04, 26).map(([x, y]) => [px + (x - px) * rx, py + (y - py) * ry]), "#94adb5", 0.4, { edge: 1.5 }); - strokesH(g2, W2 * 0.9, py - ry * 0.6, py + ry * 0.7, 18, "#4f6670", 0.35, 41, u); - lotusLeaf(g2, px - rx * 0.6, py + 30 * u, 70 * u, 1); - lotusLeaf(g2, px - rx * 0.35, py + 70 * u, 55 * u, 2); - lotusLeaf(g2, px + rx * 0.1, py + 10 * u, 60 * u, 3); - blossom(g2, px - rx * 0.48, py - 30 * u, 22 * u, 4, "#e7a0b0"); - tree(g2, X(portrait ? -0.95 : -0.88), gy + 40 * u, 560 * u, 17, { - color: "#3a3128", - depth: 4, - leaves: (x, y, l) => { - blot(g2, x, y, l * 1.4, "#5f8a4c", 0.32); - blot(g2, x + l * 0.4, y - l * 0.3, l * 0.9, "#86a85e", 0.3); - } + question(g2, fx + 30 * u, fy - 330 * u * k, 80 * u, smooth(inv(C.question, C.question + 0.25, t)) * (1 - smooth(inv(C.flapHard + 0.2, C.flapHard + 0.5, t)))); + if (t > C.lift && t < C.lift + 1.2) particles(8, 99, (r, i) => { + const q2 = t - C.lift; + leaf(g2, X(-0.12) + (r() - 0.5) * 200 * u, gy + 180 * u - q2 * 80 * u * r(), 8 * u, q2 * 4 + i, "#9fbf7a", 0.8 * (1 - q2 / 1.2)); }); + fallingThings(g2, W2, H2, t, 26, 41, u, (x, y, rot2, r) => leaf(g2, x, y, (6 + r() * 5) * u, rot2, r() < 0.5 ? "#f0a8b4" : "#f6c8cf", 0.85), 45); + }, + cam(s) { + const lift = easeInOut(inv(CUE.spring.lift - 0.3, CUE.spring.lift + 3.5, s.t)); + return cam(s, lerp(1.3, 1.1, lift), lerp(s.X(-0.1), s.W / 2, lift), lerp(s.gy + 60 * s.u, s.H * 0.3, lift)); + }, + enterAt: [0.5, 0.5] + }, + reflection: { + label: "\u5012\u5F71", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#c9d8d8", 0.18, "#ffffff", 0); + mountains(g2, W2, gy - 60 * u, 120 * u, "#8fa69a", 0.28, 71, { soft: 1 }); + for (let i = 0; i < 7; i++) { + const x = X(-1 + i * 0.33) + hash(i) * 60 * u; + willow(g2, x, gy - 20 * u, (200 + hash(i * 3) * 80) * u, 0, 40 + i, "#8fb86b", 1); + } + band(g2, W2, gy - 25 * u, H2, "#a9bcc0", 0.32, 111, 3 * u); + strokesH(g2, W2, gy, H2, 30, "#5a6e78", 0.28, 29, u); + lotusLeaf(g2, X(-0.9), H2 - 90 * u, 120 * u, 7); + lotusLeaf(g2, X(-0.72), H2 - 30 * u, 90 * u, 8); + lotusLeaf(g2, X(0.92), H2 - 60 * u, 110 * u, 9); }, draw(s) { - const { g: g2, W: W2, u, t, gy, X } = s; + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; const k = ks(s); - cloud(g2, X(-0.2) + t * 14 * u, gy - 430 * u, 420 * u, 2, 0.3); - cloud(g2, X(0.5) + t * 9 * u, gy - 520 * u, 300 * u, 5, 0.25); - const nx = X(0.55), ny = gy + 95 * u; - for (let i = 0; i < 12; i++) reed(g2, nx - 220 * u + i * 42 * u + hash(i) * 20 * u, ny - 10 * u + hash(i + 3) * 30 * u, (260 + hash(i * 7) * 180) * u, t, 70 + i); - nest(g2, nx, ny + 10 * u, 270 * k, 3); - egg(g2, nx + 95 * k, ny + 4 * u, 30 * k, { seed: 2 }); - bird(g2, { x: nx, y: ny - 8 * u, s: 230 * k, t, sw: 0.34, body: "#f7f2e6", swim: true, face: -1, seed: 5, neckUp: 0.15 + 0.15 * Math.sin(t * 0.8), look: 0.15 * Math.sin(t * 0.5), fluff: 0 }); - egg(g2, nx - 100 * k, ny + 12 * u, 28 * k, { seed: 4 }); - for (let i = 0; i < 5; i++) reed(g2, nx - 160 * u + i * 85 * u, ny + 60 * u, (150 + hash(i * 11) * 90) * u, t, 90 + i, { alpha: 0.85 }); - const dx = X(0.05) + 280 * u * Math.sin(t * 0.55), dy = gy - 140 * u + 60 * u * Math.sin(t * 1.3); - g2.save(); - g2.translate(dx, dy); - g2.rotate(0.15 * Math.sin(t)); - brush(g2, [[-28 * u, 0], [0, 0], [26 * u, 1 * u]], 6 * u, { color: "#b8322a", alpha: 0.9, taperIn: 0.05, taperOut: 0.9 }); - g2.fillStyle = rgba("#8aa0a8", 0.35); - for (const sgn of [-1, 1]) for (const off of [0, 9 * u]) { - g2.beginPath(); - g2.ellipse(2 * u - off, sgn * (13 * u + 3 * u * Math.sin(t * 40)), 7 * u, 16 * u, sgn * 0.3, 0, Math.PI * 2); - g2.fill(); + water(g2, 0, W2, gy, H2 - gy, t, 7, { alpha: 0.22, lines: 10, speed: 3 }); + const px = X(-0.16), py = gy + 175 * u, ps = 200 * k; + const R = CUE.reflection; + const reveal = smooth(inv(R.reveal, R.reveal + 1.6, t)); + const bow = smooth(inv(R.bow, R.bow + 1, t)) * (1 - smooth(inv(R.proud - 0.4, R.proud + 0.8, t))); + const proud = smooth(inv(R.proud, R.proud + 1.2, t)); + const land = inv(0, R.land, t); + const body = mixHex("#aaa69e", WHITE, reveal); + const o = { x: px, y: py, s: ps, t, sw: 1, body, beak: mixHex("#b88a5c", SWAN_BEAK, reveal), seed: 7, face: 1, neckUp: -0.95 * bow + 0.25 * proud, glow: reveal * (1 - smooth(inv(R.reveal + 2, R.reveal + 4, t))), noBlink: bow > 0.5 }; + const refl = (c2) => bird(c2, { ...o, body: WHITE, beak: SWAN_BEAK, swim: true, glow: 0 }); + if (land >= 1) { + const mirror = smooth(inv(R.mirror, R.mirror + 1.2, t)); + reflection(g2, refl, { x: px - ps * 1.3, y: py - ps * 1.4, w: ps * 2.8, h: ps * 1.46 }, py + ps * 0.06, t, lerp(0.3, 0.95, mirror), "#7a90a0"); + if (mirror > 0 && mirror < 1) blot(g2, px + 60 * u, py + 140 * u, 220 * u, "#fff4d8", 0.35 * Math.sin(Math.PI * mirror)); + if (reveal > 0 && reveal < 1) blot(g2, px + 30 * u, py - 60 * u, 320 * u, "#fff4d8", 0.55 * Math.sin(Math.PI * reveal)); + for (let r = 0; r < 3; r++) { + const q2 = inv(R.reveal + r * 0.5, R.reveal + 2.4 + r * 0.5, t); + if (q2 > 0 && q2 < 1) ripplesRing(g2, px + 70 * u, py + 60 * u, 60 * u + q2 * 420 * u, 0.4 * (1 - q2), "#9a7a3a"); + } } - g2.restore(); - for (let b = 0; b < 2; b++) { - const bx = X(-0.5 + b * 0.35) + 120 * u * Math.sin(t * 0.7 + b * 2), by = gy - 60 * u + 50 * u * Math.sin(t * 1.1 + b); - const f = Math.abs(Math.sin(t * 9 + b)); - g2.fillStyle = rgba(b ? "#e0a040" : "#f1f0ea", 0.85); - for (const sgn of [-1, 1]) { + for (let i = 0; i < 3; i++) { + const q2 = easeOut(inv(1.2 + i * 0.4, 5.2 + i * 0.4, t)); + const sx = lerp(X(1.3 + i * 0.2), X(0.3 + i * 0.24), q2), sy = gy + (110 + i * 45) * u; + const greet = smooth(inv(R.proud + 0.4 + i * 0.2, R.proud + 1.2 + i * 0.2, t)); + swimBird(g2, swanBird(sx, sy, (150 - i * 12) * k, t + i, { face: -1, seed: 90 + i, neckUp: -0.55 * greet + 0.08 * Math.sin(t + i) })); + } + if (land < 1) { + const lx = lerp(X(-0.65), px, easeOut(land)), ly = lerp(H2 * 0.12, py, easeInOut(land)); + bird(g2, { ...o, x: lx, y: ly, legs: false, wing: 1 - land * 0.3, flap: t * 9, swim: true, neckUp: -0.5 }); + } else { + const settle = inv(R.land, R.land + 0.85, t); + swimBird(g2, { ...o, swim: true, wing: (1 - easeOut(settle)) * 0.7, flap: t * 6 }); + const q2 = t - R.land; + if (q2 < 1) particles(14, 5, (r) => { + const a = -Math.PI * r(), v = (100 + r() * 180) * u; + const x = px + Math.cos(a) * v * q2 * 1.6, y = py + Math.sin(a) * v * q2 * 1.4 + 300 * u * q2 * q2; + g2.fillStyle = rgba("#ffffff", 0.9 * (1 - q2)); g2.beginPath(); - g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.arc(x, y, (3 + r() * 5) * u, 0, Math.PI * 2); g2.fill(); - } - g2.strokeStyle = rgba(INK, 0.6); - g2.lineWidth = 1.5 * u; - for (const sgn of [-1, 1]) { - g2.beginPath(); - g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.strokeStyle = rgba("#6a7a86", 0.5 * (1 - q2)); + g2.lineWidth = u; g2.stroke(); - } + }); } - void W2; + particles(10, 61, (r, i) => { + const x = (r() * W2 + t * 14 * u) % W2, y = gy + 40 * u + r() * (H2 - gy - 80 * u); + leaf(g2, x, y, 6 * u, t * 0.3 + i, "#f0a8b4", 0.75); + }); }, cam(s) { - const p = easeInOut(inv(0, s.D, s.t)); - return cam(s, lerp(1, 1.4, p), lerp(s.W / 2, s.X(0.52), p), lerp(s.H / 2, s.gy + 20 * s.u, p)); + const { t, u, gy } = s; + const zin = easeInOut(inv(CUE.reflection.bow - 0.2, CUE.reflection.mirror + 1.2, t)), zout = easeInOut(inv(CUE.reflection.reveal - 0.6, CUE.reflection.reveal + 2.4, t)); + return cam(s, lerp(lerp(1.25, 1.65, zin), 1.3, zout), lerp(s.X(-0.05), s.X(-0.1), zin), gy + lerp(lerp(60, 230, zin), 110, zout) * u); }, - enterAt: [0.5, 0.4] + enterAt: [0.3, 0.3] }, - hatch: { - label: "\u7834\u58F3", - bg: bgNestSide, + accepted: { + label: "\u63A5\u7EB3", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#b8d0d8", 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 50 * u, 110 * u, "#88a294", 0.28, 81, { soft: 1 }); + band(g2, W2, gy - 20 * u, H2, "#a3bfc6", 0.34, 121, 3 * u); + strokesH(g2, W2, gy, H2, 30, "#4f6670", 0.28, 33, u); + const bank = [[-50 * u, gy - 60 * u], [X(-0.55), gy - 20 * u], [X(-0.38), gy + 120 * u], [X(-0.5), gy + 260 * u], [-50 * u, gy + 300 * u]]; + wash(g2, bank, "#9fb878", 0.6); + for (let i = 0; i < 10; i++) grass(g2, X(-0.95) + i * 50 * u, gy + 100 * u + hash(i) * 120 * u, 50 * u, 0, 300 + i, "#4f6a3e", 0.6, 0.3); + }, draw(s) { - const { g: g2, u, t, X } = s; + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; const k = ks(s); - const [nx, ny] = nestPos(s); - bird(g2, { x: X(-0.6), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: -0.25 + 0.1 * Math.sin(t * 0.7), fluff: 0 }); - nest(g2, nx, ny + 10 * u, 470 * k, 8); - const eggs = [[-150, -6], [-70, 8], [110, 10], [180, -6]]; - egg(g2, nx + 20 * k, ny - 2 * u, 62 * k, { color: "#e6e3d6", seed: 99, wobble: t > s.D - 2.5 ? 0.04 * Math.sin(t * 13) : 0 }); - const targets = [[X(-0.38), ny + 150 * u], [X(-0.2), ny + 175 * u], [X(0.32), ny + 165 * u], [X(0.5), ny + 140 * u]]; - eggs.forEach(([ex, ey], i) => { - const x = nx + ex * k, y = ny + ey * u; - const tc = 0.4 + i * 0.75; - const crack = inv(tc, tc + 0.35, t); - const open = easeOut(inv(tc + 0.55, tc + 1.05, t)); - egg(g2, x, y, 40 * k, { crack, open, seed: 10 + i, wobble: crack > 0 && open === 0 ? 0.08 * Math.sin(t * 30) : 0 }); - if (open > 0) { - const em = spring(inv(tc + 0.55, tc + 1.3, t)); - const hop = inv(tc + 2, tc + 3, t); - const [tx, ty] = targets[i]; - const bx = lerp(x, tx, easeInOut(hop)), by = lerp(y + 10 * u, ty, easeInOut(hop)) - Math.sin(Math.PI * hop) * 70 * u; - const peep = (t - tc) % 2.3; - bird(g2, { - x: bx, - y: by + (1 - em) * 40 * u, - s: 88 * k * lerp(0.6, 1, em), - t, - body: DUCKY, - seed: 20 + i, - face: i < 2 ? -1 : 1, - walk: hop > 0 && hop < 1 ? 3 : 0, - mouth: peep > 1 && peep < 1.25 ? 1 : 0, - squash: (1 - em) * 0.2, - neckUp: 0.15, - legs: hop > 0 - }); - if (hop >= 1 && peep > 1 && peep < 1.5) soundMarks(g2, bx + (i < 2 ? -1 : 1) * 40 * u, by - 95 * u, 40 * u, 1 - (peep - 1) / 0.5, i, i < 2 ? -1 : 1); - } + cloud(g2, X(0.3) + t * 10 * u, H2 * 0.14, 380 * u, 9, 0.22); + willow(g2, X(-0.88), gy + 60 * u, 560 * u, t, 8, "#8fb86b", 1); + const cheer = t > CUE.accepted.cheer; + [[-0.82, "#c0504a", true], [-0.66, "#4a6a9a", false], [-0.52, "#d0a040", true]].forEach(([fx, col, girl], i) => { + child(g2, X(fx), gy + 110 * u + i * 12 * u, 270 * k, t + i, { face: 1, dress: col, girl, pose: cheer ? i === 1 ? "shoo" : "clap" : "stand", seed: 10 + i }); + if (cheer) soundMarks(g2, X(fx) + 50 * u, gy - 200 * u * k, 40 * u, 0.8 * (1 - smooth(inv(10, 11, t))), i); + }); + const hx = X(0.62), hy = gy + 70 * u; + const heart = smooth(inv(CUE.accepted.heart, CUE.accepted.heart + 2, t)); + swimBird(g2, swanBird(hx - 70 * u, hy, 115 * k, t, { face: 1, seed: 95, neckUp: -0.55 * heart })); + swimBird(g2, swanBird(hx + 70 * u, hy, 115 * k, t + 1, { face: -1, seed: 96, neckUp: -0.55 * heart })); + const px = X(0.12), py = gy + 210 * u; + const nuzzle = (ph) => Math.max(0, Math.sin(t * 1.6 + ph)) * smooth(inv(0.5, 1.5, t)); + swimBird(g2, swanBird(px - 190 * u, py - 30 * u, 165 * k, t + 2, { face: 1, seed: 91, neckUp: -0.7 * nuzzle(0) })); + swimBird(g2, swanBird(px, py, 200 * k, t, { face: 1, seed: 7, neckUp: 0.2 + 0.1 * Math.sin(t), glow: 0.2 })); + swimBird(g2, swanBird(px + 220 * u, py - 20 * u, 165 * k, t + 3, { face: -1, seed: 92, neckUp: -0.7 * nuzzle(2) })); + particles(12, 71, (r, i) => { + const x = (r() * W2 + t * 18 * u) % W2, y = gy + 30 * u + r() * (H2 - gy - 60 * u); + leaf(g2, x, y, 6 * u, t * 0.4 + i, "#f0a8b4", 0.75); }); }, cam(s) { - const [nx, ny] = nestPos(s); - return cam(s, lerp(1.3, 1.42, easeInOut(s.t / s.D)), nx - 20 * s.u, ny + 30 * s.u); + return cam(s, lerp(1.32, 1.18, easeInOut(s.t / s.D)), s.X(-0.12), s.gy + 110 * s.u); }, - enterAt: [0.55, 0.65] + enterAt: [0.2, 0.5] }, - bigegg: { - label: "\u6700\u5927\u7684\u86CB", - bg: bgNestSide, + fly: { + label: "\u9AD8\u98DE", + bg(s) { + const { g: g2, W: W2, H: H2, u, X, portrait } = s; + sky(g2, W2, H2, "#a8c4d4", 0.32, "#f2ead8", 0); + sun(g2, X(0.62), H2 * (portrait ? 0.14 : 0.18), 60 * u, "#d65a40", 0.75); + mountains(g2, W2, H2 * 0.92, 300 * u, "#7d8f8a", 0.35, 91); + mountains(g2, W2, H2 * 1.02, 260 * u, "#4f5d58", 0.5, 97); + }, draw(s) { - const { g: g2, u, t, X } = s; + const { g: g2, W: W2, H: H2, u, t, X, portrait } = s; const k = ks(s); - const [nx, ny] = nestPos(s); - const C = CUE.bigegg; - const surprise = smooth(inv(C.surprise, C.surprise + 0.3, t)) * (1 - smooth(inv(C.surprise + 2.2, C.surprise + 3.2, t))); - bird(g2, { x: X(-0.62), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: lerp(-0.2, 0.75, surprise), fluff: 0 }); - nest(g2, nx, ny + 10 * u, 470 * k, 8); - [[-150, -6], [-70, 8], [110, 10], [180, -6]].forEach(([ex2, ey2], i) => egg(g2, nx + ex2 * k, ny + ey2 * u, 40 * k, { open: 1, seed: 10 + i })); - const amp = 0.12 * smooth(inv(0.3, C.crackFrom, t)); - const wob = Math.sin(t * 14) * amp; - const ex = nx + 20 * k, ey = ny - 2 * u; - const crack = inv(C.crackFrom, C.open - 0.05, t), open = easeOut(inv(C.open, C.open + 0.5, t)); - egg(g2, ex, ey - (open ? 0 : Math.abs(Math.sin(t * 7)) * 8 * u * amp * 6), 62 * k, { color: "#e6e3d6", crack, open, seed: 99, wobble: open ? 0 : wob }); - const back = inv(C.walk, C.walk + 2.6, t); - if (open > 0) { - const em = spring(inv(C.open + 0.1, C.open + 1, t)); - const ux = lerp(ex, X(-0.02), easeInOut(back)), uy = lerp(ey + 8 * u, ny + 175 * u, easeInOut(back)) - Math.sin(Math.PI * clamp(back * 3)) * 40 * u; - bird(g2, { - x: ux, - y: uy + (1 - em) * 60 * u, - s: 132 * k * lerp(0.7, 1, em), - t, - sw: 0.12, - body: UGLY, - beak: UGLY_BEAK, - fluff: 1.1, - seed: 7, - face: -1, - squash: (1 - em) * 0.25, - legs: back > 0, - walk: back > 0 && back < 1 ? 1.6 : 0, - tilt: t > C.question && t < C.walk ? 0.18 * Math.sin((t - C.question) * 2) : 0, - neckUp: t > C.question && t < C.walk ? 0.2 : 0 - }); - question(g2, ux + 10 * u, uy - 230 * u * k, 90 * u, smooth(inv(C.question, C.question + 0.3, t)) * (1 - smooth(inv(C.walk - 0.4, C.walk - 0.1, t)))); - } - for (let i = 0; i < 4; i++) { - const recoil = surprise * 0.3 + easeInOut(back) * 0.5; - const x = X(-0.42) + i * 70 * u - recoil * 60 * u; - const y = ny + 150 * u + i % 2 * 22 * u; - bird(g2, { x, y, s: 86 * k, t: t + i * 0.3, body: DUCKY, seed: 30 + i, face: 1, neckUp: 0.25 - surprise * 0.2, tilt: -0.2 * surprise, walk: back > 0 && back < 1 ? 1.2 : 0 }); - exclaim(g2, x + 10 * u, y - 150 * u, 46 * u, smooth(inv(C.surprise, C.surprise + 0.15, t)) * (1 - smooth(inv(C.surprise + 1.4, C.surprise + 1.8, t))), i); - } + particles(7, 21, (r, i) => { + const y = H2 * (0.15 + r() * 0.6), w = (260 + r() * 360) * u, sp2 = (90 + r() * 160) * u; + const x = ((r() * (W2 + w * 2) - t * sp2) % (W2 + w * 2) + W2 + w * 2) % (W2 + w * 2) - w; + cloud(g2, x, y, w, 30 + i, 0.25 + r() * 0.15); + }); + const enter = easeOut(inv(0, 2.2, t)); + const cx = lerp(-W2 * 0.4, X(portrait ? 0.05 : 0.1), enter) + Math.sin(t * 0.9) * 20 * u; + const cy = H2 * (portrait ? 0.42 : 0.44) + Math.sin(t * 1.2) * 18 * u; + const sp = portrait ? 0.62 : 1; + const flock2 = [[-250, 110, 0.78], [-470, 210, 0.66], [-230, -120, 0.74], [-440, -210, 0.6]]; + flock2.forEach(([dx, dy, sc], i) => bird(g2, { x: cx + dx * u * sp, y: cy + dy * u * sp, s: 230 * k * sc, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.3 + i * 0.8, neckUp: -0.85, seed: 80 + i })); + bird(g2, { x: cx, y: cy, s: 240 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.2, neckUp: -0.85, seed: 7, glow: 0.3 }); }, cam(s) { - const [nx, ny] = nestPos(s); - const zin = easeInOut(inv(0, CUE.bigegg.open + 0.6, s.t)), zout = easeInOut(inv(CUE.bigegg.walk - 0.4, CUE.bigegg.walk + 2.4, s.t)); - return cam(s, lerp(lerp(1.35, 1.9, zin), 1.35, zout), lerp(nx + 20 * s.u, s.X(-0.18), zout), ny + lerp(-30, 70, zout) * s.u); + return { z: lerp(1, 1.08, s.t / s.D), x: s.W / 2, y: s.H / 2 }; }, - enterAt: [0.55, 0.62] + enterAt: [0.1, 0.5] }, - bullied: { - label: "\u88AB\u5632\u7B11", + ending: { + label: "\u5BC4\u8BED", bg(s) { const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#c9c2b0", 0.18, "#ffffff", 0); - const rx = X(0.78), ry = gy - 360 * u; - wash(g2, [[rx - 260 * u, ry + 40 * u], [rx + 400 * u, ry - 40 * u], [rx + 420 * u, ry + 70 * u], [rx - 240 * u, ry + 120 * u]], "#4a4a4f", 0.6); - for (let i = 0; i < 12; i++) brush(g2, [[rx - 240 * u + i * 55 * u, ry + 115 * u - i * 6 * u], [rx - 250 * u + i * 55 * u, ry + 40 * u - i * 6 * u]], 4 * u, { alpha: 0.5, seed: i }); - wash(g2, [[rx - 200 * u, ry + 115 * u], [rx + 400 * u, ry + 65 * u], [rx + 400 * u, gy - 10 * u], [rx - 200 * u, gy - 10 * u]], "#b3a48a", 0.4); - const hx = X(-0.78), hy = gy - 10 * u; - wash(g2, [[hx - 150 * u, hy], [hx - 110 * u, hy - 150 * u], [hx, hy - 210 * u], [hx + 110 * u, hy - 150 * u], [hx + 150 * u, hy]], "#d6b060", 0.55); - particles(60, 12, (r) => { - const a = r() * Math.PI - Math.PI, rr = r() * 140 * u; - const x = hx + Math.cos(a) * rr, y = hy - 20 * u + Math.sin(a) * rr; - brush(g2, [[x, y], [x + 18 * u * (r() - 0.5), y - 25 * u]], 2 * u, { color: "#8a6a2a", alpha: 0.6 }); + sky(g2, W2, gy, "#e8a070", 0.3, "#f0c090", 0.35); + sun(g2, X(0.05), gy - 130 * u, 115 * u, "#c8463a", 0.9); + mountains(g2, W2, gy - 20 * u, 200 * u, "#6a5048", 0.42, 101); + mountains(g2, W2, gy + 10 * u, 120 * u, "#3a2c28", 0.6, 107); + band(g2, W2, gy + 5 * u, H2, "#d8a878", 0.3, 131, 3 * u); + particles(14, 9, (r) => { + const y = gy + 20 * u + r() * 200 * u, w = (130 - (y - gy) / u * 0.4) * u; + brush(g2, [[X(0.05) - w, y], [X(0.05), y + 2 * u], [X(0.05) + w, y]], 6 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); }); - band(g2, W2, gy - 20 * u, H2, "#c9b48a", 0.32, 51); - fence(g2, -20 * u, W2 + 20 * u, gy + 10 * u, 160 * u, 5); - strokesH(g2, W2, gy + 20 * u, H2, 34, "#8a7450", 0.3, 13, u); + for (let i = 0; i < 8; i++) reed(g2, X(-0.98) + i * 34 * u, H2 + 10 * u, (300 + hash(i) * 200) * u, 0, 800 + i, { color: "#2a2220", alpha: 0.85 }); + for (let i = 0; i < 6; i++) reed(g2, X(0.86) + i * 30 * u, H2 + 10 * u, (240 + hash(i + 9) * 160) * u, 0, 820 + i, { color: "#2a2220", alpha: 0.85 }); }, draw(s) { - const { g: g2, u, t, gy, X } = s; - const k = ks(s); - const C = CUE.bullied; - const laugh = t > C.laugh1[0] && t < C.laugh1[1] || t > C.laugh2[0] && t < C.laugh2[1]; - const ga = inv(C.girlIn, C.girlIn + 0.9, t); - if (ga > 0) child(g2, lerp(X(-1.15), X(-0.78), easeOut(ga)), gy + 200 * u, 360 * k, t, { face: 1, pose: t > C.shoo ? "shoo" : "basket", dress: "#b8483e", seed: 4 }); - for (let i = 0; i < 3; i++) { - const x = X(-0.52) + i * 85 * u, y = gy + 150 * u + i % 2 * 20 * u; - const b = laugh ? Math.abs(Math.sin(t * 9 + i)) : 0; - bird(g2, { x, y, s: 92 * k, t: t + i, body: DUCKY, seed: 40 + i, face: 1, mouth: laugh ? 0.4 + 0.6 * b : 0, squash: 0.06 * b, tilt: laugh ? -0.15 : 0, neckUp: laugh ? 0.3 : 0 }); - if (laugh) soundMarks(g2, x + 50 * u, y - 120 * u, 36 * u, 0.8, i); + const { g: g2, H: H2, u, t, gy, X, portrait } = s; + for (let i = 0; i < 5; i++) { + const q2 = inv(0, 10, t); + const x0 = X(-0.75 + i * 0.07), y0 = H2 * (0.3 + 0.03 * i); + const tx = X(0.05) + i * 12 * u, ty = gy - 160 * u - i * 8 * u; + const x = lerp(x0, tx, q2 * 0.85), y = lerp(y0, ty, q2 * 0.85); + farBird(g2, x, y, lerp(28, 9, q2) * u, t * 7 + i * 1.3, "#2a2220", 0.9); + } + const fs = (portrait ? 150 : 130) * u; + const vx = portrait ? X(0.62) : X(0.72), vy = portrait ? H2 * 0.3 : H2 * 0.36; + writeText(g2, "\u76F8\u4FE1\u81EA\u5DF1", vx, vy, fs, 1.4, 0.55, t, { vertical: true, gap: 1.02 }); + const E = CUE.ending; + const ea = smooth(inv(E.end - 0.3, E.end + 0.3, t)); + if (ea > 0) { + g2.save(); + g2.globalAlpha = ea; + g2.font = `${40 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.save(); + g2.translate(vx - fs * 0.95, vy - fs * 1.45); + [..."\u4E11\u5C0F\u9E2D \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); + g2.restore(); + g2.restore(); + } + const sp = inv(E.end + 0.1, E.end + 0.35, t); + if (sp > 0) { + g2.save(); + g2.translate(vx - fs * 0.95, vy + fs * 1.2); + g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); + seal(g2, 0, 0, 80 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + }, + cam(s) { + return { z: lerp(1, 1.05, s.t / s.D), x: s.W / 2, y: s.gy }; + }, + enterAt: [0.5, 0.4] + } + }; + + // src/film/audio.ts + var SR = 44100; + var midi = (m) => 440 * Math.pow(2, (m - 69) / 12); + var N = { E2: 40, G2: 43, A2: 45, B2: 47, D3: 50, E3: 52, G3: 55, A3: 57, B3: 59, D4: 62, E4: 64, G4: 67, A4: 69, B4: 71, D5: 74, E5: 76, G5: 79, A5: 81, B5: 83, D6: 86, E6: 88 }; + var sceneStart = (id) => TL.scenes.find((s) => s.id === id)?.start ?? 0; + var sceneDur = (id) => TL.scenes.find((s) => s.id === id)?.dur ?? 0; + var Mixer = class { + ctx; + music; + sfx; + voice; + rev; + revIn; + master; + noise; + ksCache = /* @__PURE__ */ new Map(); + r = rng(4242); + q = []; + /** 人声轨(供「山谷回声」等效果使用;在 score/effects 之前由 buildSoundtrack 设置) */ + voiceBuf = null; + /** 在 t 时刻真正创建节点(离线渲染按需建节点) */ + at(t, fn) { + this.q.push({ t, fn }); + } + /** 把人声 [t0, t0+dur) 这一段当回声再放几遍:越来越远、越来越暗 */ + echo(t0, dur, delay = 0.42, gain = 0.3, taps = 3) { + this.at(t0 + delay, () => { + if (!this.voiceBuf) return; + for (let k = 1; k <= taps; k++) { + const s = this.ctx.createBufferSource(); + s.buffer = this.voiceBuf; + const f = this.ctx.createBiquadFilter(); + f.type = "lowpass"; + f.frequency.value = 2800 / k; + const g2 = this.ctx.createGain(); + g2.gain.value = gain * Math.pow(0.5, k - 1); + s.connect(f); + f.connect(g2); + this.out(g2, this.sfx, k % 2 ? -0.45 : 0.45, 0.6); + s.start(t0 + delay * k + 1 / 30, t0, dur); + } + }); + } + constructor(ctx) { + this.ctx = ctx; + const self = this; + for (const k of ["flute", "pad", "bell", "block", "noiseHit", "tone", "wind", "quack", "swanCall"]) { + const orig = self[k].bind(this); + self[k] = (...a) => { + this.q.push({ t: a[0], fn: () => orig(...a) }); + }; + } + this.master = ctx.createGain(); + this.master.gain.value = 0.9; + const comp = ctx.createDynamicsCompressor(); + comp.threshold.value = -10; + comp.knee.value = 6; + comp.ratio.value = 6; + comp.attack.value = 4e-3; + comp.release.value = 0.2; + this.master.connect(comp); + comp.connect(ctx.destination); + this.music = ctx.createGain(); + this.music.connect(this.master); + this.sfx = ctx.createGain(); + this.sfx.gain.value = 0.9; + this.sfx.connect(this.master); + this.voice = ctx.createGain(); + this.voice.gain.value = 1; + this.voice.connect(this.master); + this.rev = ctx.createConvolver(); + this.rev.buffer = this.impulse(2.3, 2.4); + this.revIn = ctx.createGain(); + this.revIn.gain.value = 0.55; + this.revIn.connect(this.rev); + this.rev.connect(this.master); + this.noise = ctx.createBuffer(1, SR * 2, SR); + const d = this.noise.getChannelData(0), nr = rng(99); + for (let i = 0; i < d.length; i++) d[i] = nr() * 2 - 1; + } + impulse(sec, decay) { + const b = this.ctx.createBuffer(2, Math.floor(SR * sec), SR), r = rng(7); + for (let c2 = 0; c2 < 2; c2++) { + const d = b.getChannelData(c2); + for (let i = 0; i < d.length; i++) d[i] = (r() * 2 - 1) * Math.pow(1 - i / d.length, decay) * (i < 400 ? i / 400 : 1); + } + return b; + } + out(node, bus, pan = 0, send = 0.25) { + const p = this.ctx.createStereoPanner(); + p.pan.value = pan; + node.connect(p); + p.connect(bus); + if (send > 0) { + const s = this.ctx.createGain(); + s.gain.value = send; + p.connect(s); + s.connect(this.revIn); + } + } + // ---------- 乐器 ---------- + ks(freq) { + const key = Math.round(freq * 10); + let b = this.ksCache.get(key); + if (b) return b; + const len = Math.floor(SR * 3), per = Math.max(2, Math.round(SR / freq)); + b = this.ctx.createBuffer(1, len, SR); + const d = b.getChannelData(0), r = rng(key); + const buf = new Float32Array(per); + for (let i = 0; i < per; i++) buf[i] = (r() * 2 - 1) * (1 - i / per * 0.5); + let idx = 0, last = 0; + const damp = freq > 600 ? 0.994 : 0.997; + for (let i = 0; i < len; i++) { + const cur = buf[idx]; + const nxt = buf[(idx + 1) % per]; + const v = damp * 0.5 * (cur + nxt); + buf[idx] = v; + d[i] = cur * 0.8 + last * 0.2; + last = cur; + idx = (idx + 1) % per; + } + this.ksCache.set(key, b); + return b; + } + // 拨弦直接在 JS 里叠加到采样缓冲(比每个音符建节点快一个数量级) + pl = []; + pluck(t, m, vel = 0.5, pan = 0, o = {}) { + if (t < 0 || t > TOTAL + 2) return; + if (!this.pl.length) { + const n2 = this.ctx.length; + this.pl = [new Float32Array(n2), new Float32Array(n2)]; + } + const src = this.ks(midi(m)).getChannelData(0); + const dur = o.dur ?? 2.6; + const i0 = Math.floor(t * SR), n = Math.min(src.length - 2, Math.floor((dur + 0.6) * SR)); + const gl = vel * Math.cos((pan + 1) * Math.PI / 4), gr = vel * Math.sin((pan + 1) * Math.PI / 4); + const relStart = dur * 0.7 * SR, tau = dur * 0.15 * SR; + const a = Math.exp(-2 * Math.PI * (2400 + (o.bright ?? 0.5) * 4e3) / SR); + const L = this.pl[0], R = this.pl[1]; + let pos = 0, lp3 = 0; + const bendTo = o.bend ? Math.pow(2, o.bend / 12) : 1; + const b0 = 0.12 * SR, b1 = 0.32 * SR; + for (let k = 0; k < n; k++) { + const idx = i0 + k; + if (idx >= L.length) break; + const rate = o.bend ? k < b0 ? 1 : k > b1 ? bendTo : 1 + (bendTo - 1) * (k - b0) / (b1 - b0) : 1; + const p = Math.floor(pos), f = pos - p; + if (p + 1 >= src.length) break; + const x = src[p] * (1 - f) + src[p + 1] * f; + pos += rate; + lp3 = x + a * (lp3 - x); + const env = k < relStart ? 1 : Math.exp(-(k - relStart) / tau); + const v = lp3 * env; + L[idx] += v * gl; + R[idx] += v * gr; + } + } + flushPlucks() { + if (!this.pl.length) return; + const b = this.ctx.createBuffer(2, this.ctx.length, SR); + b.copyToChannel(this.pl[0], 0); + b.copyToChannel(this.pl[1], 1); + const s = this.ctx.createBufferSource(); + s.buffer = b; + const g2 = this.ctx.createGain(); + g2.gain.value = 1; + s.connect(g2); + this.out(g2, this.music, 0, 0.3); + s.start(0); + this.pl = []; + } + flute(t, m, dur, vel = 0.22, pan = 0.1) { + const c2 = this.ctx, fr = midi(m); + const o1 = c2.createOscillator(); + o1.type = "sine"; + o1.frequency.value = fr; + const o2 = c2.createOscillator(); + o2.type = "triangle"; + o2.frequency.value = fr * 2; + const lfo = c2.createOscillator(); + lfo.frequency.value = 5.3; + const lg = c2.createGain(); + lg.gain.setValueAtTime(0, t); + lg.gain.linearRampToValueAtTime(fr * 7e-3, t + Math.min(dur, 0.6)); + lfo.connect(lg); + lg.connect(o1.frequency); + lg.connect(o2.frequency); + const g2 = c2.createGain(); + g2.gain.value = 0.12; + const g3 = c2.createGain(); + g3.gain.setValueAtTime(0, t); + g3.gain.linearRampToValueAtTime(vel, t + 0.07); + g3.gain.setValueAtTime(vel * 0.85, t + Math.max(0.08, dur - 0.12)); + g3.gain.linearRampToValueAtTime(0, t + dur + 0.18); + const n = c2.createBufferSource(); + n.buffer = this.noise; + n.loop = true; + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = fr * 2.2; + bp.Q.value = 1.2; + const ng = c2.createGain(); + ng.gain.setValueAtTime(0, t); + ng.gain.linearRampToValueAtTime(vel * 0.16, t + 0.05); + ng.gain.linearRampToValueAtTime(vel * 0.04, t + 0.25); + ng.gain.linearRampToValueAtTime(0, t + dur + 0.1); + n.connect(bp); + bp.connect(ng); + this.out(ng, this.music, pan, 0.4); + o1.connect(g3); + o2.connect(g2); + g2.connect(g3); + this.out(g3, this.music, pan, 0.45); + for (const o of [o1, o2, lfo]) { + o.start(t); + o.stop(t + dur + 0.3); + } + n.start(t, t * 0.37 % 1.5); + n.stop(t + dur + 0.2); + } + pad(t0, t1, notes, vel = 0.06, cutoff = 900) { + const c2 = this.ctx; + const f = c2.createBiquadFilter(); + f.type = "lowpass"; + f.frequency.value = cutoff; + const g2 = c2.createGain(); + const att = Math.min(1.6, (t1 - t0) / 3); + g2.gain.setValueAtTime(0, t0); + g2.gain.linearRampToValueAtTime(vel, t0 + att); + g2.gain.setValueAtTime(vel, Math.max(t0 + att, t1 - att)); + g2.gain.linearRampToValueAtTime(0, t1); + f.connect(g2); + this.out(g2, this.music, 0, 0.5); + for (const m of notes) for (const det of [-6, 6]) { + const o = c2.createOscillator(); + o.type = "triangle"; + o.frequency.value = midi(m); + o.detune.value = det; + o.connect(f); + o.start(t0); + o.stop(t1 + 0.1); + } + } + bell(t, m, vel = 0.15, pan = 0, bus) { + const c2 = this.ctx, fr = midi(m); + const g2 = c2.createGain(); + g2.gain.value = 1; + [[1, 1, 2.5], [2.76, 0.4, 1.2], [5.4, 0.22, 0.6], [8.93, 0.1, 0.35]].forEach(([k, a, d]) => { + const o = c2.createOscillator(); + o.type = "sine"; + o.frequency.value = fr * k; + const og = c2.createGain(); + og.gain.setValueAtTime(0, t); + og.gain.linearRampToValueAtTime(vel * a, t + 5e-3); + og.gain.exponentialRampToValueAtTime(1e-4, t + d); + o.connect(og); + og.connect(g2); + o.start(t); + o.stop(t + d + 0.05); + }); + this.out(g2, bus ?? this.music, pan, 0.5); + } + block(t, vel = 0.2, fr = 950, pan = 0) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = "sine"; + o.frequency.setValueAtTime(fr, t); + o.frequency.exponentialRampToValueAtTime(fr * 0.6, t + 0.05); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(vel, t); + g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.09); + o.connect(g2); + this.out(g2, this.music, pan, 0.15); + o.start(t); + o.stop(t + 0.12); + } + gliss(t, notes, step = 0.06, vel = 0.3, pan = 0) { + notes.forEach((m, i) => this.pluck(t + i * step, m, vel * (0.7 + 0.3 * i / notes.length), pan + (i / notes.length - 0.5) * 0.6, { dur: 2.2 })); + } + melody(t, beat, seq, vel = 0.22, oct = 0, inst = "flute") { + let x = t; + for (const [n, d] of seq) { + if (n !== "-") { + if (inst === "flute") this.flute(x, N[n] + oct, d * beat * 0.96, vel); + else this.pluck(x, N[n] + oct, vel, 0.15, { bend: d >= 2 ? this.r() < 0.5 ? 0 : 0 : 0 }); } - const ra = inv(C.roosterIn, C.roosterIn + 0.6, t); - const pecks = C.pecks; - let peck = 0, pushed = 0; - for (const p of pecks) { - const q2 = inv(p - 0.12, p + 0.12, t); - peck = Math.max(peck, Math.sin(Math.PI * q2)); - if (t > p) pushed += 1; + x += d * beat; + } + return x; + } + // ---------- 音效 ---------- + noiseHit(t, dur, type, f0, f1, vel, pan = 0, q2 = 1, send = 0.2, bus) { + const c2 = this.ctx; + const s = c2.createBufferSource(); + s.buffer = this.noise; + s.loop = true; + const f = c2.createBiquadFilter(); + f.type = type; + f.Q.value = q2; + f.frequency.setValueAtTime(f0, t); + f.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + Math.min(0.01, dur * 0.2)); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); + s.connect(f); + f.connect(g2); + this.out(g2, bus ?? this.sfx, pan, send); + s.start(t, t * 0.731 % 1.8); + s.stop(t + dur + 0.05); + } + tone(t, f0, f1, dur, vel, type = "sine", pan = 0, send = 0.2) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = type; + o.frequency.setValueAtTime(f0, t); + o.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 8e-3); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); + o.connect(g2); + this.out(g2, this.sfx, pan, send); + o.start(t); + o.stop(t + dur + 0.05); + } + wind(t, dur, vel, pan = 0) { + const c2 = this.ctx; + const s = c2.createBufferSource(); + s.buffer = this.noise; + s.loop = true; + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.Q.value = 0.8; + const lfo = c2.createOscillator(); + lfo.frequency.value = 0.23; + const lg = c2.createGain(); + lg.gain.value = 260; + f.frequency.value = 520; + lfo.connect(lg); + lg.connect(f.frequency); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + Math.min(1.5, dur / 3)); + g2.gain.setValueAtTime(vel, t + dur - Math.min(1.5, dur / 3)); + g2.gain.linearRampToValueAtTime(0, t + dur); + s.connect(f); + f.connect(g2); + this.out(g2, this.sfx, pan, 0.1); + s.start(t); + s.stop(t + dur + 0.1); + lfo.start(t); + lfo.stop(t + dur + 0.1); + } + chirp(t, vel = 0.06, pan = 0) { + const base = 2600 + this.r() * 1600; + for (let i = 0; i < 2 + Math.floor(this.r() * 3); i++) this.tone(t + i * 0.09, base, base * (1.25 + this.r() * 0.2), 0.06, vel, "sine", pan, 0.3); + } + peep(t, vel = 0.12, pan = 0) { + this.tone(t, 2100, 2900, 0.09, vel, "triangle", pan, 0.15); + this.tone(t + 0.13, 2200, 3e3, 0.08, vel * 0.8, "triangle", pan, 0.15); + } + quack(t, vel = 0.14, pan = 0, pitch = 1) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(330 * pitch, t); + o.frequency.linearRampToValueAtTime(250 * pitch, t + 0.16); + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.frequency.value = 1150 * pitch; + f.Q.value = 3.2; + const f2 = c2.createBiquadFilter(); + f2.type = "peaking"; + f2.frequency.value = 2400; + f2.gain.value = 8; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.015); + g2.gain.setValueAtTime(vel, t + 0.11); + g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.2); + o.connect(f); + f.connect(f2); + f2.connect(g2); + this.out(g2, this.sfx, pan, 0.15); + o.start(t); + o.stop(t + 0.25); + } + crack(t, vel = 0.25, pan = 0, big = false) { + const n = big ? 7 : 3; + for (let i = 0; i < n; i++) this.noiseHit(t + i * (0.018 + this.r() * 0.03), 0.03 + this.r() * 0.03, "highpass", 2500 + this.r() * 2500, 1800, vel * (0.6 + this.r() * 0.5), pan, 0.7, 0.1); + if (big) this.tone(t, 160, 70, 0.25, vel * 0.9, "sine", pan, 0.2); + } + flap(t, n, rate, vel = 0.12, pan = 0) { + for (let i = 0; i < n; i++) this.noiseHit(t + i / rate, 0.12, "bandpass", 900, 300, vel * (0.8 + this.r() * 0.3), pan, 0.9, 0.1); + } + thump(t, vel = 0.4, f = 95) { + this.tone(t, f, f * 0.55, 0.22, vel, "sine", 0, 0.15); + this.noiseHit(t, 0.05, "lowpass", 1200, 300, vel * 0.5, 0, 0.7, 0.05); + } + shot(t, pan = 0.5) { + this.noiseHit(t, 0.5, "lowpass", 6e3, 200, 0.85, pan, 0.5, 0.6); + this.tone(t, 120, 40, 0.5, 0.6, "sine", pan, 0.6); + this.noiseHit(t + 0.05, 1.6, "lowpass", 900, 120, 0.18, pan, 0.6, 0.9); + } + swanCall(t, vel = 0.08, pan = -0.3) { + const c2 = this.ctx; + for (const [dt, f] of [[0, 520], [0.32, 620]]) { + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(f, t + dt); + o.frequency.linearRampToValueAtTime(f * 0.94, t + dt + 0.28); + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = 1300; + bp.Q.value = 4; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t + dt); + g2.gain.linearRampToValueAtTime(vel, t + dt + 0.04); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dt + 0.3); + o.connect(bp); + bp.connect(g2); + this.out(g2, this.sfx, pan, 0.8); + o.start(t + dt); + o.stop(t + dt + 0.35); + } + } + splash(t, vel = 0.35) { + this.noiseHit(t, 0.6, "lowpass", 3e3, 400, vel, 0, 0.6, 0.3); + for (let i = 0; i < 6; i++) this.tone(t + 0.08 + this.r() * 0.4, 900 + this.r() * 900, 1500 + this.r() * 900, 0.05, vel * 0.2, "sine", this.r() - 0.5, 0.3); + } + heart(t) { + this.tone(t, 70, 50, 0.16, 0.28, "sine", 0, 0.05); + this.tone(t + 0.2, 64, 48, 0.14, 0.2, "sine", 0, 0.05); + } + clap(t, vel = 0.08, pan = -0.5) { + this.noiseHit(t, 0.06, "bandpass", 1600 + this.r() * 600, 900, vel, pan, 1.5, 0.2); + } + swish(t, vel = 0.08) { + this.noiseHit(t, 0.35, "bandpass", 2500, 900, vel, 0, 1, 0.25); + } + drop(t) { + this.tone(t, 700, 1500, 0.12, 0.3, "sine", 0, 0.5); + this.noiseHit(t, 0.08, "highpass", 3e3, 2e3, 0.1, 0, 0.7, 0.4); + } + lap(t0, t1, vel = 0.05) { + for (let t = t0; t < t1; t += 0.6 + this.r() * 0.9) this.noiseHit(t, 0.5, "bandpass", 700 + this.r() * 300, 300, vel, this.r() * 1.2 - 0.6, 1.5, 0.3); + } + }; + var cache = /* @__PURE__ */ new Map(); + function buildSoundtrack(voiceUrl, onStage) { + const film = FILM; + const hit = cache.get(film.id); + if (hit) return hit; + const cached = (async () => { + const len = Math.ceil(SR * (TOTAL + 0.3)); + const ctx = new OfflineAudioContext(2, len, SR); + onStage?.("\u52A0\u8F7D\u65C1\u767D\u2026"); + let voiceBuf = null; + try { + const ab = await (await fetch(voiceUrl)).arrayBuffer(); + voiceBuf = await ctx.decodeAudioData(ab); + } catch (e) { + console.warn("voice load failed", e); + } + onStage?.("\u5408\u6210\u914D\u4E50\u4E0E\u97F3\u6548\u2026"); + const m = new Mixer(ctx); + m.voiceBuf = voiceBuf; + const mg = m.music.gain; + mg.setValueAtTime(0.9, 0); + for (const s of TL.scenes) { + const spans = s.lines?.length ? s.lines.map((l) => [l.t0, l.t0 + l.dur]) : s.voice_start && s.voice_dur ? [[s.voice_start, s.voice_start + s.voice_dur]] : []; + for (const [a, b] of spans) { + mg.setTargetAtTime(0.42, Math.max(0, a - 0.25), 0.08); + mg.setTargetAtTime(0.9, b + 0.1, 0.25); } - const ux = X(0.1) - pushed * 30 * u; - const cower = t > C.shoo ? 0.2 : 0.08; - bird(g2, { x: ux, y: gy + 170 * u, s: 140 * k, t, sw: 0.16, body: UGLY, beak: UGLY_BEAK, fluff: 1.1, seed: 7, face: t > C.girlIn ? -1 : 1, neckUp: -0.45, shiver: t > C.pecks[0] ? 1 : 0.4, squash: cower }); - tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(1.2, 1.5, t)) * (1 - smooth(inv(3.4, 3.7, t)))); - tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(7.6, 7.9, t)) * (1 - smooth(inv(9.6, 10, t)))); - if (ra > 0) { - const rx = lerp(X(1.3), X(0.42), easeOut(ra)) - (t > C.pecks[3] + 0.3 ? (t - C.pecks[3] - 0.3) * 20 * u : 0); - rooster(g2, rx, gy + 175 * u, 240 * k, t, { face: -1, peck, flap: t > C.roosterIn + 0.6 && t < C.pecks[3] + 0.5 ? t * 18 : 0 }); + } + film.score(m); + film.effects(m); + m.flushPlucks(); + m.q.sort((a, b) => a.t - b.t); + let qi = 0; + const until = (tt) => { + while (qi < m.q.length && m.q[qi].t < tt) { + m.q[qi].fn(); + qi++; } - pecks.forEach((p, j) => { - const q2 = t - p; - if (q2 < 0 || q2 > 2) return; - for (let m = 0; m < 3; m++) { - const x = ux + 30 * u + (m - 1) * 30 * u + Math.sin(q2 * 4 + m) * 20 * u; - const y = gy + 40 * u - 80 * u * easeOut(q2 * 2) + q2 * q2 * 40 * u; - leaf(g2, x, y, 10 * u, q2 * 3 + m + j, "#9a968e", 0.8 * (1 - q2 / 2)); - } + }; + until(1.5); + for (let k = 1; k < TOTAL + 0.3; k++) { + const tt = k; + ctx.suspend(tt).then(() => { + until(tt + 1.5); + ctx.resume(); }); - }, - cam(s) { - return cam(s, lerp(1.28, 1.36, easeInOut(s.t / s.D)), s.X(-0.08), s.gy + 110 * s.u); - }, - enterAt: [0.3, 0.6] - }, - leave: { - label: "\u79BB\u5BB6", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#b9a48a", 0.2, "#ffffff", 0); - mountains(g2, W2, gy - 70 * u, 140 * u, "#8a8478", 0.3, 23); - mountains(g2, W2, gy - 20 * u, 90 * u, "#6a665e", 0.35, 29); - band(g2, W2, gy - 20 * u, H2, "#b8ad8a", 0.28, 61); - const path = [[X(-0.75), H2 + 20 * u], [X(-0.42), gy + 170 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 30 * u], [X(0.68), gy - 25 * u]]; - const pts = spline(path, 10); - for (let i = 0; i < pts.length - 1; i++) { - const f = i / pts.length; - brush(g2, [pts[i], pts[i + 1]], lerp(150, 16, f) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); - } - tree(g2, X(0.8), gy - 10 * u, 380 * u, 33, { depth: 4, leaves: (x, y, l) => blot(g2, x, y, l * 0.8, "#8a7a50", 0.25) }); - fence(g2, -20 * u, X(-0.12), gy + 120 * u, 170 * u, 8, X(-0.42), 120 * u); - strokesH(g2, W2, gy + 30 * u, H2, 30, "#7a6a48", 0.28, 15, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X, D } = s; - const p = Math.pow(inv(CUE.leave.walk, D + 1.2, t), 1.15); - const path = spline([[X(-0.62), gy + 200 * u], [X(-0.42), gy + 175 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 32 * u], [X(0.62), gy - 20 * u]], 12); - const idx = Math.min(path.length - 1, Math.floor(p * (path.length - 1))); - const [bx, by] = path[idx]; - const sc = lerp(150, 52, p) * ks(s); - bird(g2, { x: bx, y: by, s: sc, t, sw: 0.18, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: 1, walk: t > CUE.leave.walk ? 1.7 : 0, neckUp: -0.35, shiver: t < CUE.leave.walk ? 0.4 : 0 }); - tear(g2, bx + 45 * u * (sc / 150), by - 95 * u * (sc / 150) + t * 25 % 25 * u, 9 * u, smooth(inv(0.3, 0.6, t)) * (1 - smooth(inv(2.2, 2.6, t)))); - for (let i = 0; i < 14; i++) grass(g2, i / 14 * W2 + hash(i) * 50 * u, H2 - hash(i * 3) * 120 * u, 70 * u, t, 200 + i, "#5d6a44", 0.6, 2); - fallingThings(g2, W2, H2, t, 14, 8, u, (x, y, rot, r) => leaf(g2, x, y * 0.8, (8 + r() * 6) * u, rot, r() < 0.5 ? "#b08a4a" : "#8a7a50", 0.75), 50); - }, - cam(s) { - return cam(s, lerp(1.08, 1.16, s.t / s.D), s.W / 2, s.gy + 60 * s.u); - }, - enterAt: [0.25, 0.7] + } + if (voiceBuf) { + const v = ctx.createBufferSource(); + v.buffer = voiceBuf; + const vg = ctx.createGain(); + vg.gain.value = 1.15; + const hp = ctx.createBiquadFilter(); + hp.type = "highpass"; + hp.frequency.value = 80; + v.connect(hp); + hp.connect(vg); + vg.connect(m.voice); + v.start(1 / 30); + } + const out = await ctx.startRendering(); + onStage?.("\u54CD\u5EA6\u6807\u51C6\u5316\uFF08\u221214 LUFS\uFF09\u2026"); + normalize(out, -14, 0.84); + onStage?.("\u5B8C\u6210"); + return out; + })(); + cache.set(film.id, cached); + cached.catch(() => cache.delete(film.id)); + return cached; + } + function toWav(b) { + const ch = b.numberOfChannels, len = b.length, sr = b.sampleRate; + const buf = new ArrayBuffer(44 + len * ch * 2), v = new DataView(buf); + const w = (o2, s) => { + for (let i = 0; i < s.length; i++) v.setUint8(o2 + i, s.charCodeAt(i)); + }; + w(0, "RIFF"); + v.setUint32(4, 36 + len * ch * 2, true); + w(8, "WAVE"); + w(12, "fmt "); + v.setUint32(16, 16, true); + v.setUint16(20, 1, true); + v.setUint16(22, ch, true); + v.setUint32(24, sr, true); + v.setUint32(28, sr * ch * 2, true); + v.setUint16(32, ch * 2, true); + v.setUint16(34, 16, true); + w(36, "data"); + v.setUint32(40, len * ch * 2, true); + const data = Array.from({ length: ch }, (_, c2) => b.getChannelData(c2)); + let o = 44; + for (let i = 0; i < len; i++) for (let c2 = 0; c2 < ch; c2++) { + const s = Math.max(-1, Math.min(1, data[c2][i])); + v.setInt16(o, s < 0 ? s * 32768 : s * 32767, true); + o += 2; + } + return buf; + } + function loudness(b) { + const fs = b.sampleRate; + const biquad = (x, b0, b1, b2, a1, a2) => { + const y = new Float32Array(x.length); + let x1 = 0, x2 = 0, y1 = 0, y2 = 0; + for (let i = 0; i < x.length; i++) { + const v = b0 * x[i] + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + x2 = x1; + x1 = x[i]; + y2 = y1; + y1 = v; + y[i] = v; + } + return y; + }; + let K = Math.tan(Math.PI * 1681.974450955533 / fs), Q = 0.7071752369554196; + const Vh = Math.pow(10, 3.999843853973347 / 20), Vb = Math.pow(Vh, 0.4996667741545416); + let a0 = 1 + K / Q + K * K; + const s1 = [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; + K = Math.tan(Math.PI * 38.13547087602444 / fs); + Q = 0.5003270373238773; + a0 = 1 + K / Q + K * K; + const s2 = [1, -2, 1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; + const chans = Array.from({ length: b.numberOfChannels }, (_, c2) => { + const y = biquad(b.getChannelData(c2), s1[0], s1[1], s1[2], s1[3], s1[4]); + return biquad(y, s2[0], s2[1], s2[2], s2[3], s2[4]); + }); + const blk = Math.round(0.4 * fs), hop = Math.round(0.1 * fs), z = []; + for (let st = 0; st + blk <= b.length; st += hop) { + let sum = 0; + for (const y of chans) { + let e = 0; + for (let i = st; i < st + blk; i++) e += y[i] * y[i]; + sum += e / blk; + } + z.push(sum); + } + const L = (e) => -0.691 + 10 * Math.log10(e + 1e-12); + const abs = z.filter((e) => L(e) > -70); + if (!abs.length) return -70; + const rel = L(abs.reduce((a, e) => a + e, 0) / abs.length) - 10; + const gated = abs.filter((e) => L(e) > rel); + return L(gated.reduce((a, e) => a + e, 0) / Math.max(1, gated.length)); + } + function normalize(b, target = -14, ceiling = 0.84) { + const L = loudness(b); + const g2 = Math.pow(10, (target - L) / 20); + const n = b.length, ch = Array.from({ length: b.numberOfChannels }, (_, c2) => b.getChannelData(c2)); + const want = new Float32Array(n); + for (let i = 0; i < n; i++) { + let m = 0; + for (const d of ch) { + d[i] *= g2; + const a = Math.abs(d[i]); + if (a > m) m = a; + } + want[i] = m > ceiling ? ceiling / m : 1; + } + const att = 1 / (4e-3 * b.sampleRate), rel = 1 / (0.12 * b.sampleRate); + for (let i = n - 2; i >= 0; i--) want[i] = Math.min(want[i], want[i + 1] + att); + let cur = 1; + for (let i = 0; i < n; i++) { + cur = Math.min(want[i], cur + rel); + for (const d of ch) d[i] *= cur; + } + return { before: L, gain: g2 }; + } + + // src/films/duckling/music.ts + function score(m) { + const B2 = 60 / 72; + const S = sceneStart, D2 = sceneDur; + const r = rng(808); + const arp2 = (t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) => { + let i = 0; + for (let t2 = t0; t2 < t1 - 0.05; t2 += step, i++) { + const ch = chords[Math.floor((t2 - t0) / (step * 8)) % chords.length]; + m.pluck(t2, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.8 }); + } + }; + const G2 = [N.G3, N.D4, N.B4], Em2 = [N.E3, N.B3, N.G4], Dm = [N.D3, N.A3, N.D4], Am2 = [N.A2, N.E3, N.A3], Gh2 = [N.G4, N.B4, N.D5]; + let t = S("title"); + m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5], 0.07, 0.28); + m.pad(t + 0.5, S("summer") + 1, [N.G2, N.D3], 0.05); + m.melody(t + 2.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1.5]], 0.17); + t = S("summer"); + m.pad(t, S("hatch") + 0.5, [N.G2, N.D3, N.B3], 0.045); + arp2(t + 0.3, S("hatch"), [G2, Em2, G2, Dm], B2 / 2, 0.2); + m.melody(t + 1.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1], ["B4", 1], ["D5", 0.5], ["B4", 0.5], ["A4", 2], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.13); + t = S("hatch"); + for (let i = 0; i < Math.floor(D2("hatch") / B2); i++) { + if (i % 2 === 0) m.block(t + i * B2, 0.07, 1100, -0.3); + m.pluck(t + i * B2 + B2 / 2, [N.G4, N.B4, N.D5, N.E5][i % 4], 0.13, 0.3, { dur: 0.5, bright: 0.9 }); + } + m.pad(t, S("bigegg"), [N.G2, N.D3], 0.035); + t = S("bigegg"); + const BE = CUE.bigegg; + m.pad(t, t + BE.open, [N.E2, N.B2], 0.05, 500); + for (let x = t + 0.6; x < t + BE.open - 0.05; x += 0.09) m.pluck(x, N.E3, 0.06 + 0.1 * (x - t) / BE.open, 0, { dur: 0.3, send: 0.1 }); + m.pluck(t + BE.surprise, N.D4, 0.3, 0, { bend: -2 }); + m.pluck(t + BE.surprise + 0.25, N.A3, 0.3, 0, { bend: -2 }); + m.pluck(t + BE.question + 0.05, N.B4, 0.22, 0.2, { bend: 2 }); + arp2(t + BE.walk, S("bullied"), [Em2, G2], B2 / 2, 0.12); + t = S("bullied"); + m.pad(t, S("leave") + 0.5, [N.E2, N.B2, N.G3], 0.05, 700); + for (let i = 0; i < 12; i++) m.pluck(t + 0.3 + i * B2, [N.E4, N.D4, N.B3, N.A3, N.G3, N.A3, N.B3, N.E3][i % 8], 0.16, -0.2, { dur: 2, bend: i % 4 === 3 ? -1 : 0 }); + t = S("leave"); + m.pad(t, S("marsh") + 0.3, [N.E2, N.B2], 0.045, 600); + m.melody(t + 0.4, B2 * 1.1, [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["E4", 2]], 0.13); + t = S("marsh"); + for (let x = t + 0.2; x < t + CUE.marsh.shots[0] - 0.02; x += 0.125) m.pluck(x, Math.floor((x - t) / 0.5) % 2 ? N.B2 : N.E2, 0.17, 0, { dur: 0.25, send: 0.05 }); + m.pad(t + CUE.marsh.hide, S("autumn") + 0.5, [N.E2], 0.04, 400); + for (let x = t + CUE.marsh.hide + 0.6; x < t + 13; x += 0.95) m.heart(x); + t = S("autumn"); + m.pad(t, S("winter") + 0.5, [N.E2, N.B2, N.G3], 0.04, 800); + arp2(t + 0.3, S("winter"), [Em2, Am2, G2, Em2], B2 * 0.75, 0.13); + m.bell(t + CUE.autumn.swans + 0.6, N.B5, 0.07, 0.4); + m.bell(t + CUE.autumn.swans + 3, N.E6, 0.06, -0.3); + m.melody(t + CUE.autumn.swans + 1.4, B2, [["B4", 1.5], ["D5", 0.5], ["E5", 1], ["G5", 1], ["E5", 2], ["D5", 1], ["B4", 1], ["A4", 2]], 0.11, 0); + t = S("winter"); + m.pad(t, S("spring") + 0.5, [N.E2, N.B2], 0.035, 350); + for (let i = 0; i < 6; i++) m.bell(t + 1 + i * 2.1, [N.E5, N.B4, N.D5, N.A4, N.B4, N.E5][i], 0.05, i % 2 ? 0.4 : -0.4); + t = S("spring"); + m.bell(t + 0.15, N.G5, 0.1, 0); + m.bell(t + 0.4, N.D6, 0.07, 0.3); + m.pad(t, S("reflection") + 0.5, [N.G2, N.D3, N.B3], 0.045); + const SP = CUE.spring; + arp2(t + 0.5, t + SP.flapHard, [G2, Em2], B2 / 2, 0.15); + arp2(t + SP.flapHard, S("reflection"), [G2, Dm, Em2, G2], B2 / 4, 0.14, [0, 1, 2, 1, 2, 1]); + m.gliss(t + SP.lift - 0.05, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5, N.E5, N.G5], 0.05, 0.25); + m.melody(t + SP.lift + 0.4, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["E5", 0.5], ["D5", 2]], 0.13); + t = S("reflection"); + const RF = CUE.reflection; + m.pad(t, t + RF.reveal, [N.E2, N.B2, N.E3], 0.04, 600); + for (let i = 0; i < 10; i++) m.pluck(t + 0.8 + i * B2 * 1.05, [N.B3, N.D4, N.E4, N.D4, N.B3, N.A3, N.B3, N.D4, N.E4, N.G4][i], 0.12, 0, { dur: 2 }); + m.bell(t + RF.mirror, N.E5, 0.06, 0); + m.bell(t + RF.mirror + 0.3, N.B5, 0.04, 0.3); + m.gliss(t + RF.reveal - 0.05, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5, N.B5, N.D6], 0.06, 0.3); + [N.G5, N.B5, N.D6, N.G5].forEach((x, i) => m.bell(t + RF.reveal + i * 0.18, x, 0.08, i % 2 ? 0.3 : -0.3)); + m.pad(t + RF.reveal, S("accepted") + 0.5, [N.G2, N.D3, N.B3, N.D4], 0.05, 1200); + m.melody(t + RF.reveal + 1.2, B2, [["G5", 1.5], ["E5", 0.5], ["D5", 1], ["B4", 1], ["D5", 2]], 0.14); + t = S("accepted"); + arp2(t + 0.2, S("fly"), [G2, Em2, Dm, G2], B2 / 2, 0.15); + m.pad(t, S("fly") + 0.3, [N.G2, N.D3], 0.04); + m.melody(t + 0.8, B2, [["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.12); + t = S("fly"); + m.pad(t, S("ending") + 1, [N.G2, N.D3, N.G3, N.B3], 0.06, 1600); + arp2(t, S("ending"), [Gh2, G2, Em2, Gh2], B2 / 4, 0.16, [0, 1, 2, 1, 2, 1, 0, 2]); + for (let i = 0; i < Math.floor(D2("fly") / B2); i++) m.block(t + i * B2, 0.08, 800, 0); + m.melody(t + 0.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["A5", 0.5], ["B5", 2]], 0.15, 0); + t = S("ending"); + m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); + const EN = CUE.ending.end; + arp2(t + 0.2, t + EN - 0.2, [G2, Em2, G2, Dm], B2 * 0.75, 0.12); + m.gliss(t + EN, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.22); + m.bell(t + EN + 0.1, N.G5, 0.08); + m.bell(t + EN + 0.35, N.D6, 0.05, 0.3); + m.pluck(t + EN + 0.6, N.G3, 0.2, 0, { dur: 2 }); + m.pluck(t + EN + 0.6, N.D4, 0.15, 0, { dur: 2 }); + m.pluck(t + EN + 0.6, N.G4, 0.12, 0, { dur: 2 }); + void r; + } + function effects(m) { + const S = sceneStart, D2 = sceneDur; + const r = m.r; + let t = S("title"); + const TT = CUE.title; + m.drop(t + TT.drop); + m.swish(t + TT.write); + m.swish(t + TT.write + 0.45); + m.swish(t + TT.write + 0.9); + m.thump(t + TT.seal + 0.01, 0.45); + for (let i = 0; i < 6; i++) m.peep(t + 2.6 + i * 0.65, 0.04, 0.3); + t = S("summer"); + m.wind(t, D2("summer"), 0.025); + for (let x = t + 0.6; x < t + D2("summer"); x += 1.1 + r() * 1.6) m.chirp(x, 0.035, r() * 1.4 - 0.7); + t = S("hatch"); + for (let k = 0; k < 4; k++) { + const tc = t + 0.4 + k * 0.75; + m.crack(tc + 0.05, 0.22, -0.3 + k * 0.2); + m.crack(tc + 0.25, 0.18, -0.3 + k * 0.2); + m.noiseHit(tc + 0.55, 0.08, "bandpass", 1800, 900, 0.18, -0.3 + k * 0.2, 2, 0.1); + m.peep(tc + 0.75, 0.1, -0.3 + k * 0.2); + for (let p = 1; p < 5; p++) { + const pt = tc + 1 + 2.3 * (p - 1); + if (pt - t < D2("hatch")) m.peep(pt + 0.05, 0.07, -0.3 + k * 0.2); + } + m.noiseHit(tc + 3, 0.05, "lowpass", 600, 200, 0.12, -0.3 + k * 0.2, 0.7, 0.05); + } + t = S("bigegg"); + const BE = CUE.bigegg; + for (let x = t + 0.5; x < t + BE.crackFrom; x += Math.max(0.12, 0.5 - (x - t) * 0.12)) m.block(x, 0.05 + (x - t) * 0.02, 420, 0); + m.crack(t + BE.crackFrom + 0.05, 0.2); + m.crack(t + BE.crackFrom + 0.4, 0.22); + m.crack(t + BE.open, 0.35, 0, true); + m.noiseHit(t + BE.surprise, 0.25, "bandpass", 2400, 1200, 0.06, -0.4, 1.2, 0.2); + m.peep(t + BE.surprise + 0.1, 0.08, -0.4); + m.peep(t + BE.surprise + 0.2, 0.08, -0.5); + for (let x = t + BE.walk + 0.1; x < t + BE.walk + 2.6; x += 0.6) m.noiseHit(x, 0.05, "lowpass", 600, 200, 0.1, 0, 0.7, 0.05); + t = S("bullied"); + const BU = CUE.bullied; + for (let x = t + BU.laugh1[0] + 0.1; x < t + BU.laugh1[1] - 0.2; x += 0.35 + r() * 0.3) m.quack(x, 0.09, -0.5 + r() * 0.3, 1.25 + r() * 0.2); + for (let x = t + BU.laugh2[0] + 0.1; x < t + BU.laugh2[1] - 0.2; x += 0.4 + r() * 0.35) m.quack(x, 0.07, -0.5 + r() * 0.3, 1.25 + r() * 0.2); + m.flap(t + BU.roosterIn, 6, 9, 0.12, 0.5); + m.tone(t + BU.roosterIn + 0.2, 600, 900, 0.25, 0.08, "sawtooth", 0.5, 0.3); + m.tone(t + BU.roosterIn + 0.45, 900, 500, 0.5, 0.07, "sawtooth", 0.5, 0.4); + for (const p of BU.pecks) { + m.block(t + p, 0.25, 1500, 0.2); + m.flap(t + p - 0.15, 2, 10, 0.08, 0.3); + } + m.swish(t + BU.shoo, 0.07); + t = S("leave"); + m.wind(t, D2("leave") + 1, 0.07, -0.2); + for (let x = t + CUE.leave.walk + 0.2; x < t + D2("leave"); x += 0.62) m.noiseHit(x, 0.04, "lowpass", 500, 200, 0.05, 0, 0.7, 0.05); + t = S("marsh"); + m.lap(t, t + D2("marsh"), 0.035); + const MA = CUE.marsh; + for (let x = t + MA.laugh[0] + 0.1; x < t + MA.laugh[1] - 0.2; x += 0.3 + r() * 0.3) m.quack(x, 0.1, 0.4 + r() * 0.3, 0.95 + r() * 0.15); + m.shot(t + MA.shots[0], 0.6); + m.shot(t + MA.shots[1], 0.45); + m.flap(t + MA.scatter, 14, 12, 0.13, 0.4); + for (let i = 0; i < 6; i++) m.quack(t + MA.scatter + 0.05 + i * 0.12, 0.08, 0.5, 1.1); + m.splash(t + MA.hide + 0.2, 0.12); + t = S("autumn"); + m.wind(t, D2("autumn"), 0.03, 0.2); + const AU = CUE.autumn.swans; + m.swanCall(t + AU + 0.8, 0.06, 0.5); + m.swanCall(t + AU + 3, 0.07, 0.1); + m.swanCall(t + AU + 5.6, 0.05, -0.5); + m.flap(t + AU + 1.5, 12, 1.4, 0.04, 0.2); + t = S("winter"); + m.wind(t, D2("winter") + 0.8, 0.12, 0); + m.wind(t + 2, D2("winter") - 2, 0.06, 0.6); + m.crack(t + CUE.winter.ice, 0.2, 0, true); + m.noiseHit(t + CUE.winter.ice + 0.05, 0.9, "bandpass", 300, 120, 0.12, 0, 4, 0.5); + t = S("spring"); + for (let x = t + 0.4; x < t + D2("spring"); x += 0.7 + r() * 1.1) m.chirp(x, 0.04, r() * 1.4 - 0.7); + const SP = CUE.spring; + m.flap(t + SP.spread, 2, 2, 0.1, 0); + m.flap(t + SP.spread + 0.9, Math.floor((SP.flapHard - SP.spread - 0.9) * 0.83), 0.83, 0.08, 0); + m.flap(t + SP.flapHard, Math.floor((SP.lift - SP.flapHard) * 1.6), 1.6, 0.13, 0); + m.flap(t + SP.lift, 6, 1.46, 0.1, 0.2); + m.noiseHit(t + SP.lift - 0.1, 1.2, "bandpass", 400, 2400, 0.12, 0, 1, 0.4); + m.pluck(t + SP.question, N.B4, 0.2, 0, { bend: 2 }); + t = S("reflection"); + const RF = CUE.reflection; + m.flap(t, 5, 7, 0.1, -0.2); + m.splash(t + RF.land, 0.3); + m.lap(t + 1.2, t + D2("reflection"), 0.03); + m.bell(t + RF.reveal, 91, 0.06, 0, m.sfx); + t = S("accepted"); + m.lap(t, t + D2("accepted"), 0.03); + const CH = CUE.accepted.cheer; + for (let x = t + CH + 0.1; x < t + CH + 4.5; x += 0.11 + r() * 0.08) m.clap(x, 0.06 * (1 - (x - t - CH) / 5), -0.55 + r() * 0.2); + for (let i = 0; i < 4; i++) m.chirp(t + CH + 0.4 + i * 1.3, 0.03, 0.4); + t = S("fly"); + m.wind(t, D2("fly") + 1, 0.07, 0); + m.flap(t + 0.3, Math.floor(D2("fly") * 3.2 / 2 / Math.PI * 2), 3.2 / Math.PI, 0.09, 0); + m.noiseHit(t + 0.9, 1.6, "bandpass", 300, 2e3, 0.1, 0, 1, 0.5); + t = S("ending"); + m.lap(t, t + D2("ending"), 0.025); + m.thump(t + CUE.ending.end + 0.12, 0.4); + } + + // src/films/duckling/content.ts + var PLATFORMS = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66", + titleMax: 20, + tagMax: 10, + color: "#ff2442", + titles: ["\u6C34\u58A8\u7248\u4E11\u5C0F\u9E2D\uFF5C\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", "\u4E00\u7B14\u4E00\u753B\u753B\u51FA\u7684\u6C34\u58A8\u4E11\u5C0F\u9E2D", "\u7761\u524D\u6545\u4E8B\uFF5C\u4E11\u5C0F\u9E2D\u53D8\u767D\u5929\u9E45"], + desc: `\u8FD8\u8BB0\u5F97\u5C0F\u5B66\u8BED\u6587\u4E66\u91CC\u7684\u300A\u4E11\u5C0F\u9E2D\u300B\u5417\uFF1F +\u8FD9\u6B21\u628A\u5B83\u753B\u6210\u4E86\u4E00\u90E8\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF1A\u5BA3\u7EB8\u5E95\u3001\u6BDB\u7B14\u52FE\u7EBF\u3001\u6DE1\u5F69\u6655\u67D3\uFF0C\u8FDE\u5B57\u5E55\u90FD\u662F\u6977\u4E66\u3002 + +\u{1F95A} \u6700\u5927\u7684\u90A3\u9897\u86CB\uFF0C\u94BB\u51FA\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19 +\u{1F413} \u88AB\u7B11\u8BDD\u3001\u88AB\u6B3A\u8D1F\uFF0C\u4ED6\u6084\u6084\u79BB\u5F00\u4E86\u5BB6 +\u2744\uFE0F \u71AC\u8FC7\u6700\u51B7\u7684\u51AC\u5929 +\u{1F9A2} \u6625\u5929\u6765\u4E86\uFF0C\u6C34\u91CC\u7684\u5012\u5F71\u662F\u4E00\u53EA\u767D\u5929\u9E45 + +\u9001\u7ED9\u6BCF\u4E00\u4E2A\u89C9\u5F97\u81EA\u5DF1\u300C\u4E0D\u4E00\u6837\u300D\u7684\u5C0F\u670B\u53CB\uFF0C\u4E5F\u9001\u7ED9\u5C0F\u65F6\u5019\u7684\u81EA\u5DF1\uFF1A +\u4E0D\u6015\u98CE\u96EA\uFF0C\u4E5F\u4E0D\u6015\u5632\u7B11\uFF0C\u603B\u6709\u4E00\u5929\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002 + +\u9002\u5408\u5F53\u7761\u524D\u6545\u4E8B\uFF0C\u548C\u5B69\u5B50\u4E00\u8D77\u770B \u{1F440}`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u513F\u7AE5\u6545\u4E8B", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8\u52A8\u753B", "\u4EB2\u5B50\u9605\u8BFB", "\u5C0F\u5B66\u8BED\u6587", "\u7AE5\u8BDD\u6545\u4E8B"], + tip: "\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\uFF1B\u8BDD\u9898\u7C98\u8D34\u540E\u5728 App \u91CC\u70B9\u9009\u4E00\u4E0B\u5373\u53EF\u53D8\u6210\u84DD\u8272\u8BDD\u9898\u3002" }, - marsh: { - label: "\u6CBC\u6CFD", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy } = s; - sky(g2, W2, gy, "#7e878a", 0.25, "#ffffff", 0.02); - mountains(g2, W2, gy - 50 * u, 100 * u, "#6e7672", 0.3, 37, { soft: 1 }); - for (let i = 0; i < 46; i++) reed(g2, i / 46 * W2 + hash(i) * 30 * u, gy - 10 * u, (90 + hash(i * 3) * 90) * u, 0, 500 + i, { color: "#5d6658", alpha: 0.4, wind: 0.2 }); - band(g2, W2, gy - 15 * u, H2, "#8fa3a6", 0.32, 71, 6 * u); - particles(8, 19, (r) => blot(g2, r() * W2, gy + r() * (H2 - gy), (80 + r() * 160) * u, "#5d6a62", 0.12, 2.2, 0.4)); - strokesH(g2, W2, gy + 20 * u, H2, 36, "#3f4c52", 0.35, 17, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const M = CUE.marsh; - water(g2, 0, W2, gy + 10 * u, H2 - gy, t, 3, { alpha: 0.3, lines: 12 }); - for (let i = 0; i < 12; i++) reed(g2, X(0.62) + i * 38 * u, gy + 40 * u + hash(i) * 30 * u, (280 + hash(i * 5) * 160) * u, t, 120 + i, { wind: t > M.shots[0] ? 1 + 2 * Math.exp(-(t - M.shots[0]) * 2) : 1 }); - for (const [ts, fx, fy] of [[M.shots[0], 0.86, -130], [M.shots[1], 0.72, -170]]) { - const q2 = t - ts; - if (q2 > 0 && q2 < 0.09) { - g2.fillStyle = rgba("#e0a040", 0.9); - g2.beginPath(); - g2.arc(X(fx), gy + fy * u, 26 * u, 0, Math.PI * 2); - g2.fill(); - } - if (q2 > 0) smoke(g2, X(fx) - q2 * 30 * u, gy + fy * u - q2 * 25 * u, (30 + 110 * easeOut(q2 / 2)) * u, 0.6 * Math.max(0, 1 - q2 / 4), Math.floor(ts * 10)); - } - for (let i = 0; i < 3; i++) { - const takeoff = easeIn(inv(M.scatter + i * 0.12, M.scatter + 2.1 + i * 0.12, t)); - const bx = X(0.2 + i * 0.16) + takeoff * 380 * u, by = gy + (110 + i * 25) * u - takeoff * (gy + 300 * u); - const laugh = t > M.laugh[0] && t < M.laugh[1]; - const o = { x: bx, y: by, s: 140 * k, t: t + i, sw: 0.32, body: "#9b7b55", head: "#3d6b4a", beak: "#d8b03a", seed: 60 + i, face: takeoff > 0 ? 1 : -1, mouth: laugh ? 0.5 + 0.5 * Math.abs(Math.sin(t * 9 + i)) : 0, fluff: 0 }; - if (takeoff > 0) bird(g2, { ...o, legs: false, wing: 1, flap: t * 16 + i, neckUp: -0.7 }); - else { - swimBird(g2, o); - if (laugh) soundMarks(g2, bx - 70 * u, by - 150 * u, 34 * u, 0.8, i, -1); - } - } - particles(7, 33, (r, i) => { - const q2 = t - M.scatter + 0.1 - r() * 0.4; - if (q2 <= 0) return; - farBird(g2, X(0.8) - q2 * (120 + r() * 120) * u, gy - 120 * u - q2 * (150 + r() * 120) * u, (18 + r() * 12) * u, t * 14 + i); - }); - const hide = easeInOut(inv(M.hide, M.hide + 1.4, t)); - const startle = Math.exp(-Math.max(0, t - M.shots[0]) * 5) * (t > M.shots[0] ? 1 : 0); - const ux = lerp(X(-0.08), X(-0.55), hide), uy = gy + 175 * u; - swimBird(g2, { x: ux, y: uy, s: 150 * k, t, sw: 0.24, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: hide > 0 && hide < 1 ? -1 : 1, neckUp: lerp(0, -0.6, hide), tuck: 0.3 * hide, shiver: t > M.shots[0] ? 1 : 0, squash: startle * 0.2 }); - exclaim(g2, ux + 20 * u, uy - 220 * u, 50 * u, startle > 0.05 ? 1 : 0, 3); - for (let i = 0; i < 9; i++) reed(g2, X(-0.78) + i * 40 * u + hash(i * 9) * 20 * u, H2 + 20 * u, (420 + hash(i * 13) * 220) * u, t, 150 + i, { color: "#2f3b2c", alpha: 0.85, wind: t > M.shots[0] ? 1 + 1.5 * Math.exp(-(t - M.shots[0])) : 1 }); - }, - cam(s) { - const { t, u } = s; - let dx = 0; - for (const ts of CUE.marsh.shots) { - const q2 = t - ts; - if (q2 > 0) dx += Math.sin(q2 * 90) * 10 * u * Math.exp(-q2 * 9); - } - const p = easeInOut(inv(9.2, s.D, t)); - return cam(s, lerp(1.15, 1.55, p), lerp(s.X(0.1), s.X(-0.52), p), s.gy + lerp(90, 120, p) * u, { dx, dy: dx * 0.5 }); - }, - enterAt: [0.5, 0.6] + { + id: "dy", + name: "\u6296\u97F3", + titleMax: 30, + tagMax: 5, + color: "#111111", + titles: ["\u7528\u4EE3\u7801\u4E00\u7B14\u4E00\u753B\uFF0C\u753B\u4E86\u4E00\u90E8\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u300B", "\u50CF\u5C0F\u65F6\u5019\u8BED\u6587\u4E66\u63D2\u56FE\u7684\u4E11\u5C0F\u9E2D\uFF0C\u4F60\u8FD8\u8BB0\u5F97\u5417", "\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\uFF0C\u8FD9\u6B21\u662F\u6C34\u58A8\u624B\u7ED8\u7248"], + desc: `\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u2014\u2014\u628A\u5B89\u5F92\u751F\u7684\u300A\u4E11\u5C0F\u9E2D\u300B\u753B\u6210\u4E86\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\u3002 +\u4ECE\u7834\u58F3\u3001\u88AB\u5632\u7B11\u3001\u71AC\u8FC7\u5BD2\u51AC\uFF0C\u5230\u5728\u6C34\u91CC\u770B\u89C1\u81EA\u5DF1\u7684\u5012\u5F71\uFF1A\u539F\u6765\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002 +\u76F8\u4FE1\u81EA\u5DF1\uFF0C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u6545\u4E8B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE\u52A8\u753B"], + tip: "\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" }, - autumn: { - label: "\u79CB\u5929\u7684\u5927\u9E1F", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#e3a868", 0.12, "#e8b878", 0.38); - sun(g2, X(0.3), gy - 150 * u, 72 * u, "#c8463a", 0.85); - mountains(g2, W2, gy - 40 * u, 170 * u, "#8a7a6a", 0.32, 41); - mountains(g2, W2, gy, 100 * u, "#5e5248", 0.45, 47); - band(g2, W2, gy, gy + 190 * u, "#b8a990", 0.32, 81, 4 * u); - particles(9, 3, (r) => { - const y = gy + 20 * u + r() * 120 * u, w = (80 - (y - gy) / u * 0.3) * u; - brush(g2, [[X(0.3) - w, y], [X(0.3), y + 2 * u], [X(0.3) + w, y]], 5 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); - }); - band(g2, W2, gy + 185 * u, H2, "#b8925a", 0.32, 83); - strokesH(g2, W2, gy + 200 * u, H2, 30, "#7a5a30", 0.3, 19, u); - tree(g2, X(-0.88), gy + 260 * u, 640 * u, 9, { - depth: 5, - color: "#3a2a20", - leaves: (x, y, l) => { - blot(g2, x, y, l * 1.3, "#c0582e", 0.35); - blot(g2, x + l * 0.5, y - l * 0.2, l * 0.9, "#e0a040", 0.3); - } - }); - for (let i = 0; i < 10; i++) reed(g2, X(0.7) + i * 30 * u, gy + 200 * u, (180 + hash(i) * 120) * u, 0, 600 + i, { color: "#8a6a3a", alpha: 0.7 }); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X, portrait } = s; - const k = ks(s); - fallingThings(g2, W2, H2, t, 22, 12, u, (x, y, rot, r) => leaf(g2, x * 0.55, y, (9 + r() * 7) * u, rot, ["#c0582e", "#d98a3a", "#b8402a", "#e0a040"][Math.floor(r() * 4)], 0.85), 55); - const sp = (W2 + 1400 * u) / 11.5; - let lead = 0; - for (let i = 0; i < 5; i++) { - const x = W2 + 160 * u + i * 170 * u - (t - CUE.autumn.swans) * sp; - const y = H2 * (portrait ? 0.2 : 0.17) + i * (portrait ? 70 : 52) * u + 10 * u * Math.sin(t * 1.3 + i); - if (i === 0) lead = x; - if (x < -300 * u || x > W2 + 300 * u) continue; - bird(g2, { x, y, s: 105 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 4.2 + i * 0.9, face: -1, neckUp: -0.85, seed: 80 + i }); - } - const lookUp = smooth(inv(CUE.autumn.lookUp, CUE.autumn.lookUp + 1, t)); - const turn = lead < X(0.42); - const walkP = inv(CUE.autumn.walk, CUE.autumn.walk + 2.2, t); - const ux = X(0.42) - easeInOut(walkP) * 110 * u; - bird(g2, { x: ux, y: gy + 230 * u, s: 168 * k, t, sw: 0.42, body: UGLY, beak: UGLY_BEAK, fluff: 0.7, seed: 7, face: turn ? -1 : 1, neckUp: lookUp * 0.95, walk: walkP > 0 && walkP < 1 ? 1.4 : 0 }); - }, - cam(s) { - return cam(s, lerp(1.04, 1.12, easeInOut(s.t / s.D)), lerp(s.X(0.15), s.X(0.05), easeInOut(s.t / s.D)), s.H * 0.48); - }, - enterAt: [0.8, 0.3] + { + id: "sph", + name: "\u5FAE\u4FE1\u89C6\u9891\u53F7", + titleMax: 16, + tagMax: null, + color: "#07c160", + titles: ["\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u300B", "\u4E11\u5C0F\u9E2D\u53D8\u767D\u5929\u9E45\xB7\u6C34\u58A8\u7248", "\u76F8\u4FE1\u81EA\u5DF1\uFF0C\u4F60\u4E5F\u4F1A\u98DE"], + desc: `\u4E00\u90E8\u50CF\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u7684\u6C34\u58A8\u52A8\u753B\u3002 +\u6700\u5927\u7684\u90A3\u9897\u86CB\u91CC\uFF0C\u94BB\u51FA\u4E86\u53C8\u5927\u53C8\u7070\u7684\u4E11\u5C0F\u9E2D\uFF1B\u4ED6\u88AB\u5632\u7B11\u3001\u79BB\u5F00\u5BB6\u3001\u71AC\u8FC7\u5BD2\u51AC\uFF0C\u7EC8\u4E8E\u5728\u6625\u5929\u7684\u6E56\u6C34\u91CC\u770B\u89C1\u2014\u2014\u81EA\u5DF1\u662F\u4E00\u53EA\u767D\u5929\u9E45\u3002 +\u8F6C\u7ED9\u5BB6\u91CC\u7684\u5C0F\u670B\u53CB\uFF0C\u5F53\u4F5C\u4ECA\u665A\u7684\u7761\u524D\u6545\u4E8B\u5427\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u7AE5\u8BDD\u6545\u4E8B", "\u56FD\u98CE", "\u624B\u7ED8", "\u4EB2\u5B50", "\u8BED\u6587\u8BFE\u672C", "\u6210\u957F", "\u76F8\u4FE1\u81EA\u5DF1"], + tip: "\u6807\u9898 \u226416 \u5B57\uFF0C\u8BDD\u9898\u6570\u91CF\u4E0D\u9650\u3002" }, - winter: { - label: "\u5BD2\u51AC", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#5f6a74", 0.35, "#c9d0d4", 0.1); - mountains(g2, W2, gy - 30 * u, 160 * u, "#5a6268", 0.3, 53); - particles(10, 5, (r) => blot(g2, r() * W2, gy - 120 * u - r() * 60 * u, (50 + r() * 60) * u, "#ffffff", 0.45, 2, 0.5)); - band(g2, W2, gy - 10 * u, gy + 300 * u, "#c9d4d8", 0.5, 91, 5 * u); - particles(10, 77, (r, i) => { - const x = r() * W2, y = gy + 20 * u + r() * 250 * u; - brush(g2, [[x, y], [x + (60 + r() * 120) * u, y + (r() - 0.5) * 20 * u]], 1.6 * u, { color: "#6a7a86", alpha: 0.35, seed: i }); - }); - band(g2, W2, gy + 290 * u, H2, "#ffffff", 0.6, 93, 14 * u); - for (const [fx, h, sd] of [[-0.86, 520, 3], [0.88, 460, 7]]) { - tree(g2, X(fx), gy + 40 * u, h * u, sd, { depth: 5, color: "#2a2622", leaves: (x, y, l) => blot(g2, x, y - 3 * u, l * 0.35, "#ffffff", 0.85, 1.6, 0.6) }); - } - for (let i = 0; i < 12; i++) reed(g2, X(-0.5) + i * 26 * u, gy + 290 * u, (140 + hash(i) * 90) * u, 0, 700 + i, { color: "#6a6458", alpha: 0.65, wind: 0.6 }); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const night = 0.34 * smooth(inv(4, 6.5, t)) - 0.24 * smooth(inv(8.2, 9, t)) + 0.24 * smooth(inv(9.6, 10.6, t)) - 0.3 * smooth(inv(11.8, 13.2, t)); - if (night > 0) { - g2.fillStyle = rgba("#26303c", night); - g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); - blot(g2, X(0.6), H2 * 0.16, 70 * u, "#fffbe8", night * 2.2); - g2.fillStyle = rgba("#fffbe8", night * 2.4); - g2.beginPath(); - g2.arc(X(0.6), H2 * 0.16, 30 * u, 0, Math.PI * 2); - g2.fill(); - } - const ux = X(-0.04), uy = gy + 150 * u; - for (let i = 0; i < 6; i++) { - const a = i / 6 * Math.PI * 2 + 0.4, L = (150 + hash(i) * 160) * u; - const pts = [[ux + Math.cos(a) * 60 * u, uy + 10 * u + Math.sin(a) * 15 * u]]; - for (let j = 1; j < 5; j++) pts.push([pts[0][0] + Math.cos(a + nz(j, i) * 0.4) * L * j / 4, pts[0][1] + Math.sin(a + nz(j, i) * 0.4) * L * j / 4 * 0.25]); - brush(g2, pts, 2.2 * u, { color: "#3e4c58", alpha: 0.75, grow: easeOut(inv(CUE.winter.ice + i * 0.05, CUE.winter.ice + 0.7 + i * 0.05, t)), taperIn: 0.05, seed: i }); - } - bird(g2, { x: ux, y: uy, s: 175 * k, t, sw: 0.5, body: UGLY, beak: UGLY_BEAK, fluff: 0.8, seed: 7, face: 1, swim: true, tuck: 1, shiver: 1, snow: smooth(inv(0, s.D, t)), noBlink: true }); - for (let i = 0; i < 4; i++) { - const q2 = (t * 0.45 + i * 0.27) % 1; - const y = H2 * (0.2 + 0.18 * i); - brush(g2, [[lerp(-300, W2 + 300, q2) * 1, y], [lerp(-300, W2 + 300, q2) + 180 * u, y + 6 * u], [lerp(-300, W2 + 300, q2) + 360 * u, y]], 3 * u, { color: "#d8dde0", alpha: 0.5, dry: 0.9, seed: i }); - } - fallingThings(g2, W2, H2, t, 170, 31, u, (x, y, _r, r) => { - const rr = (2 + r() * 5) * u; - g2.fillStyle = rgba("#ffffff", 0.92); - g2.beginPath(); - g2.arc(x, y, rr, 0, Math.PI * 2); - g2.fill(); - g2.strokeStyle = rgba("#8a96a0", 0.35); - g2.lineWidth = 0.8 * u; - g2.stroke(); - }, 95); - }, - cam(s) { - return cam(s, lerp(1.2, 1.5, easeInOut(s.t / s.D)), s.X(-0.04), s.gy + 70 * s.u); - }, - enterAt: [0.5, 0.2] + { + id: "bili", + name: "\u54D4\u54E9\u54D4\u54E9", + titleMax: 80, + tagMax: 10, + color: "#fb7299", + titles: ["\u3010\u6C34\u58A8\u624B\u7ED8\u3011\u5B89\u5F92\u751F\u7AE5\u8BDD\u300A\u4E11\u5C0F\u9E2D\u300B\uFF5C\u5C0F\u5B66\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u5168\u7A0B\u4EE3\u7801\u9010\u5E27\u7ED8\u5236\uFF0B\u539F\u521B\u56FD\u98CE\u914D\u4E50", "\u3010\u513F\u7AE5\u52A8\u753B\u3011\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\u300B\uFF1A\u4E00\u90E8\u50CF\u8BFE\u672C\u63D2\u56FE\u7684\u56FD\u98CE\u7AE5\u8BDD\u77ED\u7247"], + desc: `\u4E00\u90E8\u7EA6 2 \u5206\u534A\u7684\u6C34\u58A8\u624B\u7ED8\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u6539\u7F16\u81EA\u5B89\u5F92\u751F\u7AE5\u8BDD\u300A\u4E11\u5C0F\u9E2D\u300B\uFF081843\uFF09\u3002 + +\u753B\u9762\uFF1A\u5168\u90E8\u7531\u4EE3\u7801\u5728\u6D4F\u89C8\u5668\u91CC\u9010\u5E27\u7ED8\u5236\u2014\u2014\u5BA3\u7EB8\u7EB9\u7406\u3001\u53D8\u5BBD\u6BDB\u7B14\u7B14\u89E6\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u6655\u67D3\u3001\u6731\u7802\u5370\u7AE0\uFF0C14 \u4E2A\u573A\u666F\u6A2A\u7AD6\u5C4F\u5404\u81EA\u91CD\u65B0\u6784\u56FE\u3002 +\u58F0\u97F3\uFF1A\u4E2D\u6587\u65C1\u767D\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF08\u53E4\u7B5D\u62E8\u5F26\uFF0B\u7AF9\u7B1B\uFF0B\u94FA\u5E95\uFF0C\u968F\u56DB\u5B63\u6362\u60C5\u7EEA\uFF09\uFF1B\u7834\u58F3\u3001\u9E2D\u53EB\u3001\u67AA\u58F0\u3001\u51B0\u88C2\u3001\u632F\u7FC5\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\u3002 + +\u7AE0\u8282\uFF1A +00:00 \u7247\u5934 +00:06 \u590F\u65E5\u4E61\u6751 +00:18 \u7834\u58F3 +00:29 \u6700\u5927\u7684\u86CB +00:41 \u88AB\u5632\u7B11 +00:51 \u79BB\u5BB6 +00:59 \u6CBC\u6CFD +01:13 \u79CB\u5929\u7684\u5927\u9E1F +01:28 \u5BD2\u51AC +01:42 \u6625\u56DE +01:54 \u5012\u5F71 +02:07 \u63A5\u7EB3 +02:19 \u9AD8\u98DE +02:26 \u5BC4\u8BED`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u56FD\u98CE", "\u624B\u7ED8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u539F\u521B\u914D\u4E50", "\u4EE3\u7801\u827A\u672F", "\u7AE5\u8BDD"], + tip: "\u6807\u9898 \u226480 \u5B57\uFF0C\u6807\u7B7E \u226410 \u4E2A\uFF1B\u7AE0\u8282\u65F6\u95F4\u53EF\u76F4\u63A5\u7C98\u8FDB\u7B80\u4ECB\u3002" + } + ]; + var PHOTO_POSTS = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 20, + tagMax: 10, + titles: ["\u4E11\u5C0F\u9E2D\u7684\u516D\u4E2A\u77AC\u95F4\uFF5C\u6C34\u58A8\u5B9E\u51B5\u56FE", "\u4F1A\u52A8\u7684\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\uFF5C\u4E11\u5C0F\u9E2D"], + body: `\u628A\u300A\u4E11\u5C0F\u9E2D\u300B\u753B\u6210\u4E86 6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u957F\u6309\u5C31\u80FD\u770B\u5B83\u52A8\u8D77\u6765 \u2728 + +\u5176\u4E00 \xB7 \u6700\u5927\u7684\u86CB\uFF1A\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19 +\u5176\u4E8C \xB7 \u4E11\u5C0F\u9E2D\uFF1A\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6 +\u5176\u4E09 \xB7 \u4EF0\u671B\uFF1A\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7 +\u5176\u56DB \xB7 \u5BD2\u51AC\uFF1A\u6E56\u9762\u7ED3\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\u71AC\u8FC7\u4E00\u4E2A\u4E2A\u5BD2\u591C +\u5176\u4E94 \xB7 \u5012\u5F71\uFF1A\u4F4E\u5934\u4E00\u770B\uFF0C\u6C34\u91CC\u662F\u4E00\u53EA\u767D\u5929\u9E45 +\u5176\u516D \xB7 \u5C55\u7FC5\uFF1A\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45 + +\u6BCF\u4E00\u7B14\u90FD\u662F\u4EE3\u7801\u753B\u51FA\u6765\u7684\uFF1A\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u3002 +\u4F60\u5C0F\u65F6\u5019\u6700\u559C\u6B22\u8BFE\u672C\u91CC\u7684\u54EA\u4E00\u7BC7\uFF1F`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8", "\u5C0F\u5B66\u8BED\u6587", "\u7AE5\u8BDD\u6545\u4E8B", "\u6CBB\u6108\u7CFB", "\u52A8\u6001\u58C1\u7EB8"], + tip: "\u53D1\u5E03\u65F6\u9009\u62E9\u300C\u5B9E\u51B5\u56FE\u300D\u4E0A\u4F20 6 \u7EC4 JPG+MOV\uFF1B\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\u3002" }, - spring: { - label: "\u6625\u56DE", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#bcd3c8", 0.2, "#ffffff", 0); - sun(g2, X(0.5), H2 * 0.15, 50 * u, "#d65a40", 0.7); - mountains(g2, W2, gy - 40 * u, 150 * u, "#86a87a", 0.35, 61); - mountains(g2, W2, gy, 90 * u, "#5e8a50", 0.4, 67); - band(g2, W2, gy - 5 * u, H2, "#a9c48a", 0.35, 101); - const pts = spline([[X(-1.1), gy + 120 * u], [X(-0.3), gy + 60 * u], [X(0.3), gy + 160 * u], [X(1.1), gy + 90 * u]], 12); - brush(g2, pts, 70 * u, { color: "#9fc0c8", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.2 }); - brush(g2, pts.map(([x, y]) => [x, y - 10 * u]), 6 * u, { color: "#5e7a86", alpha: 0.3, dry: 0.7, taperIn: 0, taperOut: 0 }); - strokesH(g2, W2, gy + 200 * u, H2, 26, "#5e8a50", 0.3, 23, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const C = CUE.spring; - willow(g2, X(-0.9), gy + 220 * u, 620 * u, t, 3, "#8fb86b", 0.15 + 0.85 * smooth(inv(C.buds, C.buds + 1.4, t))); - const bloom = smooth(inv(C.bloom, C.bloom + 1.3, t)); - tree(g2, X(0.86), gy + 220 * u, 520 * u, 21, { depth: 4, color: "#3a2c26", t, leaves: (x, y, l) => { - if (bloom > 0) { - blossom(g2, x, y, l * 0.28 * bloom, Math.floor(x), "#ec9aa8", 0.75); - blossom(g2, x + l * 0.3, y - l * 0.2, l * 0.2 * bloom, Math.floor(y), "#f4b8c0", 0.7); - } else blot(g2, x, y, l * 0.12, "#b86a74", 0.5); - } }); - for (let i = 0; i < 2; i++) farBird(g2, X(-0.2 + i * 0.4) + Math.sin(t * 0.5 + i) * 100 * u, H2 * 0.25 + i * 40 * u, 20 * u, t * 9 + i, "#3a3a3a"); - const spread = easeOut(inv(C.spread, C.spread + 0.9, t)); - const lift = easeIn(inv(C.lift, C.lift + 4.2, t)); - const fx = X(-0.12) + lift * W2 * 0.55, fy = gy + 170 * u - lift * (gy + 260 * u); - const flapRate = t < C.flapHard ? 2.6 : t < C.lift ? lerp(3, 6, inv(C.flapHard, C.lift, t)) : 4.6; - bird(g2, { - x: fx, - y: fy, - s: 205 * k, - t, - sw: 0.8, - body: "#b5b1a9", - beak: "#b88a5c", - fluff: 0.2, - seed: 7, - face: 1, - wing: spread, - flap: t * flapRate * 1 + (t > 3.4 ? Math.sin(t) : 0), - legs: lift < 0.05, - neckUp: lift > 0 ? -0.6 * smooth(lift * 4) : lerp(0, 0.3, spread), - look: t < C.spread ? 0.2 * Math.sin(t * 1.5) : 0 - }); - question(g2, fx + 30 * u, fy - 330 * u * k, 80 * u, smooth(inv(C.question, C.question + 0.25, t)) * (1 - smooth(inv(C.flapHard + 0.2, C.flapHard + 0.5, t)))); - if (t > C.lift && t < C.lift + 1.2) particles(8, 99, (r, i) => { - const q2 = t - C.lift; - leaf(g2, X(-0.12) + (r() - 0.5) * 200 * u, gy + 180 * u - q2 * 80 * u * r(), 8 * u, q2 * 4 + i, "#9fbf7a", 0.8 * (1 - q2 / 1.2)); - }); - fallingThings(g2, W2, H2, t, 26, 41, u, (x, y, rot, r) => leaf(g2, x, y, (6 + r() * 5) * u, rot, r() < 0.5 ? "#f0a8b4" : "#f6c8cf", 0.85), 45); - }, - cam(s) { - const lift = easeInOut(inv(CUE.spring.lift - 0.3, CUE.spring.lift + 3.5, s.t)); - return cam(s, lerp(1.3, 1.1, lift), lerp(s.X(-0.1), s.W / 2, lift), lerp(s.gy + 60 * s.u, s.H * 0.3, lift)); + { + id: "dy", + name: "\u6296\u97F3\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 30, + tagMax: 5, + titles: ["\u6C34\u58A8\u5B9E\u51B5\u56FE\uFF1A\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\u7684\u516D\u4E2A\u77AC\u95F4", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF0C\u4E11\u5C0F\u9E2D\u957F\u5927\u4E86"], + body: `6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u8BB2\u5B8C\u4E00\u4E2A\u300A\u4E11\u5C0F\u9E2D\u300B\u3002 +\u7834\u58F3 \u2192 \u88AB\u5632\u7B11 \u2192 \u4EF0\u671B\u5929\u9E45 \u2192 \u71AC\u8FC7\u5BD2\u51AC \u2192 \u6C34\u4E2D\u5012\u5F71 \u2192 \u5C55\u7FC5\u9AD8\u98DE\u3002 +\u4E0D\u6015\u98CE\u96EA\uFF0C\u4E5F\u4E0D\u6015\u5632\u7B11\uFF0C\u603B\u6709\u4E00\u5929\uFF0C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE"], + tip: "\u56FE\u6587\u6A21\u5F0F\u4E0A\u4F20\u5B9E\u51B5\u56FE\uFF1B\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + } + ]; + + // src/films/duckling/index.ts + var tl = timeline_default; + var at = (id, dt) => sceneAt(tl, id, dt); + var duckling = { + id: "duckling", + title: "\u4E11\u5C0F\u9E2D", + tagline: "\u53D8\u6210\u767D\u5929\u9E45", + intro: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u513F\u7AE5\u52A8\u753B\u3002\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\uFF0C\u50CF\u5C0F\u5B66\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE\u90A3\u6837\uFF0C\u4E00\u7B14\u4E00\u753B\u5728\u4F60\u7684\u6D4F\u89C8\u5668\u91CC\u5B9E\u65F6\u753B\u51FA\u6765\uFF1B\u65C1\u767D\u3001\u539F\u521B\u56FD\u98CE\u914D\u4E50\u4E0E\u97F3\u6548\u540C\u6B65\u64AD\u653E\u3002", + credit: "\u6545\u4E8B\u6539\u7F16\u81EA\u5B89\u5F92\u751F\u300A\u4E11\u5C0F\u9E2D\u300B\uFF081843\uFF0C\u516C\u6709\u9886\u57DF\uFF09", + assets: "film/", + slug: "ugly-duckling", + timeline: tl, + scenes: SCENES2, + seal: ["\u7AE5\u8BDD"], + series: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u4E11\u5C0F\u9E2D", + score, + effects, + covers: [ + { id: "reflect", name: "\u5012\u5F71\u63ED\u6653", t: at("reflection", 7.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u539F\u6765\uFF0C\u4ED6\u662F\u4E00\u53EA\u767D\u5929\u9E45", side: "left", vertical: true }, + { id: "hatch", name: "\u7834\u58F3\u800C\u51FA", t: at("bigegg", 4.4), title: "\u4E11\u5C0F\u9E2D", sub: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, + { id: "fly", name: "\u5C55\u7FC5\u9AD8\u98DE", t: at("fly", 4.2), title: "\u76F8\u4FE1\u81EA\u5DF1", sub: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180", side: "right", vertical: true }, + { id: "autumn", name: "\u79CB\u65E5\u4EF0\u671B", t: at("autumn", 6.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", side: "left", vertical: true } + ], + live: live_default, + platforms: PLATFORMS, + photoPosts: PHOTO_POSTS, + qa: qa_default, + making: [ + ["\u753B\u9762", "\u5168\u90E8\u7531\u4EE3\u7801\u9010\u5E27\u7ED8\u5236\uFF1A\u7A0B\u5E8F\u751F\u6210\u5BA3\u7EB8\u7EB9\u7406\uFF0C\u53D8\u5BBD\u6BDB\u7B14\u7B14\u89E6\u5E26\u8D77\u7B14\u3001\u6536\u7B14\u548C\u98DE\u767D\uFF0C\u6DE1\u5F69\u6655\u67D3\u6709\u6C34\u5F69\u79EF\u8FB9\uFF0C\u518D\u76D6\u6731\u7802\u5370\u7AE0\u3002\u540C\u4E00\u53EA\u300C\u9E1F\u300D\u53EF\u4EE5\u4ECE\u5C0F\u9EC4\u9E2D\u4E00\u8DEF\u957F\u6210\u5929\u9E45\u3002\u6BCF\u5E27\u53EA\u7531\u65F6\u95F4\u51B3\u5B9A\uFF0C\u53EF\u9010\u5E27\u590D\u73B0\u3002"], + ["\u58F0\u97F3", "\u4E2D\u6587\u795E\u7ECF\u7F51\u7EDC\u8BED\u97F3\u65C1\u767D\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF08\u53E4\u7B5D\u62E8\u5F26\u3001\u7AF9\u7B1B\u3001\u94FA\u5E95\u3001\u94C3\uFF09\uFF0C\u968F\u56DB\u5B63\u6362\u60C5\u7EEA\uFF1B\u7834\u58F3\u3001\u9E2D\u53EB\u3001\u67AA\u58F0\u3001\u51B0\u88C2\u3001\u632F\u7FC5\u3001\u62CD\u624B\u7B49\u97F3\u6548\u90FD\u662F\u7A0B\u5E8F\u5408\u6210\u3002\u4EBA\u58F0\u51FA\u73B0\u65F6\u914D\u4E50\u81EA\u52A8\u538B\u4F4E\uFF0C\u6574\u4F53\u54CD\u5EA6 \u221214 LUFS\u3002"], + ["\u6D41\u7A0B", "\u6309 vibe-video \u6D41\u7A0B\uFF1A\u5206\u955C storyboard \u2192 \u914D\u97F3\u751F\u6210\u65F6\u95F4\u8F74 \u2192 \u63A5\u89E6\u8868 / \u9759\u5E27\u68C0\u67E5 \u2192 \u786E\u5B9A\u6027\u81EA\u68C0 \u2192 \u5168\u7247\u6E32\u67D3 \u2192 \u6DF7\u97F3 \u2192 vibe qa \u8D28\u68C0 \u2192 \u5C01\u9762\u4E0E\u53D1\u5E03\u5305\uFF1B\u5B9E\u51B5\u56FE\u6309 livecanvas \u6D41\u7A0B\u914D\u5BF9\u5C01\u88C5\u3002"] + ] + }; + + // src/films/wolf/timeline.json + var timeline_default2 = { + title: "\u72FC\u6765\u4E86", + style: "ink-wash textbook illustration", + w: 1920, + h: 1080, + fps: 30, + total: 137.86666666666667, + frames: 4136, + voice: { + n: "zh-CN-XiaoxiaoNeural", + boy: "zh-CN-YunxiaNeural", + farmer: "zh-CN-YunjianNeural" + }, + scenes: [ + { + id: "title", + start: 0, + dur: 6.5, + voice_start: 0.5, + voice_dur: 2.568, + lines: [ + { + who: "n", + text: "\u4F0A\u7D22\u5BD3\u8A00\uFF0C\u72FC\u6765\u4E86\u3002", + t0: 0.5, + dur: 2.568, + subs: [ + { + t0: 0.5, + t1: 1.967, + text: "\u4F0A\u7D22\u5BD3\u8A00", + who: "n" + }, + { + t0: 1.967, + t1: 3.068, + text: "\u72FC\u6765\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 0.5, + t1: 1.967, + text: "\u4F0A\u7D22\u5BD3\u8A00", + who: "n" + }, + { + t0: 1.967, + t1: 3.068, + text: "\u72FC\u6765\u4E86", + who: "n" + } + ] }, - enterAt: [0.5, 0.5] - }, - reflection: { - label: "\u5012\u5F71", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#c9d8d8", 0.18, "#ffffff", 0); - mountains(g2, W2, gy - 60 * u, 120 * u, "#8fa69a", 0.28, 71, { soft: 1 }); - for (let i = 0; i < 7; i++) { - const x = X(-1 + i * 0.33) + hash(i) * 60 * u; - willow(g2, x, gy - 20 * u, (200 + hash(i * 3) * 80) * u, 0, 40 + i, "#8fb86b", 1); - } - band(g2, W2, gy - 25 * u, H2, "#a9bcc0", 0.32, 111, 3 * u); - strokesH(g2, W2, gy, H2, 30, "#5a6e78", 0.28, 29, u); - lotusLeaf(g2, X(-0.9), H2 - 90 * u, 120 * u, 7); - lotusLeaf(g2, X(-0.72), H2 - 30 * u, 90 * u, 8); - lotusLeaf(g2, X(0.92), H2 - 60 * u, 110 * u, 9); + { + id: "village", + start: 6.5, + dur: 10.566666666666666, + voice_start: 7, + voice_dur: 9.168, + lines: [ + { + who: "n", + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50\u3002\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B\uFF0C\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50\uFF0C\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349\u3002", + t0: 7, + dur: 9.168, + subs: [ + { + t0: 7, + t1: 9.096, + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50", + who: "n" + }, + { + t0: 9.096, + t1: 11.715, + text: "\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B", + who: "n" + }, + { + t0: 11.715, + t1: 13.549, + text: "\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50", + who: "n" + }, + { + t0: 13.549, + t1: 16.168, + text: "\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 7, + t1: 9.096, + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50", + who: "n" + }, + { + t0: 9.096, + t1: 11.715, + text: "\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B", + who: "n" + }, + { + t0: 11.715, + t1: 13.549, + text: "\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50", + who: "n" + }, + { + t0: 13.549, + t1: 16.168, + text: "\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349", + who: "n" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - water(g2, 0, W2, gy, H2 - gy, t, 7, { alpha: 0.22, lines: 10, speed: 3 }); - const px = X(-0.16), py = gy + 175 * u, ps = 200 * k; - const R = CUE.reflection; - const reveal = smooth(inv(R.reveal, R.reveal + 1.6, t)); - const bow = smooth(inv(R.bow, R.bow + 1, t)) * (1 - smooth(inv(R.proud - 0.4, R.proud + 0.8, t))); - const proud = smooth(inv(R.proud, R.proud + 1.2, t)); - const land = inv(0, R.land, t); - const body = mixHex("#aaa69e", WHITE, reveal); - const o = { x: px, y: py, s: ps, t, sw: 1, body, beak: mixHex("#b88a5c", SWAN_BEAK, reveal), seed: 7, face: 1, neckUp: -0.95 * bow + 0.25 * proud, glow: reveal * (1 - smooth(inv(R.reveal + 2, R.reveal + 4, t))), noBlink: bow > 0.5 }; - const refl = (c2) => bird(c2, { ...o, body: WHITE, beak: SWAN_BEAK, swim: true, glow: 0 }); - if (land >= 1) { - const mirror = smooth(inv(R.mirror, R.mirror + 1.2, t)); - reflection(g2, refl, { x: px - ps * 1.3, y: py - ps * 1.4, w: ps * 2.8, h: ps * 1.46 }, py + ps * 0.06, t, lerp(0.3, 0.95, mirror), "#7a90a0"); - if (mirror > 0 && mirror < 1) blot(g2, px + 60 * u, py + 140 * u, 220 * u, "#fff4d8", 0.35 * Math.sin(Math.PI * mirror)); - if (reveal > 0 && reveal < 1) blot(g2, px + 30 * u, py - 60 * u, 320 * u, "#fff4d8", 0.55 * Math.sin(Math.PI * reveal)); - for (let r = 0; r < 3; r++) { - const q2 = inv(R.reveal + r * 0.5, R.reveal + 2.4 + r * 0.5, t); - if (q2 > 0 && q2 < 1) ripplesRing(g2, px + 70 * u, py + 60 * u, 60 * u + q2 * 420 * u, 0.4 * (1 - q2), "#9a7a3a"); + { + id: "bored", + start: 17.0667, + dur: 12.833333333333334, + voice_start: 17.567, + voice_dur: 11.424, + lines: [ + { + who: "n", + text: "\u5C71\u5761\u4E0A\uFF0C\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349\u3002\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B\uFF0C\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349\uFF0C\u89C9\u5F97\u65E0\u804A\u6781\u4E86\u3002\u5FFD\u7136\uFF0C\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C\u3002", + t0: 17.567, + dur: 11.424, + subs: [ + { + t0: 17.567, + t1: 18.403, + text: "\u5C71\u5761\u4E0A", + who: "n" + }, + { + t0: 18.403, + t1: 20.91, + text: "\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349", + who: "n" + }, + { + t0: 20.91, + t1: 23.139, + text: "\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B", + who: "n" + }, + { + t0: 23.139, + t1: 25.09, + text: "\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349", + who: "n" + }, + { + t0: 25.09, + t1: 26.762, + text: "\u89C9\u5F97\u65E0\u804A\u6781\u4E86", + who: "n" + }, + { + t0: 26.762, + t1: 27.319, + text: "\u5FFD\u7136", + who: "n" + }, + { + t0: 27.319, + t1: 28.991, + text: "\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C", + who: "n" + } + ] } - } - for (let i = 0; i < 3; i++) { - const q2 = easeOut(inv(1.2 + i * 0.4, 5.2 + i * 0.4, t)); - const sx = lerp(X(1.3 + i * 0.2), X(0.3 + i * 0.24), q2), sy = gy + (110 + i * 45) * u; - const greet = smooth(inv(R.proud + 0.4 + i * 0.2, R.proud + 1.2 + i * 0.2, t)); - swimBird(g2, swanBird(sx, sy, (150 - i * 12) * k, t + i, { face: -1, seed: 90 + i, neckUp: -0.55 * greet + 0.08 * Math.sin(t + i) })); - } - if (land < 1) { - const lx = lerp(X(-0.65), px, easeOut(land)), ly = lerp(H2 * 0.12, py, easeInOut(land)); - bird(g2, { ...o, x: lx, y: ly, legs: false, wing: 1 - land * 0.3, flap: t * 9, swim: true, neckUp: -0.5 }); - } else { - const settle = inv(R.land, R.land + 0.85, t); - swimBird(g2, { ...o, swim: true, wing: (1 - easeOut(settle)) * 0.7, flap: t * 6 }); - const q2 = t - R.land; - if (q2 < 1) particles(14, 5, (r) => { - const a = -Math.PI * r(), v = (100 + r() * 180) * u; - const x = px + Math.cos(a) * v * q2 * 1.6, y = py + Math.sin(a) * v * q2 * 1.4 + 300 * u * q2 * q2; - g2.fillStyle = rgba("#ffffff", 0.9 * (1 - q2)); - g2.beginPath(); - g2.arc(x, y, (3 + r() * 5) * u, 0, Math.PI * 2); - g2.fill(); - g2.strokeStyle = rgba("#6a7a86", 0.5 * (1 - q2)); - g2.lineWidth = u; - g2.stroke(); - }); - } - particles(10, 61, (r, i) => { - const x = (r() * W2 + t * 14 * u) % W2, y = gy + 40 * u + r() * (H2 - gy - 80 * u); - leaf(g2, x, y, 6 * u, t * 0.3 + i, "#f0a8b4", 0.75); - }); + ], + subs: [ + { + t0: 17.567, + t1: 18.403, + text: "\u5C71\u5761\u4E0A", + who: "n" + }, + { + t0: 18.403, + t1: 20.91, + text: "\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349", + who: "n" + }, + { + t0: 20.91, + t1: 23.139, + text: "\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B", + who: "n" + }, + { + t0: 23.139, + t1: 25.09, + text: "\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349", + who: "n" + }, + { + t0: 25.09, + t1: 26.762, + text: "\u89C9\u5F97\u65E0\u804A\u6781\u4E86", + who: "n" + }, + { + t0: 26.762, + t1: 27.319, + text: "\u5FFD\u7136", + who: "n" + }, + { + t0: 27.319, + t1: 28.991, + text: "\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C", + who: "n" + } + ] }, - cam(s) { - const { t, u, gy } = s; - const zin = easeInOut(inv(CUE.reflection.bow - 0.2, CUE.reflection.mirror + 1.2, t)), zout = easeInOut(inv(CUE.reflection.reveal - 0.6, CUE.reflection.reveal + 2.4, t)); - return cam(s, lerp(lerp(1.25, 1.65, zin), 1.3, zout), lerp(s.X(-0.05), s.X(-0.1), zin), gy + lerp(lerp(60, 230, zin), 110, zout) * u); + { + id: "shout1", + start: 29.9, + dur: 9.3, + voice_start: 30.4, + voice_dur: 7.892, + lines: [ + { + who: "n", + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934\uFF0C\u62E2\u7740\u5634\u5927\u558A\uFF1A", + t0: 30.4, + dur: 3.36, + subs: [ + { + t0: 30.4, + t1: 32.233, + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934", + who: "n" + }, + { + t0: 32.233, + t1: 33.76, + text: "\u62E2\u7740\u5634\u5927\u558A", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u6765\u4E86\uFF01\u5FEB\u6765\u5440\uFF01", + t0: 34.14, + dur: 4.152, + subs: [ + { + t0: 34.14, + t1: 35.524, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 35.524, + t1: 36.908, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 36.908, + t1: 38.292, + text: "\u5FEB\u6765\u5440\uFF01", + who: "boy" + } + ] + } + ], + subs: [ + { + t0: 30.4, + t1: 32.233, + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934", + who: "n" + }, + { + t0: 32.233, + t1: 33.76, + text: "\u62E2\u7740\u5634\u5927\u558A", + who: "n" + }, + { + t0: 34.14, + t1: 35.524, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 35.524, + t1: 36.908, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 36.908, + t1: 38.292, + text: "\u5FEB\u6765\u5440\uFF01", + who: "boy" + } + ] }, - enterAt: [0.3, 0.3] - }, - accepted: { - label: "\u63A5\u7EB3", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#b8d0d8", 0.2, "#ffffff", 0); - mountains(g2, W2, gy - 50 * u, 110 * u, "#88a294", 0.28, 81, { soft: 1 }); - band(g2, W2, gy - 20 * u, H2, "#a3bfc6", 0.34, 121, 3 * u); - strokesH(g2, W2, gy, H2, 30, "#4f6670", 0.28, 33, u); - const bank = [[-50 * u, gy - 60 * u], [X(-0.55), gy - 20 * u], [X(-0.38), gy + 120 * u], [X(-0.5), gy + 260 * u], [-50 * u, gy + 300 * u]]; - wash(g2, bank, "#9fb878", 0.6); - for (let i = 0; i < 10; i++) grass(g2, X(-0.95) + i * 50 * u, gy + 100 * u + hash(i) * 120 * u, 50 * u, 0, 300 + i, "#4f6a3e", 0.6, 0.3); + { + id: "run1", + start: 39.2, + dur: 11.8, + voice_start: 39.7, + voice_dur: 10.412, + lines: [ + { + who: "n", + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86\uFF0C\u625B\u8D77\u9504\u5934\uFF0C\u62FF\u8D77\u6241\u62C5\uFF0C\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1\u3002", + t0: 39.7, + dur: 6.768, + subs: [ + { + t0: 39.7, + t1: 41.956, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 41.956, + t1: 43.084, + text: "\u625B\u8D77\u9504\u5934", + who: "n" + }, + { + t0: 43.084, + t1: 44.212, + text: "\u62FF\u8D77\u6241\u62C5", + who: "n" + }, + { + t0: 44.212, + t1: 46.468, + text: "\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1", + who: "n" + } + ] + }, + { + who: "farmer", + text: "\u72FC\u5728\u54EA\u513F\uFF1F\u72FC\u5728\u54EA\u513F\uFF1F", + t0: 46.848, + dur: 3.264, + subs: [ + { + t0: 46.848, + t1: 48.48, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + }, + { + t0: 48.48, + t1: 50.112, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + } + ] + } + ], + subs: [ + { + t0: 39.7, + t1: 41.956, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 41.956, + t1: 43.084, + text: "\u625B\u8D77\u9504\u5934", + who: "n" + }, + { + t0: 43.084, + t1: 44.212, + text: "\u62FF\u8D77\u6241\u62C5", + who: "n" + }, + { + t0: 44.212, + t1: 46.468, + text: "\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1", + who: "n" + }, + { + t0: 46.848, + t1: 48.48, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + }, + { + t0: 48.48, + t1: 50.112, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - cloud(g2, X(0.3) + t * 10 * u, H2 * 0.14, 380 * u, 9, 0.22); - willow(g2, X(-0.88), gy + 60 * u, 560 * u, t, 8, "#8fb86b", 1); - const cheer = t > CUE.accepted.cheer; - [[-0.82, "#c0504a", true], [-0.66, "#4a6a9a", false], [-0.52, "#d0a040", true]].forEach(([fx, col, girl], i) => { - child(g2, X(fx), gy + 110 * u + i * 12 * u, 270 * k, t + i, { face: 1, dress: col, girl, pose: cheer ? i === 1 ? "shoo" : "clap" : "stand", seed: 10 + i }); - if (cheer) soundMarks(g2, X(fx) + 50 * u, gy - 200 * u * k, 40 * u, 0.8 * (1 - smooth(inv(10, 11, t))), i); - }); - const hx = X(0.62), hy = gy + 70 * u; - const heart = smooth(inv(CUE.accepted.heart, CUE.accepted.heart + 2, t)); - swimBird(g2, swanBird(hx - 70 * u, hy, 115 * k, t, { face: 1, seed: 95, neckUp: -0.55 * heart })); - swimBird(g2, swanBird(hx + 70 * u, hy, 115 * k, t + 1, { face: -1, seed: 96, neckUp: -0.55 * heart })); - const px = X(0.12), py = gy + 210 * u; - const nuzzle = (ph) => Math.max(0, Math.sin(t * 1.6 + ph)) * smooth(inv(0.5, 1.5, t)); - swimBird(g2, swanBird(px - 190 * u, py - 30 * u, 165 * k, t + 2, { face: 1, seed: 91, neckUp: -0.7 * nuzzle(0) })); - swimBird(g2, swanBird(px, py, 200 * k, t, { face: 1, seed: 7, neckUp: 0.2 + 0.1 * Math.sin(t), glow: 0.2 })); - swimBird(g2, swanBird(px + 220 * u, py - 20 * u, 165 * k, t + 3, { face: -1, seed: 92, neckUp: -0.7 * nuzzle(2) })); - particles(12, 71, (r, i) => { - const x = (r() * W2 + t * 18 * u) % W2, y = gy + 30 * u + r() * (H2 - gy - 60 * u); - leaf(g2, x, y, 6 * u, t * 0.4 + i, "#f0a8b4", 0.75); - }); + { + id: "laugh1", + start: 51, + dur: 9.4, + voice_start: 51.5, + voice_dur: 7.988, + lines: [ + { + who: "boy", + text: "\u54C8\u54C8\u54C8\uFF0C\u54EA\u6709\u4EC0\u4E48\u72FC\uFF0C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + t0: 51.5, + dur: 3.816, + subs: [ + { + t0: 51.5, + t1: 52.215, + text: "\u54C8\u54C8\u54C8", + who: "boy" + }, + { + t0: 52.215, + t1: 53.408, + text: "\u54EA\u6709\u4EC0\u4E48\u72FC", + who: "boy" + }, + { + t0: 53.408, + t1: 55.316, + text: "\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + who: "boy" + } + ] + }, + { + who: "n", + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934\uFF0C\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86\u3002", + t0: 55.696, + dur: 3.792, + subs: [ + { + t0: 55.696, + t1: 57.863, + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934", + who: "n" + }, + { + t0: 57.863, + t1: 59.488, + text: "\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 51.5, + t1: 52.215, + text: "\u54C8\u54C8\u54C8", + who: "boy" + }, + { + t0: 52.215, + t1: 53.408, + text: "\u54EA\u6709\u4EC0\u4E48\u72FC", + who: "boy" + }, + { + t0: 53.408, + t1: 55.316, + text: "\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + who: "boy" + }, + { + t0: 55.696, + t1: 57.863, + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934", + who: "n" + }, + { + t0: 57.863, + t1: 59.488, + text: "\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86", + who: "n" + } + ] }, - cam(s) { - return cam(s, lerp(1.32, 1.18, easeInOut(s.t / s.D)), s.X(-0.12), s.gy + 110 * s.u); + { + id: "shout2", + start: 60.4, + dur: 10.9, + voice_start: 60.9, + voice_dur: 9.496, + lines: [ + { + who: "n", + text: "\u8FC7\u4E86\u51E0\u5929\uFF0C\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765\uFF1A", + t0: 60.9, + dur: 3.216, + subs: [ + { + t0: 60.9, + t1: 62.069, + text: "\u8FC7\u4E86\u51E0\u5929", + who: "n" + }, + { + t0: 62.069, + t1: 64.116, + text: "\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u6765\u4E86\uFF01", + t0: 64.496, + dur: 2.904, + subs: [ + { + t0: 64.496, + t1: 65.948, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 65.948, + t1: 67.4, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + } + ] + }, + { + who: "n", + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71\u3002", + t0: 67.78, + dur: 2.616, + subs: [ + { + t0: 67.78, + t1: 70.396, + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 60.9, + t1: 62.069, + text: "\u8FC7\u4E86\u51E0\u5929", + who: "n" + }, + { + t0: 62.069, + t1: 64.116, + text: "\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765", + who: "n" + }, + { + t0: 64.496, + t1: 65.948, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 65.948, + t1: 67.4, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 67.78, + t1: 70.396, + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71", + who: "n" + } + ] }, - enterAt: [0.2, 0.5] - }, - fly: { - label: "\u9AD8\u98DE", - bg(s) { - const { g: g2, W: W2, H: H2, u, X, portrait } = s; - sky(g2, W2, H2, "#a8c4d4", 0.32, "#f2ead8", 0); - sun(g2, X(0.62), H2 * (portrait ? 0.14 : 0.18), 60 * u, "#d65a40", 0.75); - mountains(g2, W2, H2 * 0.92, 300 * u, "#7d8f8a", 0.35, 91); - mountains(g2, W2, H2 * 1.02, 260 * u, "#4f5d58", 0.5, 97); + { + id: "warn", + start: 71.3, + dur: 8, + voice_start: 71.8, + voice_dur: 6.596, + lines: [ + { + who: "boy", + text: "\u54C8\u54C8\uFF0C\u53C8\u4E0A\u5F53\u5566\uFF01", + t0: 71.8, + dur: 2.352, + subs: [ + { + t0: 71.8, + t1: 72.472, + text: "\u54C8\u54C8", + who: "boy" + }, + { + t0: 72.472, + t1: 74.152, + text: "\u53C8\u4E0A\u5F53\u5566\uFF01", + who: "boy" + } + ] + }, + { + who: "farmer", + text: "\u4F60\u518D\u8BF4\u8C0E\uFF0C\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + t0: 74.532, + dur: 3.864, + subs: [ + { + t0: 74.532, + t1: 75.636, + text: "\u4F60\u518D\u8BF4\u8C0E", + who: "farmer" + }, + { + t0: 75.636, + t1: 78.396, + text: "\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + who: "farmer" + } + ] + } + ], + subs: [ + { + t0: 71.8, + t1: 72.472, + text: "\u54C8\u54C8", + who: "boy" + }, + { + t0: 72.472, + t1: 74.152, + text: "\u53C8\u4E0A\u5F53\u5566\uFF01", + who: "boy" + }, + { + t0: 74.532, + t1: 75.636, + text: "\u4F60\u518D\u8BF4\u8C0E", + who: "farmer" + }, + { + t0: 75.636, + t1: 78.396, + text: "\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + who: "farmer" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, X, portrait } = s; - const k = ks(s); - particles(7, 21, (r, i) => { - const y = H2 * (0.15 + r() * 0.6), w = (260 + r() * 360) * u, sp2 = (90 + r() * 160) * u; - const x = ((r() * (W2 + w * 2) - t * sp2) % (W2 + w * 2) + W2 + w * 2) % (W2 + w * 2) - w; - cloud(g2, x, y, w, 30 + i, 0.25 + r() * 0.15); - }); - const enter = easeOut(inv(0, 2.2, t)); - const cx = lerp(-W2 * 0.4, X(portrait ? 0.05 : 0.1), enter) + Math.sin(t * 0.9) * 20 * u; - const cy = H2 * (portrait ? 0.42 : 0.44) + Math.sin(t * 1.2) * 18 * u; - const sp = portrait ? 0.62 : 1; - const flock = [[-250, 110, 0.78], [-470, 210, 0.66], [-230, -120, 0.74], [-440, -210, 0.6]]; - flock.forEach(([dx, dy, sc], i) => bird(g2, { x: cx + dx * u * sp, y: cy + dy * u * sp, s: 230 * k * sc, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.3 + i * 0.8, neckUp: -0.85, seed: 80 + i })); - bird(g2, { x: cx, y: cy, s: 240 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.2, neckUp: -0.85, seed: 7, glow: 0.3 }); + { + id: "wolf", + start: 79.3, + dur: 12, + voice_start: 79.8, + voice_dur: 10.584, + lines: [ + { + who: "n", + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50\uFF0C\u4E4C\u4E91\u538B\u7740\u5C71\u5934\u3002\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B\uFF0C\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4\u3002\u72FC\uFF0C\u771F\u7684\u6765\u4E86\uFF01", + t0: 79.8, + dur: 10.584, + subs: [ + { + t0: 79.8, + t1: 81.564, + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50", + who: "n" + }, + { + t0: 81.564, + t1: 83.328, + text: "\u4E4C\u4E91\u538B\u7740\u5C71\u5934", + who: "n" + }, + { + t0: 83.328, + t1: 84.21, + text: "\u6811\u4E1B\u91CC", + who: "n" + }, + { + t0: 84.21, + t1: 86.562, + text: "\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B", + who: "n" + }, + { + t0: 86.562, + t1: 88.62, + text: "\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4", + who: "n" + }, + { + t0: 88.62, + t1: 88.914, + text: "\u72FC", + who: "n" + }, + { + t0: 88.914, + t1: 90.384, + text: "\u771F\u7684\u6765\u4E86\uFF01", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 79.8, + t1: 81.564, + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50", + who: "n" + }, + { + t0: 81.564, + t1: 83.328, + text: "\u4E4C\u4E91\u538B\u7740\u5C71\u5934", + who: "n" + }, + { + t0: 83.328, + t1: 84.21, + text: "\u6811\u4E1B\u91CC", + who: "n" + }, + { + t0: 84.21, + t1: 86.562, + text: "\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B", + who: "n" + }, + { + t0: 86.562, + t1: 88.62, + text: "\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4", + who: "n" + }, + { + t0: 88.62, + t1: 88.914, + text: "\u72FC", + who: "n" + }, + { + t0: 88.914, + t1: 90.384, + text: "\u771F\u7684\u6765\u4E86\uFF01", + who: "n" + } + ] }, - cam(s) { - return { z: lerp(1, 1.08, s.t / s.D), x: s.W / 2, y: s.H / 2 }; + { + id: "cry", + start: 91.3, + dur: 9.4, + voice_start: 91.8, + voice_dur: 7.988, + lines: [ + { + who: "n", + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86\uFF0C\u62FC\u547D\u5730\u558A\uFF1A", + t0: 91.8, + dur: 3.192, + subs: [ + { + t0: 91.8, + t1: 93.715, + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86", + who: "n" + }, + { + t0: 93.715, + t1: 94.992, + text: "\u62FC\u547D\u5730\u558A", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u771F\u7684\u6765\u4E86\uFF01\u6551\u547D\u554A\uFF01", + t0: 95.372, + dur: 4.416, + subs: [ + { + t0: 95.372, + t1: 96.634, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 96.634, + t1: 98.526, + text: "\u72FC\u771F\u7684\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 98.526, + t1: 99.788, + text: "\u6551\u547D\u554A\uFF01", + who: "boy" + } + ] + } + ], + subs: [ + { + t0: 91.8, + t1: 93.715, + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86", + who: "n" + }, + { + t0: 93.715, + t1: 94.992, + text: "\u62FC\u547D\u5730\u558A", + who: "n" + }, + { + t0: 95.372, + t1: 96.634, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 96.634, + t1: 98.526, + text: "\u72FC\u771F\u7684\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 98.526, + t1: 99.788, + text: "\u6551\u547D\u554A\uFF01", + who: "boy" + } + ] }, - enterAt: [0.1, 0.5] - }, - ending: { - label: "\u5BC4\u8BED", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#e8a070", 0.3, "#f0c090", 0.35); - sun(g2, X(0.05), gy - 130 * u, 115 * u, "#c8463a", 0.9); - mountains(g2, W2, gy - 20 * u, 200 * u, "#6a5048", 0.42, 101); - mountains(g2, W2, gy + 10 * u, 120 * u, "#3a2c28", 0.6, 107); - band(g2, W2, gy + 5 * u, H2, "#d8a878", 0.3, 131, 3 * u); - particles(14, 9, (r) => { - const y = gy + 20 * u + r() * 200 * u, w = (130 - (y - gy) / u * 0.4) * u; - brush(g2, [[X(0.05) - w, y], [X(0.05), y + 2 * u], [X(0.05) + w, y]], 6 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); - }); - for (let i = 0; i < 8; i++) reed(g2, X(-0.98) + i * 34 * u, H2 + 10 * u, (300 + hash(i) * 200) * u, 0, 800 + i, { color: "#2a2220", alpha: 0.85 }); - for (let i = 0; i < 6; i++) reed(g2, X(0.86) + i * 30 * u, H2 + 10 * u, (240 + hash(i + 9) * 160) * u, 0, 820 + i, { color: "#2a2220", alpha: 0.85 }); + { + id: "noone", + start: 100.7, + dur: 11.166666666666666, + voice_start: 101.2, + voice_dur: 9.76, + lines: [ + { + who: "n", + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86\uFF0C\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E\u3002", + t0: 101.2, + dur: 4.296, + subs: [ + { + t0: 101.2, + t1: 103.348, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 103.348, + t1: 105.496, + text: "\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E", + who: "n" + } + ] + }, + { + who: "farmer", + text: "\u54FC\uFF0C\u53C8\u6765\u9A97\u4EBA\u3002", + t0: 105.876, + dur: 2.664, + subs: [ + { + t0: 105.876, + t1: 106.409, + text: "\u54FC", + who: "farmer" + }, + { + t0: 106.409, + t1: 108.54, + text: "\u53C8\u6765\u9A97\u4EBA", + who: "farmer" + } + ] + }, + { + who: "n", + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71\u3002", + t0: 108.92, + dur: 2.04, + subs: [ + { + t0: 108.92, + t1: 110.96, + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 101.2, + t1: 103.348, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 103.348, + t1: 105.496, + text: "\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E", + who: "n" + }, + { + t0: 105.876, + t1: 106.409, + text: "\u54FC", + who: "farmer" + }, + { + t0: 106.409, + t1: 108.54, + text: "\u53C8\u6765\u9A97\u4EBA", + who: "farmer" + }, + { + t0: 108.92, + t1: 110.96, + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71", + who: "n" + } + ] }, - draw(s) { - const { g: g2, H: H2, u, t, gy, X, portrait } = s; - for (let i = 0; i < 5; i++) { - const q2 = inv(0, 10, t); - const x0 = X(-0.75 + i * 0.07), y0 = H2 * (0.3 + 0.03 * i); - const tx = X(0.05) + i * 12 * u, ty = gy - 160 * u - i * 8 * u; - const x = lerp(x0, tx, q2 * 0.85), y = lerp(y0, ty, q2 * 0.85); - farBird(g2, x, y, lerp(28, 9, q2) * u, t * 7 + i * 1.3, "#2a2220", 0.9); - } - const fs = (portrait ? 150 : 130) * u; - const vx = portrait ? X(0.62) : X(0.72), vy = portrait ? H2 * 0.3 : H2 * 0.36; - writeText(g2, "\u76F8\u4FE1\u81EA\u5DF1", vx, vy, fs, 1.4, 0.55, t, { vertical: true, gap: 1.02 }); - const E = CUE.ending; - const ea = smooth(inv(E.end - 0.3, E.end + 0.3, t)); - if (ea > 0) { - g2.save(); - g2.globalAlpha = ea; - g2.font = `${40 * u}px ${FONT_KAI}`; - g2.fillStyle = rgba(INK, 0.85); - g2.textAlign = "center"; - g2.save(); - g2.translate(vx - fs * 0.95, vy - fs * 1.45); - [..."\u4E11\u5C0F\u9E2D \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); - g2.restore(); - g2.restore(); - } - const sp = inv(E.end + 0.1, E.end + 0.35, t); - if (sp > 0) { - g2.save(); - g2.translate(vx - fs * 0.95, vy + fs * 1.2); - g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); - seal(g2, 0, 0, 80 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); - g2.restore(); - } + { + id: "chaos", + start: 111.8667, + dur: 8, + voice_start: 112.367, + voice_dur: 5.976, + lines: [ + { + who: "n", + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4\uFF0C\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1\uFF0C\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86\u3002", + t0: 112.367, + dur: 5.976, + subs: [ + { + t0: 112.367, + t1: 113.725, + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4", + who: "n" + }, + { + t0: 113.725, + t1: 115.898, + text: "\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1", + who: "n" + }, + { + t0: 115.898, + t1: 118.343, + text: "\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 112.367, + t1: 113.725, + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4", + who: "n" + }, + { + t0: 113.725, + t1: 115.898, + text: "\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1", + who: "n" + }, + { + t0: 115.898, + t1: 118.343, + text: "\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86", + who: "n" + } + ] }, - cam(s) { - return { z: lerp(1, 1.05, s.t / s.D), x: s.W / 2, y: s.gy }; + { + id: "regret", + start: 119.8667, + dur: 8, + voice_start: 120.367, + voice_dur: 5.328, + lines: [ + { + who: "n", + text: "\u592A\u9633\u843D\u5C71\u4E86\uFF0C\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A\uFF0C\u4F24\u5FC3\u5730\u54ED\u4E86\u3002", + t0: 120.367, + dur: 5.328, + subs: [ + { + t0: 120.367, + t1: 121.699, + text: "\u592A\u9633\u843D\u5C71\u4E86", + who: "n" + }, + { + t0: 121.699, + t1: 124.363, + text: "\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A", + who: "n" + }, + { + t0: 124.363, + t1: 125.695, + text: "\u4F24\u5FC3\u5730\u54ED\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 120.367, + t1: 121.699, + text: "\u592A\u9633\u843D\u5C71\u4E86", + who: "n" + }, + { + t0: 121.699, + t1: 124.363, + text: "\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A", + who: "n" + }, + { + t0: 124.363, + t1: 125.695, + text: "\u4F24\u5FC3\u5730\u54ED\u4E86", + who: "n" + } + ] }, - enterAt: [0.5, 0.4] - } - }; - - // src/film/film.ts - var TL = timeline_default; - var TOTAL = TL.total; - var bgCache = /* @__PURE__ */ new Map(); - function cachedBg(id, W2, H2, draw) { - const k = `${id}:${W2}x${H2}`; - let c2 = bgCache.get(k); - if (c2) { - bgCache.delete(k); - bgCache.set(k, c2); - } - if (!c2) { - c2 = makeCanvas(W2, H2); - const g2 = ctx2d(c2); - draw(g2, W2, H2); - bgCache.set(k, c2); - if (bgCache.size > 8) bgCache.delete(bgCache.keys().next().value); - } - return c2; - } - function sceneIndexAt(T) { - const s = TL.scenes; - for (let i = s.length - 1; i >= 0; i--) if (T >= s[i].start) return i; - return 0; - } - function drawScene(g2, W2, H2, i, T) { - const tl = TL.scenes[i]; - const def = SCENES[tl.id]; - const u = Math.min(W2, H2) / 1080; - const portrait = H2 > W2 * 1.05; - const t = T - tl.start; - const sc = { g: g2, W: W2, H: H2, u, t, D: tl.dur, T, portrait, gy: H2 * (portrait ? 0.62 : 0.6), X: (f) => W2 / 2 + f * Math.min(W2 / 2, H2 * 0.85) }; - g2.drawImage(paper(W2, H2), 0, 0); - g2.save(); - const cam2 = def.cam ? def.cam(sc) : { z: 1, x: W2 / 2, y: H2 / 2, dx: 0, dy: 0 }; - const z = Math.max(1, cam2.z); - const cx = Math.min(Math.max(cam2.x, W2 / (2 * z)), W2 - W2 / (2 * z)); - const cy = Math.min(Math.max(cam2.y, H2 / (2 * z)), H2 - H2 / (2 * z)); - g2.translate(W2 / 2 + (cam2.dx ?? 0), H2 / 2 + (cam2.dy ?? 0)); - g2.scale(z, z); - g2.translate(-cx, -cy); - if (def.bg) g2.drawImage(cachedBg(tl.id, W2, H2, (bg, w, h) => def.bg({ ...sc, g: bg, W: w, H: h })), 0, 0); - def.draw(sc); - g2.restore(); - if (def.overlay) def.overlay(sc); - } - var TR = 1.1; - function renderFrame(g2, W2, H2, T, opts = {}) { - T = clamp(T, 0, TOTAL - 1e-4); - const i = sceneIndexAt(T); - const tl = TL.scenes[i]; - const lt = T - tl.start; - g2.save(); - g2.setTransform(1, 0, 0, 1, 0, 0); - g2.globalAlpha = 1; - g2.globalCompositeOperation = "source-over"; - if (i > 0 && lt < TR) { - drawScene(g2, W2, H2, i - 1, T); - const p = easeInOut(lt / TR); - const R = Math.hypot(W2, H2) * 0.62 * p / 0.8 + 1; - const def = SCENES[tl.id]; - const fx = (def.enterAt?.[0] ?? 0.5) * W2, fy = (def.enterAt?.[1] ?? 0.5) * H2; - const loop = spline(blobLoop(fx, fy, R, i * 17 + 3, 0.16, 26), 4, true); - g2.save(); - tracePath(g2, loop, true); - g2.clip(); - drawScene(g2, W2, H2, i, T); - g2.restore(); - g2.save(); - tracePath(g2, loop, true); - g2.strokeStyle = rgba(INK, 0.16 * (1 - p)); - g2.lineWidth = Math.min(W2, H2) * 0.02 * (1 - p) + 1; - g2.stroke(); - g2.strokeStyle = rgba("#7a6a50", 0.12 * (1 - p)); - g2.lineWidth = Math.min(W2, H2) * 0.05 * (1 - p) + 1; - g2.stroke(); - g2.restore(); - } else { - drawScene(g2, W2, H2, i, T); - } - if (opts.subs !== false) drawSubs(g2, W2, H2, T); - if (opts.fadeEnd !== false) { - const f = smooth(inv(TOTAL - 1.2, TOTAL - 0.1, T)); - if (f > 0) { - g2.globalAlpha = f; - g2.drawImage(paper(W2, H2), 0, 0); - g2.globalAlpha = 1; - } - } - g2.restore(); - } - function currentSub(T) { - const i = sceneIndexAt(T); - const subs = TL.scenes[i].subs ?? []; - for (let k = 0; k < subs.length; k++) { - const s = subs[k]; - const end = k + 1 < subs.length ? subs[k + 1].t0 : s.t1 + 0.35; - if (T >= s.t0 - 0.05 && T < end) { - const a = Math.min(smooth((T - s.t0 + 0.05) / 0.18), smooth((end - T) / 0.15)); - return { text: s.text, a }; + { + id: "moral", + start: 127.8667, + dur: 10, + voice_start: 128.367, + voice_dur: 7.224, + lines: [ + { + who: "n", + text: "\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002", + t0: 128.367, + dur: 7.224, + subs: [ + { + t0: 128.367, + t1: 129.437, + text: "\u8BF4\u8C0E\u7684\u4EBA", + who: "n" + }, + { + t0: 129.437, + t1: 131.042, + text: "\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD", + who: "n" + }, + { + t0: 131.042, + t1: 133.183, + text: "\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6", + who: "n" + }, + { + t0: 133.183, + t1: 135.591, + text: "\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 128.367, + t1: 129.437, + text: "\u8BF4\u8C0E\u7684\u4EBA", + who: "n" + }, + { + t0: 129.437, + t1: 131.042, + text: "\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD", + who: "n" + }, + { + t0: 131.042, + t1: 133.183, + text: "\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6", + who: "n" + }, + { + t0: 133.183, + t1: 135.591, + text: "\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427", + who: "n" + } + ] } + ] + }; + + // src/films/wolf/live.json + var live_default2 = [ + { id: "01-idea", scene: "bored", from: 9.4, dur: 3, cover: 2, caption: "\u574F\u4E3B\u610F", index: "\u5176\u4E00", xhs: "\u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u653E\u7F8A\u5A03\u773C\u73E0\u4E00\u8F6C\u3002" }, + { id: "02-shout", scene: "shout1", from: 4, dur: 3, cover: 1.2, caption: "\u72FC\u6765\u4E86", index: "\u5176\u4E8C", xhs: "\u8DF3\u4E0A\u77F3\u5934\u5927\u558A\uFF0C\u6731\u7EA2\u5927\u5B57\u98DE\u5411\u5C71\u4E0B\u3002" }, + { id: "03-run", scene: "run1", from: 5, dur: 3, cover: 1.5, caption: "\u6025\u6025\u5FD9\u5FD9", index: "\u5176\u4E09", xhs: "\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71\u3002" }, + { id: "04-laugh", scene: "laugh1", from: 0.6, dur: 3, cover: 1, caption: "\u54C8\u54C8\u5927\u7B11", index: "\u5176\u56DB", xhs: "\u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D" }, + { id: "05-eyes", scene: "wolf", from: 6.4, dur: 3, cover: 2.2, caption: "\u771F\u7684\u6765\u4E86", index: "\u5176\u4E94", xhs: "\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B\u3002" }, + { id: "06-regret", scene: "regret", from: 4, dur: 3, cover: 1.5, caption: "\u540E\u6094", index: "\u5176\u516D", xhs: "\u5915\u9633\u4E0B\uFF0C\u4ED6\u62B1\u7740\u5C0F\u7F8A\u54ED\u4E86\u3002" } + ]; + + // src/films/wolf/qa.json + var qa_default2 = { status: "pending", results: [] }; + + // src/films/wolf/actors.ts + var actors_exports2 = {}; + __export(actors_exports2, { + POSE: () => POSE, + bigTree: () => bigTree, + bush: () => bush, + chimneySmoke: () => chimneySmoke, + cottage: () => cottage, + eyesInDark: () => eyesInDark, + flowers: () => flowers, + person: () => person, + rock: () => rock, + sheep: () => sheep, + shoutText: () => shoutText, + shoutWaves: () => shoutWaves, + stormCloud: () => stormCloud, + terraces: () => terraces, + wolf: () => wolf + }); + var lp2 = (a, b, t) => [lerp(a[0], b[0], t), lerp(a[1], b[1], t)]; + var add2 = (a, b) => [a[0] + b[0], a[1] + b[1]]; + var rot = (p, c2, a) => { + const dx = p[0] - c2[0], dy = p[1] - c2[1], co = Math.cos(a), si = Math.sin(a); + return [c2[0] + dx * co - dy * si, c2[1] + dx * si + dy * co]; + }; + function ik(a, c2, l1, l2, bend) { + let dx = c2[0] - a[0], dy = c2[1] - a[1]; + let d = Math.hypot(dx, dy); + if (d > l1 + l2 - 1e-4) { + const k = (l1 + l2 - 1e-4) / d; + dx *= k; + dy *= k; + d = l1 + l2 - 1e-4; + c2 = [a[0] + dx, a[1] + dy]; } - return null; + if (d < 1e-4) return [[a[0] + l1, a[1]], c2]; + const cosA = clamp((l1 * l1 + d * d - l2 * l2) / (2 * l1 * d), -1, 1); + const ang = Math.atan2(dy, dx) + bend * Math.acos(cosA); + return [[a[0] + Math.cos(ang) * l1, a[1] + Math.sin(ang) * l1], c2]; } - function drawSubs(g2, W2, H2, T) { - const s = currentSub(T); - if (!s || s.a <= 0) return; - const portrait = H2 > W2 * 1.05; - const u = Math.min(W2, H2) / 1080; - const fs = (portrait ? 58 : 50) * u; - const y = portrait ? H2 * 0.785 : H2 - 86 * u; - g2.save(); - g2.globalAlpha = s.a; - g2.font = `${fs}px ${FONT_KAI}`; - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const w = g2.measureText(s.text).width; - const rx = w / 2 + fs * 2.2, ry = fs * 0.95; - g2.save(); - g2.translate(W2 / 2, y); - g2.scale(rx / ry, 1); - const gr = g2.createRadialGradient(0, 0, 0, 0, 0, ry); - gr.addColorStop(0, rgba(PAPER, 0.95)); - gr.addColorStop(0.72, rgba(PAPER, 0.9)); - gr.addColorStop(1, rgba(PAPER, 0)); - g2.fillStyle = gr; - g2.fillRect(-ry, -ry, ry * 2, ry * 2); - g2.restore(); + function limb(g2, pts, w, col, iw, seed, endCol, split = 0) { + brush(g2, pts, w + iw * 2.2, { color: INK, alpha: 0.82, taperIn: 0.02, taperOut: 0.04, jitter: 0.12, seed }); + g2.lineCap = "round"; g2.lineJoin = "round"; - g2.strokeStyle = rgba(PAPER, 0.96); - g2.lineWidth = fs * 0.2; - g2.strokeText(s.text, W2 / 2, y); - g2.fillStyle = rgba(INK, 0.97); - g2.fillText(s.text, W2 / 2, y); - g2.restore(); - } - - // src/film/audio.ts - var SR = 44100; - var midi = (m) => 440 * Math.pow(2, (m - 69) / 12); - var N = { E2: 40, G2: 43, A2: 45, B2: 47, D3: 50, E3: 52, G3: 55, A3: 57, B3: 59, D4: 62, E4: 64, G4: 67, A4: 69, B4: 71, D5: 74, E5: 76, G5: 79, A5: 81, B5: 83, D6: 86, E6: 88 }; - var sceneStart = (id) => TL.scenes.find((s) => s.id === id)?.start ?? 0; - var sceneDur = (id) => TL.scenes.find((s) => s.id === id)?.dur ?? 0; - var Mixer = class { - ctx; - music; - sfx; - voice; - rev; - revIn; - master; - noise; - ksCache = /* @__PURE__ */ new Map(); - r = rng(4242); - q = []; - constructor(ctx) { - this.ctx = ctx; - const self = this; - for (const k of ["flute", "pad", "bell", "block", "noiseHit", "tone", "wind", "quack", "swanCall"]) { - const orig = self[k].bind(this); - self[k] = (...a) => { - this.q.push({ t: a[0], fn: () => orig(...a) }); - }; - } - this.master = ctx.createGain(); - this.master.gain.value = 0.9; - const comp = ctx.createDynamicsCompressor(); - comp.threshold.value = -10; - comp.knee.value = 6; - comp.ratio.value = 6; - comp.attack.value = 4e-3; - comp.release.value = 0.2; - this.master.connect(comp); - comp.connect(ctx.destination); - this.music = ctx.createGain(); - this.music.connect(this.master); - this.sfx = ctx.createGain(); - this.sfx.gain.value = 0.9; - this.sfx.connect(this.master); - this.voice = ctx.createGain(); - this.voice.gain.value = 1; - this.voice.connect(this.master); - this.rev = ctx.createConvolver(); - this.rev.buffer = this.impulse(2.3, 2.4); - this.revIn = ctx.createGain(); - this.revIn.gain.value = 0.55; - this.revIn.connect(this.rev); - this.rev.connect(this.master); - this.noise = ctx.createBuffer(1, SR * 2, SR); - const d = this.noise.getChannelData(0), nr = rng(99); - for (let i = 0; i < d.length; i++) d[i] = nr() * 2 - 1; - } - impulse(sec, decay) { - const b = this.ctx.createBuffer(2, Math.floor(SR * sec), SR), r = rng(7); - for (let c2 = 0; c2 < 2; c2++) { - const d = b.getChannelData(c2); - for (let i = 0; i < d.length; i++) d[i] = (r() * 2 - 1) * Math.pow(1 - i / d.length, decay) * (i < 400 ? i / 400 : 1); - } - return b; - } - out(node, bus, pan = 0, send = 0.25) { - const p = this.ctx.createStereoPanner(); - p.pan.value = pan; - node.connect(p); - p.connect(bus); - if (send > 0) { - const s = this.ctx.createGain(); - s.gain.value = send; - p.connect(s); - s.connect(this.revIn); - } - } - // ---------- 乐器 ---------- - ks(freq) { - const key = Math.round(freq * 10); - let b = this.ksCache.get(key); - if (b) return b; - const len = Math.floor(SR * 3), per = Math.max(2, Math.round(SR / freq)); - b = this.ctx.createBuffer(1, len, SR); - const d = b.getChannelData(0), r = rng(key); - const buf = new Float32Array(per); - for (let i = 0; i < per; i++) buf[i] = (r() * 2 - 1) * (1 - i / per * 0.5); - let idx = 0, last = 0; - const damp = freq > 600 ? 0.994 : 0.997; - for (let i = 0; i < len; i++) { - const cur = buf[idx]; - const nxt = buf[(idx + 1) % per]; - const v = damp * 0.5 * (cur + nxt); - buf[idx] = v; - d[i] = cur * 0.8 + last * 0.2; - last = cur; - idx = (idx + 1) % per; - } - this.ksCache.set(key, b); - return b; + g2.strokeStyle = col; + g2.lineWidth = w; + g2.beginPath(); + g2.moveTo(pts[0][0], pts[0][1]); + for (const p of pts.slice(1)) g2.lineTo(p[0], p[1]); + g2.stroke(); + if (endCol && split > 0) { + const a = pts[pts.length - 2], b = pts[pts.length - 1], m = lp2(a, b, split); + g2.strokeStyle = endCol; + g2.lineWidth = w * 0.9; + g2.beginPath(); + g2.moveTo(m[0], m[1]); + g2.lineTo(b[0], b[1]); + g2.stroke(); } - // 拨弦直接在 JS 里叠加到采样缓冲(比每个音符建节点快一个数量级) - pl = []; - pluck(t, m, vel = 0.5, pan = 0, o = {}) { - if (t < 0 || t > TOTAL + 2) return; - if (!this.pl.length) { - const n2 = this.ctx.length; - this.pl = [new Float32Array(n2), new Float32Array(n2)]; + } + var SKIN = "#efcfa8"; + var PROP = { + boy: { hip: -0.4, sh: -0.68, head: -0.85, hr: 0.13, ua: 0.15, fa: 0.15, th: 0.2, sn: 0.2, wTop: 0.095, wBot: 0.11, limb: 0.055 }, + man: { hip: -0.48, sh: -0.79, head: -0.91, hr: 0.088, ua: 0.17, fa: 0.165, th: 0.245, sn: 0.235, wTop: 0.11, wBot: 0.105, limb: 0.05 }, + woman: { hip: -0.47, sh: -0.77, head: -0.89, hr: 0.085, ua: 0.16, fa: 0.16, th: 0.24, sn: 0.23, wTop: 0.095, wBot: 0.115, limb: 0.045 } + }; + function person(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 1, t = o.t; + const P0 = PROP[o.kind]; + const iw = Math.max(1.1, s * 0.012) / s; + const alpha = o.alpha ?? 1; + if (alpha <= 0) return; + g2.save(); + g2.globalAlpha = alpha; + g2.translate(o.x + (o.shiver ?? 0) * s * 6e-3 * Math.sin(t * 53 + seed), o.y); + g2.scale(face * s, s); + const bob = o.bob ?? 0; + const hip = [0, (o.hipY ?? P0.hip) + bob]; + const lean = o.lean ?? 0; + const sh = rot([0, P0.sh + bob], hip, lean); + const neck = rot([8e-3, P0.sh - 0.035 + bob], hip, lean); + const head = rot([0.022, P0.head + bob], hip, lean); + const jacket = o.jacket ?? (o.kind === "boy" ? "#c0473a" : "#5d7385"); + const pants = o.pants ?? (o.kind === "boy" ? "#3e4a5c" : "#3a4048"); + const L = P0.limb; + const drawTool = () => { + const tl3 = o.tool; + if (!tl3) return; + if (tl3.kind === "hoe" || tl3.kind === "pole" || tl3.kind === "stick") { + brush(g2, [tl3.a, lp2(tl3.a, tl3.b, 0.5), tl3.b], 0.022, { color: "#5a4026", alpha: 0.95, taperIn: 0, taperOut: 0, jitter: 0.08, seed: seed + 70 }); + g2.strokeStyle = rgba("#a07a48", 0.9); + g2.lineWidth = 0.012; + g2.beginPath(); + g2.moveTo(tl3.a[0], tl3.a[1]); + g2.lineTo(tl3.b[0], tl3.b[1]); + g2.stroke(); + if (tl3.kind === "hoe") { + const dx = tl3.b[0] - tl3.a[0], dy = tl3.b[1] - tl3.a[1], d = Math.hypot(dx, dy) || 1; + const ux = dx / d, uy = dy / d, nx = -uy, ny = ux; + const b = tl3.b; + const q2 = [[b[0] - nx * 0.01, b[1] - ny * 0.01], [b[0] + nx * 0.13 - ux * 0.02, b[1] + ny * 0.13 - uy * 0.02], [b[0] + nx * 0.14 + ux * 0.05, b[1] + ny * 0.14 + uy * 0.05], [b[0] + ux * 0.03, b[1] + uy * 0.03]]; + g2.fillStyle = rgba("#4a4c50", 0.95); + g2.beginPath(); + g2.moveTo(q2[0][0], q2[0][1]); + for (const p of q2.slice(1)) g2.lineTo(p[0], p[1]); + g2.closePath(); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.8); + g2.lineWidth = iw; + g2.stroke(); + } + if (tl3.kind === "pole") { + for (const e of [tl3.a, tl3.b]) { + g2.strokeStyle = rgba("#5a4026", 0.8); + g2.lineWidth = 6e-3; + g2.beginPath(); + g2.moveTo(e[0], e[1]); + g2.lineTo(e[0], e[1] + 0.06); + g2.stroke(); + } + } } - const src = this.ks(midi(m)).getChannelData(0); - const dur = o.dur ?? 2.6; - const i0 = Math.floor(t * SR), n = Math.min(src.length - 2, Math.floor((dur + 0.6) * SR)); - const gl = vel * Math.cos((pan + 1) * Math.PI / 4), gr = vel * Math.sin((pan + 1) * Math.PI / 4); - const relStart = dur * 0.7 * SR, tau = dur * 0.15 * SR; - const a = Math.exp(-2 * Math.PI * (2400 + (o.bright ?? 0.5) * 4e3) / SR); - const L = this.pl[0], R = this.pl[1]; - let pos = 0, lp2 = 0; - const bendTo = o.bend ? Math.pow(2, o.bend / 12) : 1; - const b0 = 0.12 * SR, b1 = 0.32 * SR; - for (let k = 0; k < n; k++) { - const idx = i0 + k; - if (idx >= L.length) break; - const rate = o.bend ? k < b0 ? 1 : k > b1 ? bendTo : 1 + (bendTo - 1) * (k - b0) / (b1 - b0) : 1; - const p = Math.floor(pos), f = pos - p; - if (p + 1 >= src.length) break; - const x = src[p] * (1 - f) + src[p + 1] * f; - pos += rate; - lp2 = x + a * (lp2 - x); - const env = k < relStart ? 1 : Math.exp(-(k - relStart) / tau); - const v = lp2 * env; - L[idx] += v * gl; - R[idx] += v * gr; + if (tl3.kind === "whip") { + brush(g2, [tl3.a, lp2(tl3.a, tl3.b, 0.5), tl3.b], 0.014, { color: "#5a4026", alpha: 0.95, taperIn: 0, taperOut: 0.3, seed: seed + 71 }); + const sw = tl3.swing ?? Math.sin(t * 3); + const end = [tl3.b[0] + 0.12 + 0.1 * sw, tl3.b[1] + 0.2 - 0.08 * sw]; + brush(g2, spline([tl3.b, [tl3.b[0] + 0.14, tl3.b[1] - 0.02 + 0.06 * sw], end], 8), 6e-3, { color: INK, alpha: 0.8, taperIn: 0, taperOut: 0.6, seed: seed + 72 }); } + }; + const hipL = add2(hip, [-0.03, 0.01]), hipR = add2(hip, [0.03, 0.01]); + const shL = add2(sh, [-0.05, 0.02]), shR = add2(sh, [0.05, 0.02]); + const legBend = 1; + const [kneeL] = ik(hipL, o.fl, P0.th, P0.sn, -legBend); + const [kneeR] = ik(hipR, o.fr, P0.th, P0.sn, -legBend); + const [elL] = ik(shL, o.hl, P0.ua, P0.fa, 1); + const [elR] = ik(shR, o.hr, P0.ua, P0.fa, 1); + const backCol = (c2) => mixHex(c2, "#3a3530", 0.18); + const legCol = o.kind === "boy" ? SKIN : pants; + limb(g2, [hipL, kneeL, o.fl], L * 1.05, backCol(o.kind === "boy" ? pants : pants), iw, seed + 1, o.kind === "boy" ? backCol(SKIN) : void 0, o.kind === "boy" ? 0 : 0); + if (o.kind === "boy") limb(g2, [kneeL, o.fl], L * 0.95, backCol(SKIN), iw, seed + 2); + foot(g2, o.fl, iw, o.kind); + limb(g2, [shL, elL, o.hl], L * 0.95, backCol(jacket), iw, seed + 3, backCol(SKIN), o.kind === "boy" ? 0.05 : 0.55); + hand(g2, o.hl, L, iw); + drawTool(); + const torso = [ + rot([-P0.wTop, P0.sh + 0.02 + bob], hip, lean), + rot([P0.wTop * 0.9, P0.sh + 0.02 + bob], hip, lean), + rot([P0.wBot, hip[1] + 0.03], hip, lean), + rot([-P0.wBot * 0.95, hip[1] + 0.03], hip, lean) + ]; + const pantsTop = [rot([-P0.wBot * 0.95, hip[1] - 0.01], hip, lean), rot([P0.wBot, hip[1] - 0.01], hip, lean), add2(hipR, [0.03, o.kind === "boy" ? 0.09 : 0.06]), add2(hipL, [-0.04, o.kind === "boy" ? 0.09 : 0.06])]; + wash(g2, pantsTop, pants, 0.95, { lw: iw }); + if (o.kind === "boy") { + limb(g2, [hipR, kneeR], L * 1.1, pants, iw, seed + 4); + limb(g2, [kneeR, o.fr], L, SKIN, iw, seed + 5); + } else limb(g2, [hipR, kneeR, o.fr], L * 1.08, legCol, iw, seed + 4); + foot(g2, o.fr, iw, o.kind); + wash(g2, torso, jacket, 0.96, { lw: iw }); + outline(g2, torso, iw * 1.2, { seed: seed + 9, alpha: 0.8, breaks: [0.05, 0.45, 0.95] }); + if (o.kind === "boy") { + const c0 = rot([-0.04, P0.sh + 0.02 + bob], hip, lean), c1 = rot([0.05, P0.sh + 0.02 + bob], hip, lean), c2 = rot([5e-3, P0.sh + 0.09 + bob], hip, lean); + g2.fillStyle = rgba("#f4ead4", 0.95); + g2.beginPath(); + g2.moveTo(c0[0], c0[1]); + g2.lineTo(c1[0], c1[1]); + g2.lineTo(c2[0], c2[1]); + g2.closePath(); + g2.fill(); + } else { + const b0 = rot([-P0.wBot, hip[1] + 0], hip, lean), b1 = rot([P0.wBot, hip[1] + 0], hip, lean); + brush(g2, [b0, lp2(b0, b1, 0.5), b1], 0.025, { color: "#2a2622", alpha: 0.8, taperIn: 0, taperOut: 0, seed: seed + 10 }); + const k0 = rot([0.02, P0.sh + 0.03 + bob], hip, lean), k1 = rot([0.035, hip[1] + 0.02], hip, lean); + brush(g2, [k0, lp2(k0, k1, 0.5), k1], iw * 0.9, { alpha: 0.6, seed: seed + 11 }); } - flushPlucks() { - if (!this.pl.length) return; - const b = this.ctx.createBuffer(2, this.ctx.length, SR); - b.copyToChannel(this.pl[0], 0); - b.copyToChannel(this.pl[1], 1); - const s = this.ctx.createBufferSource(); - s.buffer = b; - const g2 = this.ctx.createGain(); - g2.gain.value = 1; - s.connect(g2); - this.out(g2, this.music, 0, 0.3); - s.start(0); - this.pl = []; - } - flute(t, m, dur, vel = 0.22, pan = 0.1) { - const c2 = this.ctx, fr = midi(m); - const o1 = c2.createOscillator(); - o1.type = "sine"; - o1.frequency.value = fr; - const o2 = c2.createOscillator(); - o2.type = "triangle"; - o2.frequency.value = fr * 2; - const lfo = c2.createOscillator(); - lfo.frequency.value = 5.3; - const lg = c2.createGain(); - lg.gain.setValueAtTime(0, t); - lg.gain.linearRampToValueAtTime(fr * 7e-3, t + Math.min(dur, 0.6)); - lfo.connect(lg); - lg.connect(o1.frequency); - lg.connect(o2.frequency); - const g2 = c2.createGain(); - g2.gain.value = 0.12; - const g3 = c2.createGain(); - g3.gain.setValueAtTime(0, t); - g3.gain.linearRampToValueAtTime(vel, t + 0.07); - g3.gain.setValueAtTime(vel * 0.85, t + Math.max(0.08, dur - 0.12)); - g3.gain.linearRampToValueAtTime(0, t + dur + 0.18); - const n = c2.createBufferSource(); - n.buffer = this.noise; - n.loop = true; - const bp = c2.createBiquadFilter(); - bp.type = "bandpass"; - bp.frequency.value = fr * 2.2; - bp.Q.value = 1.2; - const ng = c2.createGain(); - ng.gain.setValueAtTime(0, t); - ng.gain.linearRampToValueAtTime(vel * 0.16, t + 0.05); - ng.gain.linearRampToValueAtTime(vel * 0.04, t + 0.25); - ng.gain.linearRampToValueAtTime(0, t + dur + 0.1); - n.connect(bp); - bp.connect(ng); - this.out(ng, this.music, pan, 0.4); - o1.connect(g3); - o2.connect(g2); - g2.connect(g3); - this.out(g3, this.music, pan, 0.45); - for (const o of [o1, o2, lfo]) { - o.start(t); - o.stop(t + dur + 0.3); + g2.strokeStyle = SKIN; + g2.lineWidth = o.kind === "boy" ? 0.05 : 0.045; + g2.lineCap = "round"; + g2.beginPath(); + g2.moveTo(neck[0], neck[1]); + g2.lineTo(lp2(neck, head, 0.6)[0], lp2(neck, head, 0.6)[1]); + g2.stroke(); + drawHead(g2, head, P0.hr, o, iw, seed); + limb(g2, [shR, elR, o.hr], L, jacket, iw, seed + 12, SKIN, o.kind === "boy" ? 0.05 : 0.55); + hand(g2, o.hr, L, iw); + g2.restore(); + } + function hand(g2, p, L, iw) { + g2.fillStyle = SKIN; + g2.beginPath(); + g2.arc(p[0], p[1], L * 0.62, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.75); + g2.lineWidth = iw; + g2.stroke(); + } + function foot(g2, p, iw, kind) { + const w = kind === "boy" ? 0.05 : 0.055; + g2.fillStyle = rgba(kind === "boy" ? "#8a6a3a" : "#2e2a26", 0.95); + g2.beginPath(); + g2.ellipse(p[0] + w * 0.4, p[1] - 8e-3, w, 0.018, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw; + g2.stroke(); + } + function drawHead(g2, c2, r, o, iw, seed) { + const t = o.t; + g2.save(); + g2.translate(c2[0], c2[1]); + g2.rotate(o.tilt ?? 0); + const kind = o.kind; + g2.fillStyle = SKIN; + g2.beginPath(); + g2.ellipse(0, 0, r, r * 1.05, 0, 0, Math.PI * 2); + g2.fill(); + const fl = []; + for (let i = 0; i < 14; i++) { + const a = i / 14 * Math.PI * 2; + fl.push([Math.cos(a) * r, Math.sin(a) * r * 1.05]); + } + outline(g2, fl, iw * 1.1, { seed: seed + 20, alpha: 0.8, breaks: [0.08, 0.5, 0.95] }); + g2.fillStyle = rgba("#1f1c1a", 0.95); + g2.beginPath(); + if (kind === "boy") { + g2.ellipse(-r * 0.12, -r * 0.3, r * 0.98, r * 0.78, -0.15, Math.PI * 0.95, Math.PI * 2.08); + g2.fill(); + brush(g2, spline([[-r * 0.2, -r * 0.95], [-r * 0.1, -r * 1.45], [r * 0.25, -r * 1.7]], 6), r * 0.32, { color: "#1f1c1a", alpha: 0.95, taperIn: 0.1, taperOut: 0.8, seed: seed + 21 }); + g2.fillStyle = rgba("#c0473a", 0.95); + g2.beginPath(); + g2.arc(-r * 0.16, -r * 1.02, r * 0.14, 0, Math.PI * 2); + g2.fill(); + } else { + g2.ellipse(-r * 0.15, -r * 0.35, r * 0.95, r * 0.72, -0.1, Math.PI * 0.92, Math.PI * 2.05); + g2.fill(); + if (kind === "woman" || o.hat === "bun") { + g2.beginPath(); + g2.arc(-r * 0.85, -r * 0.45, r * 0.38, 0, Math.PI * 2); + g2.fill(); } - n.start(t, t * 0.37 % 1.5); - n.stop(t + dur + 0.2); } - pad(t0, t1, notes, vel = 0.06, cutoff = 900) { - const c2 = this.ctx; - const f = c2.createBiquadFilter(); - f.type = "lowpass"; - f.frequency.value = cutoff; - const g2 = c2.createGain(); - const att = Math.min(1.6, (t1 - t0) / 3); - g2.gain.setValueAtTime(0, t0); - g2.gain.linearRampToValueAtTime(vel, t0 + att); - g2.gain.setValueAtTime(vel, Math.max(t0 + att, t1 - att)); - g2.gain.linearRampToValueAtTime(0, t1); - f.connect(g2); - this.out(g2, this.music, 0, 0.5); - for (const m of notes) for (const det of [-6, 6]) { - const o = c2.createOscillator(); - o.type = "triangle"; - o.frequency.value = midi(m); - o.detune.value = det; - o.connect(f); - o.start(t0); - o.stop(t1 + 0.1); + g2.fillStyle = SKIN; + g2.beginPath(); + g2.ellipse(-r * 0.2, r * 0.05, r * 0.16, r * 0.22, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw * 0.8; + g2.beginPath(); + g2.arc(-r * 0.2, r * 0.05, r * 0.14, -1.2, 1.6); + g2.stroke(); + const e = o.expr ?? {}; + const ex = r * 0.5, ey = -r * 0.08; + const blink = (t + seed * 0.71) % 3.7 < 0.12; + const look = (o.look ?? 0) * r * 0.08; + g2.fillStyle = INK; + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.2; + g2.lineCap = "round"; + const eye = e.eye ?? "open"; + if (blink && eye !== "closed" && eye !== "happy") { + g2.beginPath(); + g2.moveTo(ex - r * 0.1, ey); + g2.lineTo(ex + r * 0.1, ey); + g2.stroke(); + } else if (eye === "closed" || eye === "happy") { + g2.beginPath(); + g2.arc(ex, ey + r * 0.05, r * 0.11, Math.PI * 1.1, Math.PI * 1.9); + g2.stroke(); + } else if (eye === "sad") { + g2.beginPath(); + g2.arc(ex, ey - r * 0.02, r * 0.1, 0.2, Math.PI - 0.2); + g2.stroke(); + } else if (eye === "wide") { + g2.fillStyle = "#fbf7ee"; + g2.beginPath(); + g2.ellipse(ex, ey, r * 0.14, r * 0.17, 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(ex + r * 0.03 + look, ey, r * 0.065, 0, Math.PI * 2); + g2.fill(); + } else { + g2.fillStyle = INK; + g2.beginPath(); + g2.ellipse(ex + (eye === "sly" ? r * 0.06 : 0) + look, ey, r * 0.065, r * 0.085, 0, 0, Math.PI * 2); + g2.fill(); + if (eye === "sly") { + g2.beginPath(); + g2.moveTo(ex - r * 0.13, ey - r * 0.07); + g2.lineTo(ex + r * 0.12, ey - r * 0.1); + g2.stroke(); } } - bell(t, m, vel = 0.15, pan = 0, bus) { - const c2 = this.ctx, fr = midi(m); - const g2 = c2.createGain(); - g2.gain.value = 1; - [[1, 1, 2.5], [2.76, 0.4, 1.2], [5.4, 0.22, 0.6], [8.93, 0.1, 0.35]].forEach(([k, a, d]) => { - const o = c2.createOscillator(); - o.type = "sine"; - o.frequency.value = fr * k; - const og = c2.createGain(); - og.gain.setValueAtTime(0, t); - og.gain.linearRampToValueAtTime(vel * a, t + 5e-3); - og.gain.exponentialRampToValueAtTime(1e-4, t + d); - o.connect(og); - og.connect(g2); - o.start(t); - o.stop(t + d + 0.05); - }); - this.out(g2, bus ?? this.music, pan, 0.5); + const brow = e.brow ?? "none"; + if (brow !== "none") { + g2.lineWidth = iw * 1.4; + g2.beginPath(); + if (brow === "angry") { + g2.moveTo(ex - r * 0.14, ey - r * 0.32); + g2.lineTo(ex + r * 0.16, ey - r * 0.2); + } else if (brow === "worried") { + g2.moveTo(ex - r * 0.14, ey - r * 0.2); + g2.lineTo(ex + r * 0.15, ey - r * 0.32); + } else { + g2.moveTo(ex - r * 0.14, ey - r * 0.36); + g2.quadraticCurveTo(ex, ey - r * 0.44, ex + r * 0.15, ey - r * 0.36); + } + g2.stroke(); } - block(t, vel = 0.2, fr = 950, pan = 0) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = "sine"; - o.frequency.setValueAtTime(fr, t); - o.frequency.exponentialRampToValueAtTime(fr * 0.6, t + 0.05); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(vel, t); - g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.09); - o.connect(g2); - this.out(g2, this.music, pan, 0.15); - o.start(t); - o.stop(t + 0.12); + g2.lineWidth = iw; + g2.beginPath(); + g2.moveTo(r * 0.92, -r * 0.02); + g2.quadraticCurveTo(r * 1.06, r * 0.12, r * 0.9, r * 0.17); + g2.stroke(); + if (kind !== "man") { + g2.fillStyle = rgba("#e07a6a", 0.35); + g2.beginPath(); + g2.arc(r * 0.45, r * 0.32, r * 0.16, 0, Math.PI * 2); + g2.fill(); } - gliss(t, notes, step = 0.06, vel = 0.3, pan = 0) { - notes.forEach((m, i) => this.pluck(t + i * step, m, vel * (0.7 + 0.3 * i / notes.length), pan + (i / notes.length - 0.5) * 0.6, { dur: 2.2 })); + const m = e.mouth ?? "smile"; + const mx = r * 0.68, my = r * 0.5; + g2.fillStyle = rgba("#7a2a22", 0.95); + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.1; + g2.beginPath(); + if (m === "smile") { + g2.arc(mx - r * 0.05, my - r * 0.12, r * 0.16, 0.35, Math.PI - 0.6); + g2.stroke(); + } else if (m === "line") { + g2.moveTo(mx - r * 0.15, my); + g2.lineTo(mx + r * 0.08, my - r * 0.02); + g2.stroke(); + } else if (m === "frown") { + g2.arc(mx - r * 0.05, my + r * 0.1, r * 0.14, Math.PI + 0.5, Math.PI * 2 - 0.4); + g2.stroke(); + } else if (m === "chew") { + const k = Math.abs(Math.sin(t * 6)); + g2.ellipse(mx, my, r * 0.1, r * (0.03 + 0.05 * k), 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + } else if (m === "open" || m === "shout") { + const k = m === "shout" ? 0.22 : 0.12; + g2.ellipse(mx + r * 0.02, my, r * 0.12, r * k, 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + } else if (m === "laugh") { + const k = 0.6 + 0.4 * Math.abs(Math.sin(t * 9)); + g2.moveTo(mx - r * 0.2, my - r * 0.06); + g2.quadraticCurveTo(mx, my + r * 0.36 * k, mx + r * 0.2, my - r * 0.08); + g2.closePath(); + g2.fill(); + g2.stroke(); + } else if (m === "cry") { + const k = 0.8 + 0.2 * Math.sin(t * 7); + g2.moveTo(mx - r * 0.18, my + r * 0.1); + g2.quadraticCurveTo(mx, my - r * 0.14 * k, mx + r * 0.16, my + r * 0.12); + g2.quadraticCurveTo(mx, my + r * 0.02, mx - r * 0.18, my + r * 0.1); + g2.fill(); + g2.stroke(); } - melody(t, beat, seq, vel = 0.22, oct = 0, inst = "flute") { - let x = t; - for (const [n, d] of seq) { - if (n !== "-") { - if (inst === "flute") this.flute(x, N[n] + oct, d * beat * 0.96, vel); - else this.pluck(x, N[n] + oct, vel, 0.15, { bend: d >= 2 ? this.r() < 0.5 ? 0 : 0 : 0 }); - } - x += d * beat; + if (o.hat === "straw") { + g2.fillStyle = rgba("#d8b45a", 0.95); + g2.beginPath(); + g2.moveTo(-r * 1.6, -r * 0.45); + g2.quadraticCurveTo(0, -r * 1.85, r * 1.6, -r * 0.45); + g2.quadraticCurveTo(0, -r * 0.7, -r * 1.6, -r * 0.45); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.7); + g2.lineWidth = iw; + g2.stroke(); + for (let i = -3; i <= 3; i++) { + g2.beginPath(); + g2.moveTo(i * r * 0.18, -r * 1.4); + g2.lineTo(i * r * 0.5, -r * 0.62); + g2.strokeStyle = rgba("#8a6a2a", 0.5); + g2.stroke(); } - return x; + } else if (o.hat === "scarf") { + g2.fillStyle = rgba("#f2ece0", 0.97); + g2.beginPath(); + g2.ellipse(-r * 0.05, -r * 0.55, r * 1.04, r * 0.5, 0, Math.PI * 0.98, Math.PI * 2.02); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw; + g2.stroke(); + g2.beginPath(); + g2.moveTo(-r * 0.95, -r * 0.5); + g2.quadraticCurveTo(-r * 1.3, -r * 0.3, -r * 1.25, r * 0.1); + g2.lineWidth = iw * 2.5; + g2.strokeStyle = rgba("#e8e0d0", 0.95); + g2.stroke(); } - // ---------- 音效 ---------- - noiseHit(t, dur, type, f0, f1, vel, pan = 0, q2 = 1, send = 0.2, bus) { - const c2 = this.ctx; - const s = c2.createBufferSource(); - s.buffer = this.noise; - s.loop = true; - const f = c2.createBiquadFilter(); - f.type = type; - f.Q.value = q2; - f.frequency.setValueAtTime(f0, t); - f.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + Math.min(0.01, dur * 0.2)); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); - s.connect(f); - f.connect(g2); - this.out(g2, bus ?? this.sfx, pan, send); - s.start(t, t * 0.731 % 1.8); - s.stop(t + dur + 0.05); + if (o.tears && o.tears > 0) { + for (let i = 0; i < 2; i++) { + const q2 = (t * 1.4 + i * 0.5) % 1; + g2.fillStyle = rgba("#7fa4c4", 0.85 * o.tears); + g2.beginPath(); + g2.ellipse(ex + r * 0.05, ey + r * 0.2 + q2 * r * 1.3, r * 0.06, r * 0.1, 0, 0, Math.PI * 2); + g2.fill(); + } } - tone(t, f0, f1, dur, vel, type = "sine", pan = 0, send = 0.2) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = type; - o.frequency.setValueAtTime(f0, t); - o.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + 8e-3); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); - o.connect(g2); - this.out(g2, this.sfx, pan, send); - o.start(t); - o.stop(t + dur + 0.05); + if (o.sweat && o.sweat > 0) { + g2.fillStyle = rgba("#9cc0d8", 0.9 * o.sweat); + g2.beginPath(); + g2.ellipse(-r * 0.6, -r * 0.7 + t % 0.8 * r * 0.5, r * 0.08, r * 0.13, 0.3, 0, Math.PI * 2); + g2.fill(); } - wind(t, dur, vel, pan = 0) { - const c2 = this.ctx; - const s = c2.createBufferSource(); - s.buffer = this.noise; - s.loop = true; - const f = c2.createBiquadFilter(); - f.type = "bandpass"; - f.Q.value = 0.8; - const lfo = c2.createOscillator(); - lfo.frequency.value = 0.23; - const lg = c2.createGain(); - lg.gain.value = 260; - f.frequency.value = 520; - lfo.connect(lg); - lg.connect(f.frequency); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + Math.min(1.5, dur / 3)); - g2.gain.setValueAtTime(vel, t + dur - Math.min(1.5, dur / 3)); - g2.gain.linearRampToValueAtTime(0, t + dur); - s.connect(f); - f.connect(g2); - this.out(g2, this.sfx, pan, 0.1); - s.start(t); - s.stop(t + dur + 0.1); - lfo.start(t); - lfo.stop(t + dur + 0.1); + g2.restore(); + } + var POSE = { + stand: (k = "boy") => k === "boy" ? { hl: [-0.05, -0.39], hr: [0.07, -0.4], fl: [-0.04, 0], fr: [0.05, 0] } : { hl: [-0.06, -0.46], hr: [0.07, -0.47], fl: [-0.05, 0], fr: [0.06, 0] }, + walk: (ph, k = "boy", run = 0) => { + const st = (0.09 + 0.07 * run) * (k === "boy" ? 1 : 1.15), lift = 0.04 + 0.06 * run; + const s1 = Math.sin(ph), s2 = Math.sin(ph + Math.PI); + const armY = k === "boy" ? -0.42 : -0.5; + return { + fl: [st * s1, -Math.max(0, Math.cos(ph)) * lift], + fr: [st * s2, -Math.max(0, Math.cos(ph + Math.PI)) * lift], + hl: [-st * 0.8 * s1 + 0.02, armY + run * 0.08], + hr: [-st * 0.8 * s2 + 0.04 + run * 0.06, armY + run * 0.1], + bob: -Math.abs(Math.sin(ph)) * 0.015 * (1 + run), + lean: 0.06 + 0.16 * run + }; } - chirp(t, vel = 0.06, pan = 0) { - const base = 2600 + this.r() * 1600; - for (let i = 0; i < 2 + Math.floor(this.r() * 3); i++) this.tone(t + i * 0.09, base, base * (1.25 + this.r() * 0.2), 0.06, vel, "sine", pan, 0.3); + }; + function sheep(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 1, t = o.t; + const run = o.run ?? 0, walk = o.walk ?? 0; + const ph = t * (run > 0 ? 11 : walk > 0 ? 5 : 0) + seed; + const bounce = run > 0 ? Math.abs(Math.sin(ph)) * 0.05 * run : walk > 0 ? Math.abs(Math.sin(ph)) * 0.012 : 4e-3 * Math.sin(t * 2 + seed); + const iw = Math.max(1, s * 0.016) / s; + g2.save(); + g2.globalAlpha = o.alpha ?? 1; + g2.translate(o.x, o.y); + g2.scale(face * s, s); + const by = -0.36 - bounce; + const legs = [[-0.2, 0], [-0.12, Math.PI], [0.14, Math.PI * 0.5], [0.22, Math.PI * 1.5]]; + legs.forEach(([lx, off], i) => { + const sw = run > 0 ? Math.sin(ph + off) * 0.1 * run : walk > 0 ? Math.sin(ph + off) * 0.04 : 0; + const top = [lx, by + 0.1], foot2 = [lx + sw, -0 - (run > 0 ? Math.max(0, Math.cos(ph + off)) * 0.04 : 0)]; + brush(g2, [top, lp2(top, foot2, 0.5), foot2], 0.045, { color: i < 2 ? "#4a4440" : "#2e2a27", alpha: 0.92, taperIn: 0, taperOut: 0.1, seed: seed + i }); + }); + const rx = 0.33, ry = o.lamb ? 0.21 : 0.2; + const bumps = []; + const nB = 13; + for (let i = 0; i < nB; i++) { + const a = i / nB * Math.PI * 2; + bumps.push([Math.cos(a) * rx, by + Math.sin(a) * ry]); } - peep(t, vel = 0.12, pan = 0) { - this.tone(t, 2100, 2900, 0.09, vel, "triangle", pan, 0.15); - this.tone(t + 0.13, 2200, 3e3, 0.08, vel * 0.8, "triangle", pan, 0.15); + g2.fillStyle = rgba("#fbf7ee", 0.98); + g2.beginPath(); + g2.ellipse(0, by, rx * 1.02, ry * 1.04, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.7); + g2.lineWidth = iw * 1.1; + for (let i = 0; i < nB; i++) { + const a = i / nB * Math.PI * 2, b = bumps[i]; + const rr = 0.075 + 0.015 * hash(i + seed); + g2.fillStyle = rgba("#fbf7ee", 0.98); + g2.beginPath(); + g2.arc(b[0], b[1], rr, a - 1.3, a + 1.3); + g2.fill(); + g2.stroke(); } - quack(t, vel = 0.14, pan = 0, pitch = 1) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = "sawtooth"; - o.frequency.setValueAtTime(330 * pitch, t); - o.frequency.linearRampToValueAtTime(250 * pitch, t + 0.16); - const f = c2.createBiquadFilter(); - f.type = "bandpass"; - f.frequency.value = 1150 * pitch; - f.Q.value = 3.2; - const f2 = c2.createBiquadFilter(); - f2.type = "peaking"; - f2.frequency.value = 2400; - f2.gain.value = 8; - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + 0.015); - g2.gain.setValueAtTime(vel, t + 0.11); - g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.2); - o.connect(f); - f.connect(f2); - f2.connect(g2); - this.out(g2, this.sfx, pan, 0.15); - o.start(t); - o.stop(t + 0.25); + blot(g2, -0.02, by + 0.1, 0.3, "#8a8578", 0.22, 1.2, 0.5); + for (let i = 0; i < 5; i++) { + const cx = -0.18 + i * 0.09, cy = by - 0.04 + i % 2 * 0.06; + g2.strokeStyle = rgba("#9a958a", 0.6); + g2.lineWidth = iw; + g2.beginPath(); + g2.arc(cx, cy, 0.025, 0.5, 4.5); + g2.stroke(); + } + g2.fillStyle = rgba("#fbf7ee", 1); + g2.beginPath(); + g2.arc(-rx - 0.02, by - 0.04, 0.05, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.stroke(); + const graze = clamp(o.graze ?? 0), look = o.look ?? 0; + const chew = graze > 0.5 ? Math.sin(t * 7 + seed) * 0.01 : 0; + const hx = lerp(0.36, 0.38, graze) + run * 0.05, hy = lerp(by - 0.1 - look * 0.06, -0.08 + chew, graze); + const ang = lerp(0.35, 1.25, graze) - run * 0.2 - look * 0.35; + g2.save(); + g2.translate(hx, hy); + g2.rotate(ang); + g2.fillStyle = rgba("#fbf7ee", 1); + g2.beginPath(); + g2.arc(-0.03, -0.07, 0.06, 0, Math.PI * 2); + g2.fill(); + const fc = o.lamb ? "#5a4c44" : "#2e2a27"; + g2.fillStyle = rgba(fc, 0.96); + g2.beginPath(); + g2.ellipse(0.04, 0, 0.11, 0.07, 0.15, 0, Math.PI * 2); + g2.fill(); + g2.beginPath(); + g2.ellipse(-0.05, 0.02, 0.06, 0.022, -0.6 - 0.3 * (o.scared ?? 0), 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = "#f6efdf"; + g2.beginPath(); + g2.arc(0.04, -0.02, o.scared ? 0.022 : 0.016, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(0.045, -0.02, 8e-3, 0, Math.PI * 2); + g2.fill(); + if ((o.baa ?? 0) > 0) { + g2.fillStyle = rgba("#8a3a32", 0.95); + g2.beginPath(); + g2.ellipse(0.13, 0.03, 0.02, 0.018 * o.baa, 0, 0, Math.PI * 2); + g2.fill(); } - crack(t, vel = 0.25, pan = 0, big = false) { - const n = big ? 7 : 3; - for (let i = 0; i < n; i++) this.noiseHit(t + i * (0.018 + this.r() * 0.03), 0.03 + this.r() * 0.03, "highpass", 2500 + this.r() * 2500, 1800, vel * (0.6 + this.r() * 0.5), pan, 0.7, 0.1); - if (big) this.tone(t, 160, 70, 0.25, vel * 0.9, "sine", pan, 0.2); + g2.restore(); + g2.restore(); + } + function wolf(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 3, t = o.t; + const mode = o.mode ?? "stand"; + const iw = Math.max(1.1, s * 0.012) / s; + g2.save(); + g2.globalAlpha = o.alpha ?? 1; + g2.translate(o.x, o.y); + g2.scale(face * s, s); + const run = mode === "run", sneak = mode === "sneak"; + const ph = t * (run ? 12 : sneak ? 3 : 0) + seed; + const low = sneak ? 0.07 : 0; + const bounce = run ? Math.abs(Math.sin(ph)) * 0.04 : 0; + const by = -0.4 + low - bounce; + const stretch = run ? 1.12 : 1; + const fur = "#6d6f73", dark = "#3f4145", belly = "#b9b6ad"; + const tw = run ? -0.1 : sneak ? 0.12 : 0.05 + 0.03 * Math.sin(t * 2); + wash(g2, [[-0.32 * stretch, by - 0.04], [-0.55 * stretch, by + 0.02 - tw], [-0.68 * stretch, by + 0.12 - tw], [-0.6 * stretch, by + 0.16 - tw], [-0.4 * stretch, by + 0.08]], fur, 0.92, { lw: iw }); + brush(g2, spline([[-0.32 * stretch, by - 0.04], [-0.55 * stretch, by + 0.02 - tw], [-0.68 * stretch, by + 0.12 - tw]], 6), iw * 2, { color: dark, alpha: 0.8, dry: 0.7, seed: seed + 1 }); + const legs = [[-0.22, 0, false], [0.2, Math.PI, false], [-0.17, Math.PI * 0.6, true], [0.26, Math.PI * 1.6, true]]; + for (const [lx0, off, front] of legs) { + if (front) continue; + drawLeg(lx0, off, dark); } - flap(t, n, rate, vel = 0.12, pan = 0) { - for (let i = 0; i < n; i++) this.noiseHit(t + i / rate, 0.12, "bandpass", 900, 300, vel * (0.8 + this.r() * 0.3), pan, 0.9, 0.1); + function drawLeg(lx0, off, col) { + const lx = lx0 * stretch; + const sw = run ? Math.sin(ph + off) * 0.16 : sneak ? Math.sin(ph + off) * 0.05 : 0; + const top = [lx, by + 0.08], knee = [lx + sw * 0.5 + (lx < 0 ? -0.04 : 0.02), by + 0.22 - low * 0.5], foot2 = [lx + sw, -(run ? Math.max(0, Math.cos(ph + off)) * 0.06 : 0)]; + brush(g2, [top, knee, foot2], 0.055, { color: col, alpha: 0.95, taperIn: 0, taperOut: 0.35, seed: seed + lx0 * 10 }); } - thump(t, vel = 0.4, f = 95) { - this.tone(t, f, f * 0.55, 0.22, vel, "sine", 0, 0.15); - this.noiseHit(t, 0.05, "lowpass", 1200, 300, vel * 0.5, 0, 0.7, 0.05); + const body = [[0.3 * stretch, by - 0.1], [0.05, by - 0.14], [-0.25 * stretch, by - 0.12], [-0.36 * stretch, by - 0.02], [-0.28 * stretch, by + 0.12], [0, by + 0.12], [0.26 * stretch, by + 0.1], [0.36 * stretch, by + 0]]; + wash(g2, body, fur, 0.95, { lw: iw }); + blot(g2, 0, by + 0.06, 0.3, belly, 0.35, 1.3, 0.3); + outline(g2, body, iw * 1.3, { seed: seed + 2, alpha: 0.85, breaks: [0.02, 0.4, 0.7, 0.98] }); + for (let i = 0; i < 6; i++) { + const x0 = (-0.3 + i * 0.11) * stretch; + brush(g2, [[x0, by - 0.13], [x0 + 0.05, by - 0.17 - 0.02 * hash(i + seed)], [x0 + 0.1, by - 0.12]], 0.025, { color: dark, alpha: 0.75, dry: 0.8, seed: seed + 10 + i, taperIn: 0.1 }); } - shot(t, pan = 0.5) { - this.noiseHit(t, 0.5, "lowpass", 6e3, 200, 0.85, pan, 0.5, 0.6); - this.tone(t, 120, 40, 0.5, 0.6, "sine", pan, 0.6); - this.noiseHit(t + 0.05, 1.6, "lowpass", 900, 120, 0.18, pan, 0.6, 0.9); + for (const [lx0, off, front] of legs) if (front) drawLeg(lx0, off, fur); + const howl2 = mode === "howl", snarl = mode === "snarl"; + const hx = 0.4 * stretch, hy = by - 0.12 + low * 0.6 + (run ? 0.04 : 0); + const ha = howl2 ? -0.8 : sneak ? 0.25 : run ? 0.1 : -0.05; + g2.save(); + g2.translate(hx, hy); + g2.rotate(ha); + wash(g2, [[-0.14, 0.12], [-0.06, -0.07], [0.06, -0.06], [0.08, 0.08], [-0.02, 0.16]], fur, 0.95, { lw: iw }); + const head = [[-0.06, -0.08], [0.06, -0.09], [0.24, -0.03], [0.26, 0.01], [0.2, 0.04], [0.06, 0.07], [-0.06, 0.05]]; + wash(g2, head, fur, 0.97, { lw: iw }); + outline(g2, head, iw * 1.2, { seed: seed + 3, alpha: 0.9, breaks: [0.05, 0.55, 0.95] }); + for (const [ex, k] of [[-0.03, 0.85], [0.03, 1]]) { + g2.fillStyle = rgba(k < 1 ? dark : fur, 0.97); + g2.beginPath(); + g2.moveTo(ex - 0.03, -0.07); + g2.lineTo(ex + 0.01, -0.2 * k); + g2.lineTo(ex + 0.05, -0.08); + g2.closePath(); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.8); + g2.lineWidth = iw; + g2.stroke(); } - swanCall(t, vel = 0.08, pan = -0.3) { - const c2 = this.ctx; - for (const [dt, f] of [[0, 520], [0.32, 620]]) { - const o = c2.createOscillator(); - o.type = "sawtooth"; - o.frequency.setValueAtTime(f, t + dt); - o.frequency.linearRampToValueAtTime(f * 0.94, t + dt + 0.28); - const bp = c2.createBiquadFilter(); - bp.type = "bandpass"; - bp.frequency.value = 1300; - bp.Q.value = 4; - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t + dt); - g2.gain.linearRampToValueAtTime(vel, t + dt + 0.04); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dt + 0.3); - o.connect(bp); - bp.connect(g2); - this.out(g2, this.sfx, pan, 0.8); - o.start(t + dt); - o.stop(t + dt + 0.35); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(0.255, -0.015, 0.018, 0, Math.PI * 2); + g2.fill(); + const open = howl2 ? 0.07 : snarl ? 0.05 + 0.01 * Math.sin(t * 20) : run ? 0.03 : 0; + if (open > 0) { + g2.fillStyle = rgba("#5a1e1a", 0.95); + g2.beginPath(); + g2.moveTo(0.06, 0.04); + g2.lineTo(0.24, 0.02); + g2.lineTo(0.2, 0.02 + open); + g2.lineTo(0.06, 0.05); + g2.closePath(); + g2.fill(); + if (snarl || run) { + g2.fillStyle = "#f6efdf"; + for (let i = 0; i < 3; i++) { + const x = 0.12 + i * 0.035; + g2.beginPath(); + g2.moveTo(x, 0.025); + g2.lineTo(x + 0.01, 0.045); + g2.lineTo(x + 0.02, 0.025); + g2.fill(); + } } + } else { + g2.strokeStyle = INK; + g2.lineWidth = iw; + g2.beginPath(); + g2.moveTo(0.08, 0.045); + g2.lineTo(0.22, 0.03); + g2.stroke(); } - splash(t, vel = 0.35) { - this.noiseHit(t, 0.6, "lowpass", 3e3, 400, vel, 0, 0.6, 0.3); - for (let i = 0; i < 6; i++) this.tone(t + 0.08 + this.r() * 0.4, 900 + this.r() * 900, 1500 + this.r() * 900, 0.05, vel * 0.2, "sine", this.r() - 0.5, 0.3); + const glow = o.glow ?? (sneak ? 0.6 : 0.3); + blot(g2, 0.08, -0.035, 0.05, "#c8f05a", 0.6 * glow); + g2.fillStyle = "#c8e050"; + g2.beginPath(); + g2.ellipse(0.08, -0.035, 0.025, 0.014, -0.25, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = INK; + g2.beginPath(); + g2.ellipse(0.085, -0.035, 6e-3, 0.012, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.3; + g2.beginPath(); + g2.moveTo(0.04, -0.06); + g2.lineTo(0.12, -0.045); + g2.stroke(); + if (o.carry) { + g2.save(); + g2.translate(0.16, 0.16); + g2.rotate(-0.2); + sheep(g2, { x: 0, y: 0, s: 0.42, t, face: -1, lamb: true, scared: 1, seed: 9 }); + g2.restore(); } - heart(t) { - this.tone(t, 70, 50, 0.16, 0.28, "sine", 0, 0.05); - this.tone(t + 0.2, 64, 48, 0.14, 0.2, "sine", 0, 0.05); + g2.restore(); + g2.restore(); + } + function eyesInDark(g2, x, y, s, t, a) { + if (a <= 0) return; + const blink = (t + 0.3) % 2.6 < 0.12 ? 0.15 : 1; + for (const dx of [-0.5, 0.5]) { + blot(g2, x + dx * s, y, s * 0.9, "#c8f05a", 0.35 * a); + g2.fillStyle = rgba("#d8f070", a); + g2.beginPath(); + g2.ellipse(x + dx * s, y, s * 0.32, s * 0.16 * blink, dx * 0.4, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = rgba(INK, a); + g2.beginPath(); + g2.ellipse(x + dx * s, y, s * 0.06, s * 0.14 * blink, 0, 0, Math.PI * 2); + g2.fill(); } - clap(t, vel = 0.08, pan = -0.5) { - this.noiseHit(t, 0.06, "bandpass", 1600 + this.r() * 600, 900, vel, pan, 1.5, 0.2); + } + function cottage(g2, x, y, s, seed) { + const r = rng(seed); + const wall = [[x - s * 0.5, y], [x - s * 0.5, y - s * 0.45], [x + s * 0.5, y - s * 0.45], [x + s * 0.5, y]]; + wash(g2, wall, "#d9cba8", 0.92); + outline(g2, wall, Math.max(1.2, s * 0.012), { seed, alpha: 0.75 }); + wash(g2, [[x - s * 0.1, y], [x - s * 0.1, y - s * 0.3], [x + s * 0.08, y - s * 0.3], [x + s * 0.08, y]], "#4a3a2a", 0.85); + wash(g2, [[x + s * 0.22, y - s * 0.2], [x + s * 0.22, y - s * 0.33], [x + s * 0.38, y - s * 0.33], [x + s * 0.38, y - s * 0.2]], "#3a3530", 0.8); + const roof = [[x - s * 0.66, y - s * 0.4], [x - s * 0.3, y - s * 0.82], [x + s * 0.3, y - s * 0.84], [x + s * 0.66, y - s * 0.4]]; + wash(g2, roof, "#b8955a", 0.92); + for (let i = 0; i < 26; i++) { + const u = r(), px = lerp(x - s * 0.62, x + s * 0.62, u), top = y - s * 0.8 + Math.abs(u - 0.5) * s * 0.7; + brush(g2, [[px - s * 0.02, top], [px + s * 0.01, lerp(top, y - s * 0.42, 0.6)], [px + s * 0.02, y - s * 0.4]], Math.max(1, s * 0.01), { color: "#7a5a2a", alpha: 0.55, seed: seed + i, taperIn: 0.1, dry: 0.4 }); } - swish(t, vel = 0.08) { - this.noiseHit(t, 0.35, "bandpass", 2500, 900, vel, 0, 1, 0.25); + brush(g2, [[x - s * 0.68, y - s * 0.39], [x, y - s * 0.43], [x + s * 0.68, y - s * 0.39]], Math.max(1.5, s * 0.025), { color: "#6a4a22", alpha: 0.8, seed: seed + 50, dry: 0.5 }); + } + function chimneySmoke(g2, x, y, s, t, seed, a = 0.4) { + for (let i = 0; i < 6; i++) { + const q2 = (t * 0.18 + i / 6 + hash(seed)) % 1; + blot(g2, x + Math.sin(q2 * 5 + i) * s * 0.15 + q2 * s * 0.4, y - q2 * s * 1.6, s * (0.08 + q2 * 0.25), "#9a958c", a * (1 - q2)); } - drop(t) { - this.tone(t, 700, 1500, 0.12, 0.3, "sine", 0, 0.5); - this.noiseHit(t, 0.08, "highpass", 3e3, 2e3, 0.1, 0, 0.7, 0.4); + } + function terraces(g2, x0, y0, x1, y1, rows, seed) { + const r = rng(seed); + for (let i = 0; i < rows; i++) { + const ya = lerp(y0, y1, i / rows), yb = lerp(y0, y1, (i + 1) / rows); + const pts = [], pts2 = []; + for (let k = 0; k <= 12; k++) { + const u = k / 12, x = lerp(x0, x1, u); + pts.push([x, ya + Math.sin(u * 3 + i + seed) * (yb - ya) * 0.25]); + pts2.push([x, yb + Math.sin(u * 3 + i + 1 + seed) * (yb - ya) * 0.25]); + } + const col = i % 2 ? "#a9b86a" : "#c9b56a"; + wash(g2, [...pts, ...pts2.reverse()], col, 0.42 + 0.1 * r(), { closed: true }); + brush(g2, pts, Math.max(1.2, (yb - ya) * 0.06), { color: "#5a5a32", alpha: 0.55, seed: seed + i, dry: 0.5, jitter: 0.4 }); + for (let k = 0; k < 14; k++) { + const x = lerp(x0, x1, r()), y = lerp(ya, yb, 0.3 + 0.5 * r()); + brush(g2, [[x, y], [x + 1, y - (yb - ya) * 0.18]], Math.max(1, (yb - ya) * 0.04), { color: "#4f6a3e", alpha: 0.6, seed: seed + k * 7 }); + } } - lap(t0, t1, vel = 0.05) { - for (let t = t0; t < t1; t += 0.6 + this.r() * 0.9) this.noiseHit(t, 0.5, "bandpass", 700 + this.r() * 300, 300, vel, this.r() * 1.2 - 0.6, 1.5, 0.3); + } + function rock(g2, x, y, w, h, seed) { + const loop = [[x - w * 0.5, y], [x - w * 0.46, y - h * 0.55], [x - w * 0.2, y - h * 0.95], [x + w * 0.15, y - h], [x + w * 0.45, y - h * 0.6], [x + w * 0.52, y]]; + wash(g2, loop, "#8a8a82", 0.85); + outline(g2, loop, Math.max(1.5, w * 0.02), { seed, alpha: 0.85, breaks: [0, 0.5, 0.86] }); + const r = rng(seed); + for (let i = 0; i < 8; i++) { + const px = x + (r() - 0.5) * w * 0.7, py = y - h * (0.2 + r() * 0.6); + brush(g2, [[px, py], [px + w * 0.08, py + h * 0.08], [px + w * 0.12, py + h * 0.2]], Math.max(1.2, w * 0.018), { color: "#3a3a36", alpha: 0.6, dry: 0.7, seed: seed + i }); } - }; - function score(m) { - const B = 60 / 72; - const S = sceneStart, D = sceneDur; - const r = rng(808); - const arp = (t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) => { - let i = 0; - for (let t2 = t0; t2 < t1 - 0.05; t2 += step, i++) { - const ch = chords[Math.floor((t2 - t0) / (step * 8)) % chords.length]; - m.pluck(t2, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.8 }); - } - }; - const G = [N.G3, N.D4, N.B4], Em = [N.E3, N.B3, N.G4], Dm = [N.D3, N.A3, N.D4], Am = [N.A2, N.E3, N.A3], Gh = [N.G4, N.B4, N.D5]; - let t = S("title"); - m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5], 0.07, 0.28); - m.pad(t + 0.5, S("summer") + 1, [N.G2, N.D3], 0.05); - m.melody(t + 2.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1.5]], 0.17); - t = S("summer"); - m.pad(t, S("hatch") + 0.5, [N.G2, N.D3, N.B3], 0.045); - arp(t + 0.3, S("hatch"), [G, Em, G, Dm], B / 2, 0.2); - m.melody(t + 1.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1], ["B4", 1], ["D5", 0.5], ["B4", 0.5], ["A4", 2], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.13); - t = S("hatch"); - for (let i = 0; i < Math.floor(D("hatch") / B); i++) { - if (i % 2 === 0) m.block(t + i * B, 0.07, 1100, -0.3); - m.pluck(t + i * B + B / 2, [N.G4, N.B4, N.D5, N.E5][i % 4], 0.13, 0.3, { dur: 0.5, bright: 0.9 }); + blot(g2, x - w * 0.15, y - h * 0.7, w * 0.25, "#f2ecd8", 0.35); + } + function bigTree(g2, x, y, h, t, seed, o = {}) { + const col = o.color ?? "#5f8a4c"; + brush(g2, spline([[x, y], [x - h * 0.03, y - h * 0.3], [x + h * 0.02, y - h * 0.55]], 8), h * 0.08, { color: "#3a3027", alpha: 0.92, seed, dry: 0.5, jitter: 0.3, taperIn: 0 }); + brush(g2, spline([[x, y - h * 0.4], [x + h * 0.12, y - h * 0.52], [x + h * 0.22, y - h * 0.6]], 6), h * 0.035, { color: "#3a3027", alpha: 0.9, seed: seed + 1 }); + brush(g2, spline([[x, y - h * 0.45], [x - h * 0.14, y - h * 0.56], [x - h * 0.22, y - h * 0.62]], 6), h * 0.03, { color: "#3a3027", alpha: 0.9, seed: seed + 2 }); + const r = rng(seed); + for (let i = 0; i < 14; i++) { + const a = r() * Math.PI * 2, d = r() * h * 0.32; + const cx = x + Math.cos(a) * d * 1.2 + Math.sin(t * 0.8 + i) * h * 4e-3, cy = y - h * 0.72 + Math.sin(a) * d * 0.6; + blot(g2, cx, cy, h * (0.12 + r() * 0.08), i % 3 ? col : mixHex(col, "#2a3a22", 0.45), 0.42 + 0.1 * (o.dark ?? 0)); + } + for (let i = 0; i < 40; i++) { + const a = r() * Math.PI * 2, d = r() * h * 0.38; + g2.fillStyle = rgba("#2f4a28", 0.5); + g2.beginPath(); + g2.arc(x + Math.cos(a) * d * 1.2, y - h * 0.72 + Math.sin(a) * d * 0.6, h * 8e-3, 0, Math.PI * 2); + g2.fill(); } - m.pad(t, S("bigegg"), [N.G2, N.D3], 0.035); - t = S("bigegg"); - const BE = CUE.bigegg; - m.pad(t, t + BE.open, [N.E2, N.B2], 0.05, 500); - for (let x = t + 0.6; x < t + BE.open - 0.05; x += 0.09) m.pluck(x, N.E3, 0.06 + 0.1 * (x - t) / BE.open, 0, { dur: 0.3, send: 0.1 }); - m.pluck(t + BE.surprise, N.D4, 0.3, 0, { bend: -2 }); - m.pluck(t + BE.surprise + 0.25, N.A3, 0.3, 0, { bend: -2 }); - m.pluck(t + BE.question + 0.05, N.B4, 0.22, 0.2, { bend: 2 }); - arp(t + BE.walk, S("bullied"), [Em, G], B / 2, 0.12); - t = S("bullied"); - m.pad(t, S("leave") + 0.5, [N.E2, N.B2, N.G3], 0.05, 700); - for (let i = 0; i < 12; i++) m.pluck(t + 0.3 + i * B, [N.E4, N.D4, N.B3, N.A3, N.G3, N.A3, N.B3, N.E3][i % 8], 0.16, -0.2, { dur: 2, bend: i % 4 === 3 ? -1 : 0 }); - t = S("leave"); - m.pad(t, S("marsh") + 0.3, [N.E2, N.B2], 0.045, 600); - m.melody(t + 0.4, B * 1.1, [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["E4", 2]], 0.13); - t = S("marsh"); - for (let x = t + 0.2; x < t + CUE.marsh.shots[0] - 0.02; x += 0.125) m.pluck(x, Math.floor((x - t) / 0.5) % 2 ? N.B2 : N.E2, 0.17, 0, { dur: 0.25, send: 0.05 }); - m.pad(t + CUE.marsh.hide, S("autumn") + 0.5, [N.E2], 0.04, 400); - for (let x = t + CUE.marsh.hide + 0.6; x < t + 13; x += 0.95) m.heart(x); - t = S("autumn"); - m.pad(t, S("winter") + 0.5, [N.E2, N.B2, N.G3], 0.04, 800); - arp(t + 0.3, S("winter"), [Em, Am, G, Em], B * 0.75, 0.13); - m.bell(t + CUE.autumn.swans + 0.6, N.B5, 0.07, 0.4); - m.bell(t + CUE.autumn.swans + 3, N.E6, 0.06, -0.3); - m.melody(t + CUE.autumn.swans + 1.4, B, [["B4", 1.5], ["D5", 0.5], ["E5", 1], ["G5", 1], ["E5", 2], ["D5", 1], ["B4", 1], ["A4", 2]], 0.11, 0); - t = S("winter"); - m.pad(t, S("spring") + 0.5, [N.E2, N.B2], 0.035, 350); - for (let i = 0; i < 6; i++) m.bell(t + 1 + i * 2.1, [N.E5, N.B4, N.D5, N.A4, N.B4, N.E5][i], 0.05, i % 2 ? 0.4 : -0.4); - t = S("spring"); - m.bell(t + 0.15, N.G5, 0.1, 0); - m.bell(t + 0.4, N.D6, 0.07, 0.3); - m.pad(t, S("reflection") + 0.5, [N.G2, N.D3, N.B3], 0.045); - const SP = CUE.spring; - arp(t + 0.5, t + SP.flapHard, [G, Em], B / 2, 0.15); - arp(t + SP.flapHard, S("reflection"), [G, Dm, Em, G], B / 4, 0.14, [0, 1, 2, 1, 2, 1]); - m.gliss(t + SP.lift - 0.05, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5, N.E5, N.G5], 0.05, 0.25); - m.melody(t + SP.lift + 0.4, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["E5", 0.5], ["D5", 2]], 0.13); - t = S("reflection"); - const RF = CUE.reflection; - m.pad(t, t + RF.reveal, [N.E2, N.B2, N.E3], 0.04, 600); - for (let i = 0; i < 10; i++) m.pluck(t + 0.8 + i * B * 1.05, [N.B3, N.D4, N.E4, N.D4, N.B3, N.A3, N.B3, N.D4, N.E4, N.G4][i], 0.12, 0, { dur: 2 }); - m.bell(t + RF.mirror, N.E5, 0.06, 0); - m.bell(t + RF.mirror + 0.3, N.B5, 0.04, 0.3); - m.gliss(t + RF.reveal - 0.05, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5, N.B5, N.D6], 0.06, 0.3); - [N.G5, N.B5, N.D6, N.G5].forEach((x, i) => m.bell(t + RF.reveal + i * 0.18, x, 0.08, i % 2 ? 0.3 : -0.3)); - m.pad(t + RF.reveal, S("accepted") + 0.5, [N.G2, N.D3, N.B3, N.D4], 0.05, 1200); - m.melody(t + RF.reveal + 1.2, B, [["G5", 1.5], ["E5", 0.5], ["D5", 1], ["B4", 1], ["D5", 2]], 0.14); - t = S("accepted"); - arp(t + 0.2, S("fly"), [G, Em, Dm, G], B / 2, 0.15); - m.pad(t, S("fly") + 0.3, [N.G2, N.D3], 0.04); - m.melody(t + 0.8, B, [["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.12); - t = S("fly"); - m.pad(t, S("ending") + 1, [N.G2, N.D3, N.G3, N.B3], 0.06, 1600); - arp(t, S("ending"), [Gh, G, Em, Gh], B / 4, 0.16, [0, 1, 2, 1, 2, 1, 0, 2]); - for (let i = 0; i < Math.floor(D("fly") / B); i++) m.block(t + i * B, 0.08, 800, 0); - m.melody(t + 0.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["A5", 0.5], ["B5", 2]], 0.15, 0); - t = S("ending"); - m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); - const EN = CUE.ending.end; - arp(t + 0.2, t + EN - 0.2, [G, Em, G, Dm], B * 0.75, 0.12); - m.gliss(t + EN, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.22); - m.bell(t + EN + 0.1, N.G5, 0.08); - m.bell(t + EN + 0.35, N.D6, 0.05, 0.3); - m.pluck(t + EN + 0.6, N.G3, 0.2, 0, { dur: 2 }); - m.pluck(t + EN + 0.6, N.D4, 0.15, 0, { dur: 2 }); - m.pluck(t + EN + 0.6, N.G4, 0.12, 0, { dur: 2 }); - void r; } - function effects(m) { - const S = sceneStart, D = sceneDur; - const r = m.r; - let t = S("title"); - const TT = CUE.title; - m.drop(t + TT.drop); - m.swish(t + TT.write); - m.swish(t + TT.write + 0.45); - m.swish(t + TT.write + 0.9); - m.thump(t + TT.seal + 0.01, 0.45); - for (let i = 0; i < 6; i++) m.peep(t + 2.6 + i * 0.65, 0.04, 0.3); - t = S("summer"); - m.wind(t, D("summer"), 0.025); - for (let x = t + 0.6; x < t + D("summer"); x += 1.1 + r() * 1.6) m.chirp(x, 0.035, r() * 1.4 - 0.7); - t = S("hatch"); - for (let k = 0; k < 4; k++) { - const tc = t + 0.4 + k * 0.75; - m.crack(tc + 0.05, 0.22, -0.3 + k * 0.2); - m.crack(tc + 0.25, 0.18, -0.3 + k * 0.2); - m.noiseHit(tc + 0.55, 0.08, "bandpass", 1800, 900, 0.18, -0.3 + k * 0.2, 2, 0.1); - m.peep(tc + 0.75, 0.1, -0.3 + k * 0.2); - for (let p = 1; p < 5; p++) { - const pt = tc + 1 + 2.3 * (p - 1); - if (pt - t < D("hatch")) m.peep(pt + 0.05, 0.07, -0.3 + k * 0.2); + function bush(g2, x, y, w, seed, dark = 0) { + const r = rng(seed); + for (let i = 0; i < 7; i++) blot(g2, x + (r() - 0.5) * w, y - r() * w * 0.35, w * (0.22 + r() * 0.15), mixHex("#4f6a3e", "#1e2a1c", dark), 0.55 + 0.25 * dark, 1.3, 0.8); + for (let i = 0; i < 10; i++) { + const px = x + (r() - 0.5) * w * 1.1, py = y - r() * w * 0.5; + brush(g2, [[px, py], [px + w * 0.05 * (r() - 0.5), py - w * 0.12]], Math.max(1, w * 0.02), { color: "#1e2a1c", alpha: 0.6, seed: seed + i }); + } + } + function stormCloud(g2, x, y, w, a, seed) { + if (a <= 0) return; + const r = rng(seed); + for (let i = 0; i < 8; i++) blot(g2, x + (r() - 0.5) * w, y + (r() - 0.5) * w * 0.15, w * (0.2 + r() * 0.15), "#3e4248", a * 0.55, 1.5, 0.6); + brush(g2, spline([[x - w * 0.5, y + w * 0.08], [x - w * 0.15, y + w * 0.12], [x + w * 0.2, y + w * 0.09], [x + w * 0.5, y + w * 0.13]], 6), Math.max(1.5, w * 0.01), { color: "#2a2c30", alpha: a * 0.7, dry: 0.6, seed }); + } + function flowers(g2, x0, x1, y0, y1, n, seed, a = 0.8) { + const r = rng(seed); + for (let i = 0; i < n; i++) { + const x = lerp(x0, x1, r()), y = lerp(y0, y1, r()), c2 = ["#e8d06a", "#f2f0e6", "#d88aa0"][i % 3]; + g2.fillStyle = rgba(c2, a); + g2.beginPath(); + g2.arc(x, y, 2 + r() * 3, 0, Math.PI * 2); + g2.fill(); + } + } + function shoutText(g2, text, x0, y0, dirx, size, t, o = {}) { + if (t <= 0) return; + const chars = [...text], per = o.per ?? 0.16, col = o.color ?? "#b8322a", sc = o.scatter ?? 0, seed = o.seed ?? 1; + chars.forEach((ch, i) => { + const q2 = t - i * per; + if (q2 <= 0) return; + const p = smooth(q2 / 0.5); + const x = x0 + dirx * (size * 0.45 + i * size * 0.98 * p + q2 * size * 0.25), y = y0 - size * 0.1 * Math.sin(i * 1.3) - q2 * size * 0.08; + const k = lerp(0.35, 1, p) * (1 + 0.04 * Math.sin(q2 * 30) * (1 - p)); + let a = Math.min(1, q2 * 5) * (o.fade ?? 1); + g2.save(); + g2.translate(x, y); + g2.rotate(-0.08 * dirx + 0.05 * Math.sin(i + q2 * 3)); + if (sc > 0) { + const s2 = smooth(sc), r = rng(seed * 31 + i); + a *= 1 - s2 * 0.9; + for (let qd = 0; qd < 4; qd++) { + const ox = (qd % 2 ? 1 : -1) * size * 0.5, oy = (qd < 2 ? -1 : 1) * size * 0.5; + g2.save(); + g2.beginPath(); + g2.rect(qd % 2 ? 0 : -size, qd < 2 ? -size : 0, size, size); + g2.clip(); + g2.translate(ox * s2 * 0.8 + s2 * size * 1.5 * dirx * (0.5 + r()), oy * s2 * 0.6 - s2 * size * 0.6 * r()); + g2.rotate(s2 * (r() - 0.5) * 1.5); + g2.globalAlpha *= a; + g2.fillStyle = col; + g2.font = `${size * k}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.fillText(ch, 0, 0); + g2.restore(); + } + for (let d = 0; d < 6; d++) { + const r2 = rng(seed + i * 7 + d); + g2.fillStyle = rgba(col, 0.6 * s2 * (1 - s2)); + g2.beginPath(); + g2.arc(dirx * s2 * size * (1 + r2() * 2), (r2() - 0.5) * size * 1.5 * s2, size * 0.04 * (1 + r2()), 0, Math.PI * 2); + g2.fill(); + } + } else { + g2.globalAlpha *= a; + g2.font = `${size * k}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.shadowColor = rgba(col, 0.35); + g2.shadowBlur = size * 0.06; + g2.lineJoin = "round"; + g2.strokeStyle = rgba(PAPER, 0.7); + g2.lineWidth = size * 0.08; + g2.strokeText(ch, 0, 0); + g2.fillStyle = col; + g2.fillText(ch, 0, 0); } - m.noiseHit(tc + 3, 0.05, "lowpass", 600, 200, 0.12, -0.3 + k * 0.2, 0.7, 0.05); + g2.restore(); + }); + } + function shoutWaves(g2, x, y, s, t, dirx, a = 1) { + for (let k = 0; k < 3; k++) { + const q2 = (t * 1.6 + k / 3) % 1; + g2.strokeStyle = rgba(INK, a * 0.6 * (1 - q2)); + g2.lineWidth = Math.max(1.2, s * 0.03); + g2.beginPath(); + g2.arc(x, y, s * (0.3 + q2 * 1.4), dirx > 0 ? -0.6 : Math.PI - 0.6, dirx > 0 ? 0.6 : Math.PI + 0.6); + g2.stroke(); + } + } + + // src/films/wolf/cues.ts + var CUE2 = { + title: { drop: 0.32, write: 1.1, seal: 4.55 }, + village: { boyIn: 4.8 }, + bored: { chew: 6.1, yawn: 8, idea: 9.8, sly: 10.3 }, + shout1: { hop: 0.6, cup: 2.4, shouts: [4.2, 5.6, 7] }, + run1: { hear: 0.6, grab: 2.8, run: 4.6, arrive: 7.3 }, + laugh1: { laugh: 0.5, angry: 4.6, leave: 6.6 }, + shout2: { days: 0.5, shouts: [4.1, 5.5], run: 7.2 }, + warn: { laugh: 0.5, point: 3.1 }, + wolf: { dark: 0.3, bush: 4, eyes: 4.9, creep: 7.3, reveal: 9.3 }, + cry: { scared: 0.5, toRock: 2.3, shouts: [4.1, 5.3, 7.2] }, + noone: { look: 0.6, shake: 2.6, wave: 5.2, work: 8 }, + chaos: { dash: 0.4, scatter: 1.8, grab: 4 }, + regret: { hug: 1.8, cry: 4.4 }, + moral: { write: 4.9, end: 8.2 } + }; + + // src/films/wolf/scenes.ts + var ks2 = (s) => s.u * (s.portrait ? 0.95 : 1); + var cam2 = (s, z, x, y, extra = {}) => ({ z: z * (s.portrait ? 1.22 : 1), x, y, ...extra }); + function sky2(g2, W2, y1, top, a0, bottom, a1) { + const gr = g2.createLinearGradient(0, 0, 0, y1); + gr.addColorStop(0, rgba(top, a0)); + gr.addColorStop(1, rgba(bottom, a1)); + g2.fillStyle = gr; + g2.fillRect(0, 0, W2, y1); + } + function band2(g2, W2, y0, y1, color, a, seed, rough = 18) { + const pts = []; + for (let i = 0; i <= 24; i++) pts.push([i / 24 * W2, y0 + nz(i * 0.6, seed) * rough]); + const gr = g2.createLinearGradient(0, y0 - rough, 0, y1); + gr.addColorStop(0, rgba(color, a)); + gr.addColorStop(1, rgba(color, a * 0.5)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, y1); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, y1); + g2.closePath(); + g2.fill(); + brush(g2, pts, 2.2, { color, alpha: Math.min(1, a * 1.3), seed, jitter: 0.6, dry: 0.5 }); + } + function strokesH2(g2, W2, y0, y1, n, color, a, seed, u) { + const r = rng(seed); + for (let i = 0; i < n; i++) { + const y = lerp(y0, y1, r()), x = r() * W2, len = (30 + r() * 120) * u; + brush(g2, [[x, y], [x + len / 2, y + (r() - 0.5) * 4 * u], [x + len, y]], (1.5 + r() * 2.5) * u, { color, alpha: a * (0.5 + r() * 0.5), seed: seed + i, dry: 0.6, jitter: 0.5 }); } - t = S("bigegg"); - const BE = CUE.bigegg; - for (let x = t + 0.5; x < t + BE.crackFrom; x += Math.max(0.12, 0.5 - (x - t) * 0.12)) m.block(x, 0.05 + (x - t) * 0.02, 420, 0); - m.crack(t + BE.crackFrom + 0.05, 0.2); - m.crack(t + BE.crackFrom + 0.4, 0.22); - m.crack(t + BE.open, 0.35, 0, true); - m.noiseHit(t + BE.surprise, 0.25, "bandpass", 2400, 1200, 0.06, -0.4, 1.2, 0.2); - m.peep(t + BE.surprise + 0.1, 0.08, -0.4); - m.peep(t + BE.surprise + 0.2, 0.08, -0.5); - for (let x = t + BE.walk + 0.1; x < t + BE.walk + 2.6; x += 0.6) m.noiseHit(x, 0.05, "lowpass", 600, 200, 0.1, 0, 0.7, 0.05); - t = S("bullied"); - const BU = CUE.bullied; - for (let x = t + BU.laugh1[0] + 0.1; x < t + BU.laugh1[1] - 0.2; x += 0.35 + r() * 0.3) m.quack(x, 0.09, -0.5 + r() * 0.3, 1.25 + r() * 0.2); - for (let x = t + BU.laugh2[0] + 0.1; x < t + BU.laugh2[1] - 0.2; x += 0.4 + r() * 0.35) m.quack(x, 0.07, -0.5 + r() * 0.3, 1.25 + r() * 0.2); - m.flap(t + BU.roosterIn, 6, 9, 0.12, 0.5); - m.tone(t + BU.roosterIn + 0.2, 600, 900, 0.25, 0.08, "sawtooth", 0.5, 0.3); - m.tone(t + BU.roosterIn + 0.45, 900, 500, 0.5, 0.07, "sawtooth", 0.5, 0.4); - for (const p of BU.pecks) { - m.block(t + p, 0.25, 1500, 0.2); - m.flap(t + p - 0.15, 2, 10, 0.08, 0.3); + } + function particles2(n, seed, fn) { + const r = rng(seed); + for (let i = 0; i < n; i++) fn(r, i); + } + function writeText2(g2, text, x, y, fs, t0, per, t, o = {}) { + const chars = [...text], gap = o.gap ?? 1.08; + g2.save(); + g2.font = `${fs}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + chars.forEach((ch, i) => { + const off = (i - (chars.length - 1) / 2) * fs * gap; + const cx = o.vertical ? x : x + off, cy = o.vertical ? y + off : y; + const p = inv(t0 + i * per, t0 + i * per + per * 1.2, t); + if (p <= 0) return; + g2.save(); + g2.beginPath(); + g2.rect(cx - fs * 0.7, cy - fs * 0.7, fs * 1.4, fs * 1.4 * easeOut(p)); + g2.clip(); + g2.shadowColor = rgba(INK, 0.35); + g2.shadowBlur = fs * 0.06; + g2.fillStyle = o.color ?? INK; + g2.fillText(ch, cx, cy); + g2.restore(); + }); + g2.restore(); + } + function marks(g2, x, y, s, a, txt, color = INK) { + if (a <= 0) return; + g2.save(); + g2.globalAlpha = a; + g2.fillStyle = color; + g2.font = `${s}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.fillText(txt, x, y); + g2.restore(); + } + function sparkle(g2, x, y, s, a) { + if (a <= 0) return; + for (let i = 0; i < 4; i++) { + const ang = i * Math.PI / 2 + 0.3; + brush(g2, [[x + Math.cos(ang) * s * 0.3, y + Math.sin(ang) * s * 0.3], [x + Math.cos(ang) * s, y + Math.sin(ang) * s]], s * 0.12, { alpha: a * 0.85, taperIn: 0.1, taperOut: 0.6, seed: i }); } - m.swish(t + BU.shoo, 0.07); - t = S("leave"); - m.wind(t, D("leave") + 1, 0.07, -0.2); - for (let x = t + CUE.leave.walk + 0.2; x < t + D("leave"); x += 0.62) m.noiseHit(x, 0.04, "lowpass", 500, 200, 0.05, 0, 0.7, 0.05); - t = S("marsh"); - m.lap(t, t + D("marsh"), 0.035); - const MA = CUE.marsh; - for (let x = t + MA.laugh[0] + 0.1; x < t + MA.laugh[1] - 0.2; x += 0.3 + r() * 0.3) m.quack(x, 0.1, 0.4 + r() * 0.3, 0.95 + r() * 0.15); - m.shot(t + MA.shots[0], 0.6); - m.shot(t + MA.shots[1], 0.45); - m.flap(t + MA.scatter, 14, 12, 0.13, 0.4); - for (let i = 0; i < 6; i++) m.quack(t + MA.scatter + 0.05 + i * 0.12, 0.08, 0.5, 1.1); - m.splash(t + MA.hide + 0.2, 0.12); - t = S("autumn"); - m.wind(t, D("autumn"), 0.03, 0.2); - const AU = CUE.autumn.swans; - m.swanCall(t + AU + 0.8, 0.06, 0.5); - m.swanCall(t + AU + 3, 0.07, 0.1); - m.swanCall(t + AU + 5.6, 0.05, -0.5); - m.flap(t + AU + 1.5, 12, 1.4, 0.04, 0.2); - t = S("winter"); - m.wind(t, D("winter") + 0.8, 0.12, 0); - m.wind(t + 2, D("winter") - 2, 0.06, 0.6); - m.crack(t + CUE.winter.ice, 0.2, 0, true); - m.noiseHit(t + CUE.winter.ice + 0.05, 0.9, "bandpass", 300, 120, 0.12, 0, 4, 0.5); - t = S("spring"); - for (let x = t + 0.4; x < t + D("spring"); x += 0.7 + r() * 1.1) m.chirp(x, 0.04, r() * 1.4 - 0.7); - const SP = CUE.spring; - m.flap(t + SP.spread, 2, 2, 0.1, 0); - m.flap(t + SP.spread + 0.9, Math.floor((SP.flapHard - SP.spread - 0.9) * 0.83), 0.83, 0.08, 0); - m.flap(t + SP.flapHard, Math.floor((SP.lift - SP.flapHard) * 1.6), 1.6, 0.13, 0); - m.flap(t + SP.lift, 6, 1.46, 0.1, 0.2); - m.noiseHit(t + SP.lift - 0.1, 1.2, "bandpass", 400, 2400, 0.12, 0, 1, 0.4); - m.pluck(t + SP.question, N.B4, 0.2, 0, { bend: 2 }); - t = S("reflection"); - const RF = CUE.reflection; - m.flap(t, 5, 7, 0.1, -0.2); - m.splash(t + RF.land, 0.3); - m.lap(t + 1.2, t + D("reflection"), 0.03); - m.bell(t + RF.reveal, 91, 0.06, 0, m.sfx); - t = S("accepted"); - m.lap(t, t + D("accepted"), 0.03); - const CH = CUE.accepted.cheer; - for (let x = t + CH + 0.1; x < t + CH + 4.5; x += 0.11 + r() * 0.08) m.clap(x, 0.06 * (1 - (x - t - CH) / 5), -0.55 + r() * 0.2); - for (let i = 0; i < 4; i++) m.chirp(t + CH + 0.4 + i * 1.3, 0.03, 0.4); - t = S("fly"); - m.wind(t, D("fly") + 1, 0.07, 0); - m.flap(t + 0.3, Math.floor(D("fly") * 3.2 / 2 / Math.PI * 2), 3.2 / Math.PI, 0.09, 0); - m.noiseHit(t + 0.9, 1.6, "bandpass", 300, 2e3, 0.1, 0, 1, 0.5); - t = S("ending"); - m.lap(t, t + D("ending"), 0.025); - m.thump(t + CUE.ending.end + 0.12, 0.4); } - var cached = null; - function buildSoundtrack(voiceUrl, onStage) { - if (cached) return cached; - cached = (async () => { - const len = Math.ceil(SR * (TOTAL + 0.3)); - const ctx = new OfflineAudioContext(2, len, SR); - onStage?.("\u52A0\u8F7D\u65C1\u767D\u2026"); - let voiceBuf = null; - try { - const ab = await (await fetch(voiceUrl)).arrayBuffer(); - voiceBuf = await ctx.decodeAudioData(ab); - } catch (e) { - console.warn("voice load failed", e); + function hoePose(ph, kind) { + const st = 0.5 + 0.5 * Math.sin(ph); + const th = lerp(-2.05, 0.95, Math.pow(st, 1.6)); + const piv = [0.06, -0.6]; + const d = [Math.cos(th), Math.sin(th)]; + const a = [piv[0] - d[0] * 0.12, piv[1] - d[1] * 0.12], b = [piv[0] + d[0] * 0.78, piv[1] + d[1] * 0.78]; + return { hl: [piv[0] - d[0] * 0.08, piv[1] - d[1] * 0.08], hr: [piv[0] + d[0] * 0.14, piv[1] + d[1] * 0.14], fl: [-0.1, 0], fr: [0.12, 0], lean: 0.08 + 0.18 * st, tool: { kind: "hoe", a, b } }; + void kind; + } + function carryPose(ph, kind, run, tool = "hoe") { + const w = POSE.walk(ph, kind, run); + const a = tool === "hoe" ? [-0.3, -0.62] : [-0.42, -0.77], b = tool === "hoe" ? [0.42, -0.98] : [0.42, -0.8]; + return { ...w, hr: [0.1, -0.84], tool: { kind: tool, a, b } }; + } + function standPose(kind, mode = "still", t = 0) { + const base = POSE.stand(kind); + if (mode === "hips") return { ...base, hl: [-0.12, -0.5], hr: [0.13, -0.5] }; + if (mode === "point") return { ...base, hr: [0.34, -0.8 + 0.02 * Math.sin(t * 8)], hl: [-0.12, -0.5], lean: 0.08 }; + if (mode === "wave") return { ...base, hr: [0.22 + 0.06 * Math.sin(t * 9), -0.86], hl: [-0.06, -0.46] }; + if (mode === "shade") return { ...base, hr: [0.1, -0.93] }; + return base; + } + var FARMERS = [ + { kind: "man", hat: "scarf", jacket: "#5d7385", pants: "#3a4048", tool: "hoe" }, + { kind: "man", hat: "straw", jacket: "#6b5a48", pants: "#3e3a36", tool: "pole" }, + { kind: "woman", hat: "bun", jacket: "#8a5a62", pants: "#3a3a44", tool: "hoe" }, + { kind: "man", hat: "none", jacket: "#4f6a5a", pants: "#2e3438", tool: "hoe" } + ]; + function farmer(g2, i, x, y, s, t, pose, extra = {}) { + const f = FARMERS[i % FARMERS.length]; + person(g2, { x, y, s, t, kind: f.kind, hat: f.hat, jacket: f.jacket, pants: f.pants, seed: 10 + i * 7, ...POSE.stand(f.kind), ...pose, ...extra }); + } + function boy(g2, x, y, s, t, pose, extra = {}) { + person(g2, { x, y, s, t, kind: "boy", seed: 3, ...POSE.stand("boy"), ...pose, ...extra }); + } + var SHOUT = { hl: [0.16, -0.8], hr: [0.15, -0.78], fl: [-0.08, 0], fr: [0.1, 0], lean: 0.12 }; + var LAUGH = (t) => ({ hl: [0.05, -0.5], hr: [0.08, -0.52], fl: [-0.06, 0], fr: [0.08, 0], lean: -0.14 - 0.05 * Math.abs(Math.sin(t * 9)), bob: -0.01 * Math.abs(Math.sin(t * 9)), expr: { eye: "happy", mouth: "laugh" } }); + var SCARED = (t) => ({ hl: [-0.14, -1 + 0.02 * Math.sin(t * 20)], hr: [0.2, -0.98], fl: [-0.09, 0], fr: [0.09, 0], lean: -0.05, shiver: 1, expr: { eye: "wide", mouth: "shout", brow: "worried" } }); + var SIT = { hipY: -0.13, fl: [0.22, 0], fr: [0.26, -0.01], hl: [0.2, -0.34], hr: [0.22, -0.32], lean: 0.2 }; + var hillY = (s, x) => s.gy + 30 * s.u + 110 * s.u * Math.pow((x - s.W * 0.48) / (s.W * 0.75), 2); + function bgHill(mood, o = {}) { + return (s) => { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + const seed = o.seed ?? 1; + if (mood === "day") sky2(g2, W2, gy, "#9fbccb", 0.22, "#ffffff", 0); + if (mood === "storm") sky2(g2, W2, gy + 40 * u, "#3e464e", 0.45, "#8a9094", 0.2); + if (mood === "sunset") { + sky2(g2, W2, gy, "#e08a50", 0.35, "#f0c890", 0.45); + sun(g2, X(-0.35), gy - 60 * u, 110 * u, "#c8463a", 0.9); } - onStage?.("\u5408\u6210\u914D\u4E50\u4E0E\u97F3\u6548\u2026"); - const m = new Mixer(ctx); - const mg = m.music.gain; - mg.setValueAtTime(0.9, 0); - for (const s of TL.scenes) { - if (!s.voice_start || !s.voice_dur) continue; - const a = s.voice_start, b = s.voice_start + s.voice_dur; - mg.setTargetAtTime(0.42, Math.max(0, a - 0.25), 0.08); - mg.setTargetAtTime(0.9, b + 0.1, 0.25); + if (mood === "dusk") sky2(g2, W2, gy, "#4a5068", 0.4, "#d8a878", 0.35); + const mc = mood === "storm" ? "#4a5258" : mood === "sunset" ? "#7a5048" : mood === "dusk" ? "#3a3a48" : "#7d8a8c"; + mountains(g2, W2, gy - 40 * u, 170 * u, mc, mood === "day" ? 0.3 : 0.45, 3 + seed); + const vx = X(-0.8), vy = gy + 10 * u; + for (let i = 0; i < 4; i++) cottage(g2, vx + i * 46 * u - i % 2 * 10 * u, vy + i % 2 * 14 * u, 40 * u, 30 + i); + const pts = []; + for (let i = 0; i <= 30; i++) { + const x = i / 30 * W2; + pts.push([x, hillY(s, x) + nz(i * 0.7, seed) * 6 * u]); } - score(m); - effects(m); - m.flushPlucks(); - m.q.sort((a, b) => a.t - b.t); - let qi = 0; - const until = (tt) => { - while (qi < m.q.length && m.q[qi].t < tt) { - m.q[qi].fn(); - qi++; + const gc = mood === "storm" ? "#6a7a5a" : mood === "sunset" ? "#8a8a50" : mood === "dusk" ? "#4a5a48" : "#8fae6a"; + const gr = g2.createLinearGradient(0, gy, 0, H2); + gr.addColorStop(0, rgba(gc, 0.6)); + gr.addColorStop(1, rgba(mixHex(gc, "#4a5a32", 0.4), 0.5)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, H2); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, H2); + g2.closePath(); + g2.fill(); + brush(g2, pts, 3 * u, { color: "#3e5a2e", alpha: 0.7, seed, dry: 0.5, jitter: 0.5 }); + strokesH2(g2, W2, gy + 80 * u, H2, 30, "#4f6a3e", 0.35, seed + 9, u); + if (mood === "day") flowers(g2, 0, W2, gy + 90 * u, H2 - 20 * u, 40, seed + 4); + if (o.tree !== false) bigTree(g2, X(0.62), hillY(s, X(0.62)) + 10 * u, 520 * u, 0, 17, { color: mood === "storm" ? "#4a6a3e" : mood === "sunset" ? "#6a7040" : "#5f8a4c" }); + if (o.rock !== false) rock(g2, X(0.05), hillY(s, X(0.05)) + 40 * u, 190 * u, 110 * u, 5); + }; + } + var rockTop = (s) => [s.X(0.05), hillY(s, s.X(0.05)) + 40 * s.u - 105 * s.u]; + function bgVillage(storm = false) { + return (s) => { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky2(g2, W2, gy, storm ? "#4e565e" : "#a9c3c9", storm ? 0.4 : 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 70 * u, 200 * u, storm ? "#4a5258" : "#7d8a8c", 0.35, 41); + mountains(g2, W2, gy - 20 * u, 120 * u, storm ? "#3a4248" : "#5e6c62", 0.4, 47); + band2(g2, W2, gy - 10 * u, H2, storm ? "#7a8a6a" : "#a8b878", 0.35, 21); + terraces(g2, X(-0.1), gy + 30 * u, W2 + 20 * u, H2, 6, 9); + for (const [fx, dy, sz, sd] of [[-0.88, 40, 150, 1], [-0.62, 70, 130, 2], [-0.92, 160, 170, 3]]) cottage(g2, X(fx), gy + dy * u, sz * u, sd); + const path = spline([[X(-0.55), H2 + 20 * u], [X(-0.35), gy + 120 * u], [X(-0.2), gy + 10 * u], [X(0.1), gy - 60 * u]], 10); + for (let i = 0; i < path.length - 1; i++) brush(g2, [path[i], path[i + 1]], lerp(90, 14, i / path.length) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + if (storm) { + g2.fillStyle = rgba("#2a3038", 0.18); + g2.fillRect(0, 0, W2, H2); + } + }; + } + var fieldSpot = (s, i) => [s.X(0.05 + i * 0.26), s.gy + (120 + i % 2 * 70) * s.u]; + function bgSlope(s) { + const { g: g2, W: W2, H: H2, u, X } = s; + sky2(g2, W2, H2 * 0.5, "#9fbccb", 0.22, "#ffffff", 0); + mountains(g2, W2, H2 * 0.42, 200 * u, "#7d8a8c", 0.3, 61); + const slope = [[-20 * u, H2 * 0.62], [X(-0.3), H2 * 0.5], [X(0.3), H2 * 0.32], [X(0.75), H2 * 0.24], [W2 + 20 * u, H2 * 0.26], [W2 + 20 * u, H2 + 20 * u], [-20 * u, H2 + 20 * u]]; + wash(g2, slope, "#8fae6a", 0.55); + brush(g2, spline(slope.slice(0, 5), 10), 3 * u, { color: "#3e5a2e", alpha: 0.7, seed: 3, dry: 0.5 }); + terraces(g2, -20 * u, H2 * 0.74, X(-0.1), H2 + 20 * u, 4, 13); + for (const [fx, fy, sz, sd] of [[-0.9, 0.72, 120, 1], [-0.7, 0.8, 100, 2]]) cottage(g2, X(fx), H2 * fy, sz * u, sd); + const path = slopePath(s); + for (let i = 0; i < path.length - 1; i++) brush(g2, [path[i], path[i + 1]], 34 * u, { color: "#d8c49a", alpha: 0.6, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + strokesH2(g2, W2, H2 * 0.4, H2, 34, "#4f6a3e", 0.3, 7, u); + bigTree(g2, X(0.88), H2 * 0.26, 360 * u, 0, 21); + rock(g2, X(0.7), H2 * 0.27, 120 * u, 70 * u, 8); + } + var slopePath = (s) => spline([[s.X(-0.85), s.H * 0.92], [s.X(-0.45), s.H * 0.68], [s.X(-0.05), s.H * 0.55], [s.X(0.3), s.H * 0.4], [s.X(0.6), s.H * 0.3]], 12); + var along = (pts, p) => { + const f = clamp(p) * (pts.length - 1), i = Math.min(pts.length - 2, Math.floor(f)), k = f - i; + return [lerp(pts[i][0], pts[i + 1][0], k), lerp(pts[i][1], pts[i + 1][1], k)]; + }; + var FLOCK = [ + { f: -0.55, dy: 60, s: 1, face: 1, seed: 1 }, + { f: -0.32, dy: 120, s: 1.1, face: -1, seed: 2 }, + { f: -0.15, dy: 40, s: 0.9, face: 1, seed: 3 }, + { f: 0.28, dy: 140, s: 1.15, face: -1, seed: 4 }, + { f: 0.45, dy: 70, s: 0.95, face: 1, seed: 5 }, + { f: 0.82, dy: 110, s: 1.05, face: -1, seed: 6 }, + { f: -0.75, dy: 150, s: 0.7, face: 1, seed: 7 } + ]; + function flock(s, o = () => ({})) { + const { g: g2, u, t, X } = s; + const k = ks2(s); + FLOCK.forEach((sp, i) => { + const e = o(sp, i); + const x = X(sp.f) + (e.dx ?? 0), y = hillY(s, X(sp.f)) + sp.dy * u + (e.dy ?? 0); + const graze = e.graze ?? (0.5 + 0.5 * Math.sin(t * 0.5 + sp.seed * 2) > 0.35 ? 1 : 0.2); + sheep(g2, { x, y, s: 150 * k * sp.s, t, face: e.face ?? sp.face, seed: sp.seed, graze, look: e.look, run: e.run, alpha: e.alpha, baa: e.baa, scared: e.scared }); + }); + } + var SCENES3 = { + title: { + label: "\u7247\u5934", + draw(s) { + const { g: g2, W: W2, H: H2, u, t, portrait } = s; + const C = CUE2.title; + const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.42; + if (t < C.drop) { + g2.fillStyle = rgba(INK, 0.95); + g2.beginPath(); + g2.ellipse(cx, lerp(-30 * u, cy, easeIn(t / C.drop)), 9 * u, 15 * u, 0, 0, Math.PI * 2); + g2.fill(); + } + const p = easeOut(inv(C.drop, 2.4, t)); + if (p > 0) { + const R = (portrait ? 420 : 350) * u * p; + wash(g2, blobLoop(cx, cy, R, 9, 0.06, 32), "#8a9a7a", 0.14 + 0.16 * (1 - p), { edge: 1.4 }); + particles2(10, 71, (r) => { + const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(C.drop, 0.8, t)); + g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + g2.beginPath(); + g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, (3 + r() * 8) * u, 0, Math.PI * 2); + g2.fill(); + }); + } + const a = smooth(inv(0.8, 2.6, t)); + if (a > 0) { + g2.save(); + g2.globalAlpha = a; + const by = portrait ? H2 * 0.82 : H2 * 0.88; + mountains(g2, W2, by - 40 * u, 120 * u, "#7d8a8c", 0.3, 5, { soft: 1 }); + band2(g2, W2, by, H2, "#8fae6a", 0.45, 3, 10 * u); + bush(g2, W2 * 0.86, by + 10 * u, 160 * u, 4, 0.7); + g2.restore(); + eyesInDark(g2, W2 * 0.86, by - 20 * u, 14 * u, t, smooth(inv(3.6, 4.2, t)) * a); + } + const fs = (portrait ? 230 : 210) * u; + writeText2(g2, "\u72FC\u6765\u4E86", cx, cy, fs, C.write, 0.45, t, { vertical: portrait }); + const sa = smooth(inv(2.9, 3.6, t)); + if (sa > 0) { + g2.save(); + g2.globalAlpha = sa; + g2.font = `${46 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; + g2.fillText("\u4F0A \u7D22 \u5BD3 \u8A00", cx, sy); + brush(g2, [[cx - 150 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 150 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); + g2.restore(); + } + const sp = inv(C.seal, C.seal + 0.3, t); + if (sp > 0) { + const sc = lerp(1.5, 1, easeOut(sp)), sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; + g2.save(); + g2.translate(sx, sy); + g2.scale(sc, sc); + g2.rotate(-0.04); + seal(g2, 0, 0, 92 * u, ["\u5BD3\u8A00"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + const wy = portrait ? H2 * 0.8 : H2 * 0.92; + const wp = inv(1.8, 6.6, t); + if (wp > 0) { + const x0 = lerp(-150 * u, W2 * 0.6, wp); + for (let i = 0; i < 3; i++) sheep(g2, { x: x0 + 150 * u - i * 120 * u, y: wy + i % 2 * 8 * u, s: 85 * u, t, walk: 1, seed: i + 1, graze: 0 }); + boy(g2, x0 - 260 * u, wy, 170 * u, t, POSE.walk(t * 6, "boy"), { tool: { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] }, expr: { mouth: "smile" } }); + } + }, + enterAt: [0.5, 0.45] + }, + village: { + label: "\u5C71\u6751", + bg: bgVillage(false), + draw(s) { + const { g: g2, u, t, gy, X, H: H2 } = s; + const k = ks2(s); + for (const [fx, dy, sz] of [[-0.88, 40, 150], [-0.62, 70, 130]]) chimneySmoke(g2, X(fx) + sz * 0.3 * u, gy + dy * u - sz * 0.8 * u, 60 * u, t, fx * 10); + for (let i = 0; i < 3; i++) { + const [x, y] = fieldSpot(s, i); + farmer(g2, i, x, y, 330 * k, t, hoePose(t * 2.2 + i * 1.7, FARMERS[i].kind), { face: i === 1 ? -1 : 1 }); + } + const p = easeInOut(inv(CUE2.village.boyIn - 1.2, s.D + 1, t)); + const path = spline([[X(-0.6), H2 + 60 * u], [X(-0.4), gy + 160 * u], [X(-0.22), gy + 40 * u], [X(0.05), gy - 40 * u]], 10); + for (let i = 0; i < 4; i++) { + const q2 = clamp(p * 1.05 - 0.02 + i * 0.05); + if (q2 <= 0) continue; + const [x, y] = along(path, q2); + sheep(g2, { x, y, s: 130 * k * (1 - q2 * 0.5), t, walk: 1, seed: i + 2, graze: 0 }); + } + const qb = clamp(p * 1.05 - 0.06); + if (qb > 0) { + const [x, y] = along(path, qb); + boy(g2, x, y, 250 * k * (1 - qb * 0.5), t, POSE.walk(t * 6, "boy"), { tool: { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] }, expr: { mouth: "smile" } }); + } + }, + cam(s) { + const p = easeInOut(inv(CUE2.village.boyIn - 0.5, s.D, s.t)); + return cam2(s, lerp(1.08, 1.35, p), lerp(s.W / 2, s.X(-0.35), p), lerp(s.gy + 60 * s.u, s.gy + 120 * s.u, p)); + }, + enterAt: [0.5, 0.5] + }, + bored: { + label: "\u5C71\u5761\u4E0A", + bg: bgHill("day", { rock: false }), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.bored; + cloud(g2, X(-0.3) + t * 12 * u, s.gy - 420 * u, 380 * u, 3, 0.25); + flock(s); + const bx = X(0.42), by = hillY(s, X(0.42)) + 50 * u; + const yawn = smooth(inv(C.yawn, C.yawn + 0.4, t)) * (1 - smooth(inv(C.yawn + 1.3, C.yawn + 1.7, t))); + const idea = t > C.idea; + g2.save(); + g2.translate(bx, by); + g2.rotate(-Math.PI / 2); + boy(g2, 0, 0, 300 * k, t, { + hl: yawn > 0.3 ? [-0.06, -1.22] : [-0.05, -0.96], + hr: yawn > 0.3 ? [0.04, -1.25] : [0.01, -0.98], + fl: [0, 0], + fr: [0.02, -0.2], + lean: 0, + expr: { eye: idea ? t > C.sly ? "sly" : "wide" : yawn > 0.3 ? "closed" : "open", mouth: yawn > 0.3 ? "shout" : t > C.chew && !idea ? "chew" : idea ? "smile" : "line", brow: idea ? "up" : "none" }, + look: t > C.sly ? 1 : 0 + }); + g2.restore(); + if (t > C.chew - 0.3 && yawn < 0.3) { + const mx = bx - 300 * k * 0.8, my = by - 300 * k * 0.2; + brush(g2, [[mx, my], [mx + 40 * u, my - 30 * u + 6 * u * Math.sin(t * 6)], [mx + 80 * u, my - 70 * u + 10 * u * Math.sin(t * 6)]], 4 * u, { color: "#5f8a3c", alpha: 0.9, taperIn: 0, taperOut: 0.7, seed: 4 }); + } + sparkle(g2, bx - 300 * k * 0.98, by - 300 * k * 0.42, 40 * u, smooth(inv(C.idea, C.idea + 0.2, t)) * (1 - smooth(inv(C.idea + 1.2, C.idea + 1.6, t)))); + if (yawn > 0.3) marks(g2, bx - 300 * k * 1.05, by - 300 * k * 0.5, 50 * u, yawn, "\uFF5E"); + }, + cam(s) { + const C = CUE2.bored, bx = s.X(0.42), by = hillY(s, s.X(0.42)); + const p1 = easeInOut(inv(C.yawn - 4.5, C.yawn - 2, s.t)), p2 = easeInOut(inv(C.idea - 0.5, C.idea + 0.8, s.t)); + return cam2(s, lerp(lerp(1.05, 1.5, p1), 2.2, p2), lerp(lerp(s.W / 2, bx - 80 * s.u, p1), bx - 230 * s.u, p2), lerp(lerp(s.gy + 50 * s.u, by, p1), by - 30 * s.u, p2)); + }, + enterAt: [0.5, 0.5] + }, + shout1: { + label: "\u7B2C\u4E00\u6B21\u558A", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.shout1; + flock(s, (sp) => t > C.shouts[0] + 0.1 ? { graze: 0, look: 0.6, scared: 1, face: sp.f > 0.05 ? -1 : 1 } : {}); + const [rx, ry] = rockTop(s); + const hop = inv(C.hop, C.hop + 0.7, t); + const x = lerp(X(-0.2), rx, easeInOut(hop)), y = lerp(hillY(s, X(-0.2)) + 30 * u, ry, easeInOut(hop)) - Math.sin(Math.PI * hop) * 90 * u; + const cupping = t > C.cup; + const shouting = C.shouts.some((st) => t > st && t < st + 1.1); + boy(g2, x, y, 300 * k, t, cupping ? SHOUT : hop > 0 && hop < 1 ? { hl: [-0.12, -0.7], hr: [0.15, -0.72], fl: [-0.04, -0.06], fr: [0.08, -0.08] } : POSE.stand("boy"), { + face: -1, + expr: { eye: shouting ? "closed" : "sly", mouth: shouting ? "shout" : "smile", brow: shouting ? "up" : "none" }, + tool: cupping ? void 0 : { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] } + }); + if (cupping) { + const mx = x - 300 * k * 0.25, my = y - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 1); + C.shouts.forEach((st, i) => shoutText(g2, i === 2 ? "\u5FEB\u6765\u5440\uFF01" : "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 40 * u, -1, 130 * u, t - st, { fade: 1 - smooth(inv(st + 2.6, st + 3.4, t)), seed: i })); + } + }, + cam(s) { + const [rx, ry] = rockTop(s), p = easeInOut(inv(3.8, 5, s.t)); + return cam2(s, lerp(1.6, 1.12, p), lerp(rx, s.X(-0.25), p), lerp(ry - 80 * s.u, s.gy - 20 * s.u, p)); + }, + enterAt: [0.3, 0.6] + }, + run1: { + label: "\u519C\u6C11\u4E0A\u5C71", + bg: bgSlope, + draw(s) { + const { g: g2, u, t, X, H: H2 } = s; + const k = ks2(s), C = CUE2.run1; + const path = slopePath(s); + boy(g2, X(0.7), H2 * 0.27 - 66 * u, 140 * k, t, { hl: [0.05, -0.5], hr: [0.08, -0.52] }, { face: -1, expr: { eye: "happy", mouth: "smile" } }); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const p = easeInOut(inv(C.run + i * 0.15, C.arrive + i * 0.2, t)) * (0.92 - i * 0.07); + const start = along(path, 0.04 + i * 0.03); + const [x, y] = p > 0 ? along(path, 0.04 + i * 0.03 + p) : start; + const fx = x + (p > 0 ? 0 : (i - 1.5) * 90 * u), fy = y + i % 2 * 20 * u; + let pose; + if (t < C.grab) pose = t > C.hear + i * 0.2 ? { ...standPose(f.kind, "shade"), tilt: -0.25 } : hoePose(t * 2.2 + i, f.kind); + else if (p <= 0) pose = carryPose(0, f.kind, 0, f.tool); + else if (t < C.arrive + i * 0.2) pose = carryPose(t * 12 + i, f.kind, 1, f.tool); + else pose = { ...standPose(f.kind, "shade") }; + const arrived = t > C.arrive + i * 0.2; + const lookFace = arrived ? Math.sin(t * 2.5 + i * 1.3) > 0 ? 1 : -1 : 1; + farmer(g2, i, fx, fy, 300 * k, t, pose, { face: lookFace, expr: { eye: "wide", mouth: t > C.grab ? "open" : "line", brow: arrived ? "worried" : "up" }, sweat: arrived ? 1 : 0, tilt: arrived ? 0 : -0.1 }); + if (arrived) marks(g2, fx + 10 * u, fy - 300 * k * 1.12, 70 * u, smooth(inv(C.arrive + 0.3 + i * 0.2, C.arrive + 0.6 + i * 0.2, t)), "\uFF1F"); + } + }, + cam(s) { + const C = CUE2.run1, path = slopePath(s); + const p = easeInOut(inv(C.run, C.arrive + 0.6, s.t)) * 0.85; + const [x, y] = along(path, 0.05 + p); + return cam2(s, 1.55, x + 80 * s.u, y - 90 * s.u); + }, + enterAt: [0.2, 0.8] + }, + laugh1: { + label: "\u54C8\u54C8\u5927\u7B11", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.laugh1; + flock(s); + const [rx, ry] = rockTop(s); + const laughing = t > C.laugh && t < C.angry + 0.8; + boy(g2, rx, ry, 300 * k, t, laughing ? LAUGH(t) : { ...POSE.stand("boy"), expr: { eye: "happy", mouth: "smile" } }, { face: -1 }); + if (laughing) soundMarks(g2, rx - 120 * u, ry - 330 * k, 50 * u, 0.8, 3, -1); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const leave = easeIn(inv(C.leave + i * 0.25, C.leave + 3 + i * 0.25, t)); + const x = X(-0.85 + i * 0.17) - leave * 700 * u, y = hillY(s, X(-0.85 + i * 0.17)) + (30 + i % 2 * 40) * u; + const angry = t > C.angry; + const pose = leave > 0 ? carryPose(t * 7 + i, f.kind, 0, f.tool) : angry ? standPose(f.kind, "hips") : { ...standPose(f.kind, "shade") }; + farmer(g2, i, x, y, 320 * k, t, pose, { + face: leave > 0 ? -1 : 1, + tilt: angry && leave <= 0 ? 0.12 * Math.sin(t * 7 + i) : 0, + sweat: angry ? 0 : 1, + expr: { eye: "open", mouth: angry ? "frown" : "open", brow: angry ? "angry" : "worried" } + }); + } + }, + cam(s) { + const [rx] = rockTop(s); + return cam2(s, lerp(1.3, 1.2, s.t / s.D), lerp(rx, s.X(-0.35), easeInOut(inv(CUE2.laugh1.angry - 0.5, CUE2.laugh1.angry + 1, s.t))), s.gy + 30 * s.u); + }, + enterAt: [0.5, 0.4] + }, + shout2: { + label: "\u53C8\u558A\u72FC\u6765\u4E86", + bg: bgHill("day"), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.shout2; + const dp = inv(0, 3, t); + if (dp < 1) { + const night = Math.max(0, Math.sin(dp * Math.PI * 2 - Math.PI / 2) * 0.5 + 0.5) * 0.3 * (1 - dp); + g2.fillStyle = rgba("#2a3040", night); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + const sa = dp * 2 % 1; + sun(g2, lerp(X(-1), X(1), sa), s.gy - Math.sin(sa * Math.PI) * 360 * u, 50 * u, "#d65a40", 0.8 * (1 - dp)); + } + flock(s, (sp) => t > C.shouts[0] ? { graze: 0, look: 0.5, face: sp.f > 0.05 ? -1 : 1 } : {}); + const [rx, ry] = rockTop(s); + const cupping = t > C.shouts[0] - 0.4; + const shouting = C.shouts.some((st) => t > st && t < st + 1.1); + boy(g2, rx, ry, 300 * k, t, cupping ? SHOUT : POSE.stand("boy"), { face: -1, expr: { eye: shouting ? "closed" : "sly", mouth: shouting ? "shout" : "smile" } }); + if (cupping) { + const mx = rx - 300 * k * 0.25, my = ry - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 1); + C.shouts.forEach((st, i) => shoutText(g2, "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 40 * u, -1, 120 * u, t - st, { fade: 1 - smooth(inv(st + 2.2, st + 3, t)), seed: i + 5 })); + } + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const p = easeOut(inv(C.run + i * 0.2, C.run + 2.6 + i * 0.2, t)); + if (p <= 0) continue; + const x = lerp(X(-1.25) - i * 90 * u, X(-0.75 + i * 0.13), p), y = hillY(s, X(-0.75 + i * 0.13)) + (40 + i % 2 * 40) * u + (1 - p) * 120 * u; + farmer(g2, i, x, y, 320 * k, t, p < 1 ? carryPose(t * 12 + i, f.kind, 1, f.tool) : standPose(f.kind, "shade"), { expr: { eye: "wide", mouth: "open", brow: "worried" }, sweat: 1 }); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + const p = easeInOut(inv(CUE2.shout2.run - 0.4, CUE2.shout2.run + 1.2, s.t)); + return cam2(s, lerp(1.45, 1.05, p), lerp(rx - 60 * s.u, s.W / 2 - 100 * s.u, p), lerp(ry - 60 * s.u, s.gy, p)); + }, + enterAt: [0.5, 0.3] + }, + warn: { + label: "\u8B66\u544A", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.warn; + flock(s); + const [rx, ry] = rockTop(s); + boy(g2, rx, ry, 300 * k, t, t < C.point + 0.4 ? LAUGH(t) : { ...POSE.stand("boy"), lean: 0.05 }, { face: -1, expr: t < C.point + 0.4 ? { eye: "happy", mouth: "laugh" } : { eye: "open", mouth: "line", brow: "worried" } }); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const lead = i === 0; + const step = lead ? easeOut(inv(C.point - 0.3, C.point + 0.4, t)) : 0; + const x = X(-0.82 + i * 0.16) + step * 140 * u, y = hillY(s, X(-0.82 + i * 0.16)) + (30 + i % 2 * 40) * u; + const pose = lead && t > C.point ? standPose(f.kind, "point", t) : standPose(f.kind, "hips"); + farmer(g2, i, x, y, 330 * k, t, pose, { expr: { eye: "open", mouth: lead && t > C.point ? "open" : "frown", brow: "angry" }, tilt: lead && t > C.point ? -0.05 : 0 }); + if (lead && t > C.point) marks(g2, x + 60 * u, y - 330 * k * 1.15, 90 * u, smooth(inv(C.point, C.point + 0.2, t)), "\uFF01", "#b8322a"); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + const p = easeInOut(inv(CUE2.warn.point - 0.4, CUE2.warn.point + 0.6, s.t)); + return cam2(s, lerp(1.6, 1.45, p), lerp(rx - 40 * s.u, s.X(-0.62), p), lerp(ry - 50 * s.u, s.gy - 10 * s.u, p)); + }, + enterAt: [0.3, 0.5] + }, + wolf: { + label: "\u72FC\u771F\u7684\u6765\u4E86", + bg: bgHill("storm", { tree: false }), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.wolf; + for (let i = 0; i < 4; i++) stormCloud(g2, X(-1.2 + i * 0.7) + t * 30 * u, s.gy - (380 + i % 2 * 90) * u, 520 * u, smooth(inv(C.dark, C.dark + 3, t)), 40 + i); + for (let i = 0; i < 18; i++) grass(g2, i / 18 * W2 + hash(i) * 40 * u, H2 - hash(i * 3) * 160 * u, 70 * u, t, 100 + i, "#3e5a2e", 0.6, 2.5); + flock(s, () => ({ graze: t > C.creep ? 0 : 0.6 + 0.4 * Math.sin(t * 2), look: t > C.creep ? 0.4 : 0, scared: t > C.creep ? 1 : 0 })); + const bx = X(0.78), by = hillY(s, X(0.78)) + 80 * u; + const creep = easeInOut(inv(C.creep, C.reveal, t)); + if (t > C.creep - 0.2) { + const mode = t > C.reveal ? "snarl" : "sneak"; + wolf(g2, { x: bx - creep * 300 * u, y: by + 10 * u, s: 330 * k, t, face: -1, mode, glow: 0.8 }); + } + bush(g2, bx + 40 * u, by + 30 * u, 260 * u, 11, 0.85); + bush(g2, bx + 180 * u, by + 10 * u, 200 * u, 12, 0.9); + eyesInDark(g2, bx + 20 * u, by - 70 * u, 18 * u, t, smooth(inv(C.eyes, C.eyes + 0.6, t)) * (1 - smooth(inv(C.creep, C.creep + 0.3, t)))); + const fl = t > C.reveal && t < C.reveal + 0.18 ? 0.6 : t > C.reveal + 0.28 && t < C.reveal + 0.36 ? 0.35 : 0; + if (fl > 0) { + g2.fillStyle = rgba("#f6f4ee", fl); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + } + }, + cam(s) { + const C = CUE2.wolf, bx = s.X(0.78), by = hillY(s, s.X(0.78)); + const p1 = easeInOut(inv(C.bush - 0.6, C.eyes, s.t)), p2 = easeInOut(inv(C.reveal - 0.3, C.reveal + 0.5, s.t)); + const sh = s.t > C.reveal ? Math.sin(s.t * 70) * 6 * s.u * Math.exp(-(s.t - C.reveal) * 4) : 0; + return cam2(s, lerp(lerp(1.05, 1.8, p1), 2.1, p2), lerp(lerp(s.W / 2, bx + 20 * s.u, p1), bx - 230 * s.u, p2), lerp(lerp(s.gy - 80 * s.u, by - 40 * s.u, p1), by - 40 * s.u, p2), { dx: sh }); + }, + enterAt: [0.5, 0.2] + }, + cry: { + label: "\u547C\u6551", + bg: bgHill("storm"), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.cry; + for (let i = 0; i < 4; i++) stormCloud(g2, X(-1.2 + i * 0.7) + t * 40 * u, s.gy - (380 + i % 2 * 90) * u, 520 * u, 1, 50 + i); + for (let i = 0; i < 5; i++) { + const q2 = (t * 0.6 + i * 0.21) % 1, y2 = H2 * (0.15 + 0.12 * i); + brush(g2, [[lerp(W2 * 1.2, -W2 * 0.3, q2), y2], [lerp(W2 * 1.2, -W2 * 0.3, q2) - 220 * u, y2 + 8 * u]], 3 * u, { color: "#cfd3d4", alpha: 0.5, dry: 0.9, seed: i }); } - }; - until(1.5); - for (let k = 1; k < TOTAL + 0.3; k++) { - const tt = k; - ctx.suspend(tt).then(() => { - until(tt + 1.5); - ctx.resume(); + flock(s, () => ({ graze: 0, look: 0.4, scared: 1 })); + wolf(g2, { x: X(1.05) - t * 25 * u, y: hillY(s, X(0.95)) + 60 * u, s: 230 * k, t, face: -1, mode: "sneak", glow: 0.9 }); + const [rx, ry] = rockTop(s); + const run = easeInOut(inv(C.toRock, C.toRock + 1, t)); + const x = lerp(X(0.4), rx, run), y = lerp(hillY(s, X(0.4)) + 40 * u, ry, run) - Math.sin(Math.PI * run) * 70 * u; + const shouting = C.shouts.some((st) => t > st && t < st + 1.2); + const pose = t < C.toRock ? SCARED(t) : run < 1 ? POSE.walk(t * 14, "boy", 1) : shouting ? { ...SHOUT, shiver: 0.6 } : SCARED(t); + boy(g2, x, y, 300 * k, t, pose, { face: -1, expr: { eye: "wide", mouth: "shout", brow: "worried" }, tears: t > C.shouts[1] ? 1 : 0, shiver: 0.6 }); + if (run >= 1) { + const mx = rx - 300 * k * 0.25, my = ry - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 0.8); + C.shouts.forEach((st, i) => shoutText(g2, i === 2 ? "\u6551\u547D\u554A\uFF01" : "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 50 * u, -1, 130 * u, t - st, { scatter: inv(st + 0.9, st + 2.2, t), seed: i + 11 })); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + return cam2(s, lerp(1.35, 1.25, s.t / s.D), lerp(s.X(0.3), rx - 120 * s.u, easeInOut(inv(1.8, 3.6, s.t))), ry - 60 * s.u); + }, + enterAt: [0.4, 0.5] + }, + noone: { + label: "\u6CA1\u4EBA\u6765", + bg: bgVillage(true), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.noone; + shoutText(g2, "\u72FC\u6765\u4E86", X(0.85), s.gy - 300 * u, -1, 60 * u, t - 0.2, { scatter: inv(1.4, 3.2, t), color: "#b8322a", seed: 21 }); + for (let i = 0; i < 3; i++) { + const [x, y] = fieldSpot(s, i); + const look = t > C.look + i * 0.15 && t < C.work; + const lead = i === 0; + let pose = hoePose(t * 2.2 + i * 1.7, FARMERS[i].kind); + if (look) pose = { ...POSE.stand(FARMERS[i].kind), hl: [0.1, -0.62], hr: [0.16, -0.66], tool: { kind: "hoe", a: [0.05, -0.05], b: [0.18, -0.95] } }; + if (lead && t > C.wave && t < C.work) pose = { ...standPose(FARMERS[i].kind, "wave", t), tool: { kind: "hoe", a: [-0.12, -0.05], b: [-0.05, -0.95] } }; + const shake = t > C.shake && t < C.wave ? 0.14 * Math.sin(t * 8 + i) : 0; + farmer(g2, i, x, y, 330 * k, t, pose, { + face: t > C.work ? i === 1 ? 1 : -1 : i === 1 ? -1 : 1, + tilt: look ? -0.18 + shake : 0, + expr: { eye: "open", mouth: lead && t > C.wave && t < C.work ? "open" : look ? "frown" : "line", brow: t > C.shake ? "angry" : "up" } + }); + } + }, + cam(s) { + const C = CUE2.noone, [lx, ly] = fieldSpot(s, 0); + const p = easeInOut(inv(C.wave - 0.6, C.wave + 0.4, s.t)) * (1 - easeInOut(inv(C.work - 0.3, C.work + 0.9, s.t))); + return cam2(s, lerp(1.3, 1.9, p), lerp(s.X(0.25), lx + 40 * s.u, p), lerp(s.gy + 20 * s.u, ly - 180 * s.u, p)); + }, + enterAt: [0.8, 0.2] + }, + chaos: { + label: "\u7F8A\u7FA4\u56DB\u6563", + bg: bgHill("storm", { rock: true }), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.chaos; + for (let i = 0; i < 3; i++) stormCloud(g2, X(-1 + i * 0.8) + t * 30 * u, s.gy - 400 * u, 520 * u, 1, 60 + i); + const [rx, ry] = rockTop(s); + boy(g2, rx, ry, 300 * k, t, SCARED(t), { face: 1, tears: 1 }); + const wx = lerp(X(1.2), X(0.35), easeOut(inv(C.dash, C.scatter, t))) + easeIn(inv(C.grab, C.grab + 2.2, t)) * 900 * u; + const wy = hillY(s, X(0.4)) + 90 * u; + FLOCK.forEach((sp, i) => { + const x0 = X(sp.f), y0 = hillY(s, x0) + sp.dy * u; + const run = inv(C.scatter + i * 0.06, C.scatter + 0.3 + i * 0.06, t); + const dir = x0 < X(0.35) ? -1 : 1, q2 = Math.max(0, t - C.scatter - i * 0.06); + if (i === 3 && t > C.grab) return; + sheep(g2, { x: x0 + dir * q2 * (260 + hash(i) * 200) * u, y: y0 + (hash(i + 3) - 0.5) * q2 * 60 * u, s: 150 * k * sp.s, t, face: run > 0 ? dir : sp.face, run: run > 0 ? 1 : 0, scared: run > 0 ? 1 : 0, baa: run > 0 ? Math.abs(Math.sin(t * 6 + i)) : 0, seed: sp.seed, graze: run > 0 ? 0 : 1 }); + if (run > 0 && q2 < 1.2) blot(g2, x0 + dir * q2 * 200 * u, y0 + 10 * u, 60 * u, "#a89a78", 0.3 * (1 - q2 / 1.2)); }); - } - if (voiceBuf) { - const v = ctx.createBufferSource(); - v.buffer = voiceBuf; - const vg = ctx.createGain(); - vg.gain.value = 1.15; - const hp = ctx.createBiquadFilter(); - hp.type = "highpass"; - hp.frequency.value = 80; - v.connect(hp); - hp.connect(vg); - vg.connect(m.voice); - v.start(1 / 30); - } - const out = await ctx.startRendering(); - onStage?.("\u54CD\u5EA6\u6807\u51C6\u5316\uFF08\u221214 LUFS\uFF09\u2026"); - normalize(out, -14, 0.84); - onStage?.("\u5B8C\u6210"); - return out; - })(); - cached.catch(() => { - cached = null; - }); - return cached; + wolf(g2, { x: wx, y: wy, s: 340 * k, t, face: t > C.grab ? 1 : -1, mode: t < C.grab - 0.4 ? "run" : t < C.grab ? "snarl" : "run", carry: t > C.grab }); + for (let i = 0; i < 12; i++) grass(g2, i / 12 * W2, H2 - hash(i) * 100 * u, 60 * u, t, 200 + i, "#3e5a2e", 0.6, 2.5); + particles2(6, 9, (r, i) => { + const q2 = t - C.scatter; + if (q2 > 0 && q2 < 2.5) leaf(g2, X(0.35) + (r() - 0.5) * 400 * u + q2 * 100 * u, wy - q2 * 120 * u * r(), 10 * u, q2 * 4 + i, "#f2ece0", 0.8 * (1 - q2 / 2.5)); + }); + }, + cam(s) { + const sh = s.t > CUE2.chaos.scatter && s.t < CUE2.chaos.scatter + 1 ? Math.sin(s.t * 60) * 5 * s.u : 0; + return cam2(s, 1.15, s.X(0.15), s.gy + 20 * s.u, { dx: sh }); + }, + enterAt: [0.8, 0.5] + }, + regret: { + label: "\u540E\u6094", + bg: bgHill("sunset", { rock: false }), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.regret; + for (let i = 0; i < 3; i++) farBird(g2, X(-0.6 + i * 0.12) + t * 20 * u, s.gy - (330 + i * 30) * u, 18 * u, t * 7 + i, "#2a2220", 0.8); + sheep(g2, { x: X(-0.4), y: hillY(s, X(-0.4)) + 80 * u, s: 140 * k, t, graze: 1, seed: 2 }); + sheep(g2, { x: X(0.45), y: hillY(s, X(0.45)) + 120 * u, s: 130 * k, t, face: -1, graze: 0.3, seed: 5 }); + const bx = X(0.02), by = hillY(s, X(0.02)) + 100 * u; + const hug = smooth(inv(C.hug - 0.4, C.hug + 0.4, t)); + const crying = t > C.cry; + boy(g2, bx, by, 300 * k, t, SIT, { expr: { eye: crying ? "sad" : "open", mouth: crying ? "cry" : "frown", brow: "worried" }, tears: crying ? 1 : 0, tilt: 0.12 * hug, shiver: crying ? 0.4 : 0 }); + sheep(g2, { x: bx + 300 * k * 0.2, y: by - 300 * k * 0.06, s: 100 * k, t, lamb: true, face: -1, graze: 0, look: 0.3, seed: 9, alpha: 0.4 + 0.6 * hug }); + if (crying) { + tear(g2, bx + 300 * k * 0.2, by - 300 * k * 0.6 + t * 40 % 50 * u, 8 * u, 1); + } + }, + cam(s) { + const bx = s.X(0.02), by = hillY(s, bx); + return cam2(s, lerp(1.15, 1.6, easeInOut(s.t / s.D)), bx + 20 * s.u, by - 40 * s.u); + }, + enterAt: [0.3, 0.3] + }, + moral: { + label: "\u5BD3\u610F", + bg: bgHill("dusk", { rock: false, tree: true }), + draw(s) { + const { g: g2, H: H2, u, t, X, portrait } = s; + const C = CUE2.moral; + for (let i = 0; i < 4; i++) blot(g2, X(-0.8) + i * 46 * u + 10 * u, s.gy - 2 * u + i % 2 * 14 * u, 14 * u, "#ffd890", 0.7 * smooth(inv(0.5 + i * 0.3, 1.5 + i * 0.3, t))); + const p = inv(0, s.D, t); + const x = lerp(X(0.5), X(-0.3), p), y = hillY(s, lerp(X(0.5), X(-0.3), p)) + 60 * u; + boy(g2, x, y, 170 * u, t, POSE.walk(t * 5, "boy"), { face: -1, alpha: 0.85, expr: { mouth: "line", eye: "sad" } }); + sheep(g2, { x: x - 110 * u, y: y + 4 * u, s: 70 * u, t, walk: 1, face: -1, lamb: true, seed: 9 }); + shoutText(g2, "\u72FC\u6765\u4E86", X(-0.2), H2 * 0.25, 1, 80 * u, t - 0.6, { scatter: inv(2, 4.6, t), seed: 31, color: "#9a3a32" }); + const fs = (portrait ? 200 : 180) * u; + const vx = portrait ? X(0.6) : X(0.68), vy = portrait ? H2 * 0.28 : H2 * 0.36; + writeText2(g2, "\u8BDA\u5B9E", vx, vy, fs, C.write, 0.55, t, { vertical: true, gap: 1.02 }); + const ea = smooth(inv(C.end - 0.3, C.end + 0.3, t)); + if (ea > 0) { + g2.save(); + g2.globalAlpha = ea; + g2.font = `${40 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.save(); + g2.translate(vx - fs * 0.95, vy - fs * 0.9); + [..."\u72FC\u6765\u4E86 \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); + g2.restore(); + g2.restore(); + } + const sp = inv(C.end + 0.1, C.end + 0.35, t); + if (sp > 0) { + g2.save(); + g2.translate(vx - fs * 0.95, vy + fs * 0.9); + g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); + seal(g2, 0, 0, 80 * u, ["\u5BD3\u8A00"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + }, + cam(s) { + return cam2(s, lerp(1, 1.06, s.t / s.D), s.W / 2, s.H / 2); + }, + enterAt: [0.5, 0.4] + } + }; + + // src/films/wolf/music.ts + var B = 60 / 96; + var G = [N.G3, N.D4, N.B4]; + var Em = [N.E3, N.B3, N.G4]; + var D = [N.D3, N.A3, N.D4]; + var Am = [N.A2, N.E3, N.A3]; + var Gh = [N.G4, N.B4, N.D5]; + function arp(m, t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) { + let i = 0; + for (let t = t0; t < t1 - 0.05; t += step, i++) { + const ch = chords[Math.floor((t - t0) / (step * 8)) % chords.length]; + m.pluck(t, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.6 }); + } } - function toWav(b) { - const ch = b.numberOfChannels, len = b.length, sr = b.sampleRate; - const buf = new ArrayBuffer(44 + len * ch * 2), v = new DataView(buf); - const w = (o2, s) => { - for (let i = 0; i < s.length; i++) v.setUint8(o2 + i, s.charCodeAt(i)); - }; - w(0, "RIFF"); - v.setUint32(4, 36 + len * ch * 2, true); - w(8, "WAVE"); - w(12, "fmt "); - v.setUint32(16, 16, true); - v.setUint16(20, 1, true); - v.setUint16(22, ch, true); - v.setUint32(24, sr, true); - v.setUint32(28, sr * ch * 2, true); - v.setUint16(32, ch * 2, true); - v.setUint16(34, 16, true); - w(36, "data"); - v.setUint32(40, len * ch * 2, true); - const data = Array.from({ length: ch }, (_, c2) => b.getChannelData(c2)); - let o = 44; - for (let i = 0; i < len; i++) for (let c2 = 0; c2 < ch; c2++) { - const s = Math.max(-1, Math.min(1, data[c2][i])); - v.setInt16(o, s < 0 ? s * 32768 : s * 32767, true); - o += 2; + var MISCHIEF = [["G4", 0.5], ["B4", 0.5], ["D5", 0.5], ["B4", 0.5], ["A4", 0.5], ["G4", 0.5], ["E4", 1]]; + var PASTORAL = [["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 2]]; + var LAMENT = [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1.5], ["A4", 0.5], ["E4", 2]]; + function score2(m) { + const S = sceneStart, Dd = sceneDur; + let t = S("title"); + m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4], 0.07, 0.26); + m.pad(t + 0.5, S("village") + 1, [N.G2, N.D3], 0.045); + m.melody(t + 2, B, MISCHIEF, 0.15, 0, "pluck"); + for (let i = 0; i < 6; i++) m.block(t + 2 + i * B, 0.06, 1e3, 0.2); + t = S("village"); + m.pad(t, S("bored") + 0.5, [N.G2, N.D3, N.B3], 0.04); + arp(m, t + 0.3, S("bored"), [G, Em, G, D], B / 2, 0.16); + m.melody(t + 0.8, B * 1.2, PASTORAL, 0.12); + t = S("bored"); + m.pad(t, S("shout1") + 0.3, [N.G2, N.D3], 0.04); + for (let i = 0; i < Math.floor(CUE2.bored.idea / (B * 2)); i++) m.pluck(t + 0.4 + i * B * 2, [N.G3, N.D4, N.B3, N.D4][i % 4], 0.13, 0, { dur: 2.4 }); + m.tone(t + CUE2.bored.yawn, 520, 240, 1.1, 0.035, "triangle", 0, 0.3); + m.bell(t + CUE2.bored.idea, N.D6, 0.08, 0.2); + m.bell(t + CUE2.bored.idea + 0.12, N.G6 ?? 91, 0.06, 0.2); + for (let i = 0; i < 6; i++) m.pluck(t + CUE2.bored.sly + i * B / 2, [N.E4, N.G4, N.E4, N.B4, N.A4, N.G4][i], 0.14, 0.2, { dur: 0.4, bright: 0.9 }); + t = S("shout1"); + m.pad(t, S("run1"), [N.G2, N.D3], 0.035); + for (let i = 0; i < 4; i++) m.block(t + 0.6 + i * 0.12, 0.05, 1300, 0); + for (const st of CUE2.shout1.shouts) { + m.pluck(t + st, N.G2, 0.3, 0, { dur: 1.2 }); + m.pluck(t + st, N.D3, 0.25, 0, { dur: 1.2 }); } - return buf; + t = S("run1"); + m.pad(t, S("laugh1"), [N.D3, N.A3], 0.035); + for (let x = t + CUE2.run1.run; x < t + CUE2.run1.arrive + 0.6; x += B / 2) { + m.block(x, 0.07, 900, 0); + m.pluck(x + B / 4, [N.D4, N.E4, N.G4, N.A4, N.B4, N.D5][Math.floor((x - t) / (B / 2)) % 6], 0.12, 0.2, { dur: 0.3, bright: 0.9 }); + } + m.pluck(t + CUE2.run1.arrive + 0.3, N.B4, 0.2, 0, { bend: 2 }); + m.pluck(t + CUE2.run1.arrive + 1.4, N.A4, 0.18, 0, { bend: 2 }); + t = S("laugh1"); + arp(m, t + 0.2, t + CUE2.laugh1.angry, [G, Gh], B / 2, 0.13, [0, 2, 1, 2]); + const wah = [N.D4, N.D4 - 1, N.D4 - 2, N.D4 - 3]; + wah.forEach((nn, i) => m.tone(t + CUE2.laugh1.angry + i * 0.42, 440 * Math.pow(2, (nn - 69) / 12), 440 * Math.pow(2, (nn - 69.4) / 12), i === 3 ? 0.9 : 0.36, 0.05, "sawtooth", 0, 0.3)); + m.pad(t + CUE2.laugh1.angry, S("shout2") + 0.4, [N.E2, N.B2], 0.035, 600); + t = S("shout2"); + m.melody(t + 0.4, B, MISCHIEF, 0.13, 0, "pluck"); + for (const st of CUE2.shout2.shouts) { + m.pluck(t + st, N.G2, 0.28, 0, { dur: 1.2 }); + m.pluck(t + st, N.D3, 0.22, 0, { dur: 1.2 }); + } + for (let x = t + CUE2.shout2.run; x < S("warn"); x += B / 2) m.block(x, 0.06, 900, 0); + t = S("warn"); + arp(m, t + 0.2, t + CUE2.warn.point, [G, Gh], B / 2, 0.12, [0, 2, 1, 2]); + m.pluck(t + CUE2.warn.point, N.E2, 0.35, 0, { dur: 2.5 }); + m.pluck(t + CUE2.warn.point, N.B2, 0.25, 0, { dur: 2.5 }); + m.pad(t + CUE2.warn.point, S("wolf") + 1, [N.E2, N.B2], 0.045, 500); + t = S("wolf"); + m.pad(t, S("cry") + 0.5, [N.E2, N.B2, N.E3], 0.05, 420); + for (let x = t + 1; x < t + CUE2.wolf.reveal; x += 0.11) m.pluck(x, Math.floor((x - t) / 0.88) % 2 ? N.B2 : N.E2 - 12 + 12, 0.04 + 0.14 * (x - t) / CUE2.wolf.reveal, 0, { dur: 0.25, send: 0.05 }); + for (let x = t + CUE2.wolf.eyes; x < t + CUE2.wolf.reveal; x += 0.8) m.heart(x); + [N.E3, N.E3 + 1, N.B3, N.E4 + 1].forEach((nn) => m.pluck(t + CUE2.wolf.reveal, nn, 0.22, 0, { dur: 2.5 })); + t = S("cry"); + for (let x = t + 0.2; x < S("noone"); x += 0.12) m.pluck(x, Math.floor((x - t) / 0.48) % 2 ? N.E3 : N.B2, 0.1, 0, { dur: 0.2, send: 0.05 }); + m.flute(t + CUE2.cry.shouts[0] + 0.1, N.E5, 0.9, 0.08); + m.flute(t + CUE2.cry.shouts[2] + 0.1, N.G5, 1.1, 0.08); + t = S("noone"); + m.pad(t, S("chaos") + 0.4, [N.E2, N.B2], 0.04, 500); + for (let i = 0; i < 7; i++) m.pluck(t + 0.5 + i * B * 2.2, [N.B3, N.A3, N.G3, N.E3, N.G3, N.A3, N.E3][i], 0.13, -0.1, { dur: 2.2 }); + t = S("chaos"); + m.pad(t, S("regret") + 0.6, [N.E2, N.B2, N.E3 + 1], 0.05, 700); + for (let x = t + 0.3; x < S("regret"); x += 0.09) m.pluck(x, [N.E3, N.G3, N.E3, N.B3][Math.floor((x - t) / 0.09) % 4], 0.11, x * 7 % 1 - 0.5, { dur: 0.18, send: 0.05 }); + for (let i = 0; i < 8; i++) m.block(t + CUE2.chaos.scatter + i * 0.3, 0.12, 500, 0); + t = S("regret"); + m.pad(t, S("moral") + 0.6, [N.E2, N.B2, N.G3], 0.04, 700); + arp(m, t + 0.3, S("moral"), [Em, Am, Em, D], B, 0.1); + m.melody(t + 1, B * 1.25, LAMENT, 0.12); + t = S("moral"); + m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); + arp(m, t + 0.2, t + CUE2.moral.end, [G, Em, G, D], B * 0.75, 0.12); + m.melody(t + CUE2.moral.write, B, [["D5", 1], ["B4", 1], ["A4", 1], ["G4", 2]], 0.12); + m.gliss(t + CUE2.moral.end, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.2); + m.bell(t + CUE2.moral.end + 0.1, N.G5, 0.08); + m.bell(t + CUE2.moral.end + 0.35, N.D6, 0.05, 0.3); + void Dd; } - function loudness(b) { - const fs = b.sampleRate; - const biquad = (x, b0, b1, b2, a1, a2) => { - const y = new Float32Array(x.length); - let x1 = 0, x2 = 0, y1 = 0, y2 = 0; - for (let i = 0; i < x.length; i++) { - const v = b0 * x[i] + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; - x2 = x1; - x1 = x[i]; - y2 = y1; - y1 = v; - y[i] = v; - } - return y; - }; - let K = Math.tan(Math.PI * 1681.974450955533 / fs), Q = 0.7071752369554196; - const Vh = Math.pow(10, 3.999843853973347 / 20), Vb = Math.pow(Vh, 0.4996667741545416); - let a0 = 1 + K / Q + K * K; - const s1 = [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; - K = Math.tan(Math.PI * 38.13547087602444 / fs); - Q = 0.5003270373238773; - a0 = 1 + K / Q + K * K; - const s2 = [1, -2, 1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; - const chans = Array.from({ length: b.numberOfChannels }, (_, c2) => { - const y = biquad(b.getChannelData(c2), s1[0], s1[1], s1[2], s1[3], s1[4]); - return biquad(y, s2[0], s2[1], s2[2], s2[3], s2[4]); + function baa(m, t, vel = 0.08, pan = 0, pitch = 1, len = 0.6) { + m.at(t, () => { + const c2 = m.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(330 * pitch, t); + o.frequency.linearRampToValueAtTime(290 * pitch, t + len); + const am = c2.createOscillator(); + am.frequency.value = 22; + const amg = c2.createGain(); + amg.gain.value = 0.45; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.05); + g2.gain.setValueAtTime(vel, t + len * 0.7); + g2.gain.linearRampToValueAtTime(0, t + len); + const trem = c2.createGain(); + trem.gain.value = 0.6; + am.connect(amg); + amg.connect(trem.gain); + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.frequency.value = 950 * pitch; + f.Q.value = 2.5; + const f2 = c2.createBiquadFilter(); + f2.type = "peaking"; + f2.frequency.value = 2500; + f2.gain.value = 6; + o.connect(trem); + trem.connect(f); + f.connect(f2); + f2.connect(g2); + m.out(g2, m.sfx, pan, 0.25); + o.start(t); + o.stop(t + len + 0.05); + am.start(t); + am.stop(t + len + 0.05); }); - const blk = Math.round(0.4 * fs), hop = Math.round(0.1 * fs), z = []; - for (let st = 0; st + blk <= b.length; st += hop) { - let sum = 0; - for (const y of chans) { - let e = 0; - for (let i = st; i < st + blk; i++) e += y[i] * y[i]; - sum += e / blk; - } - z.push(sum); - } - const L = (e) => -0.691 + 10 * Math.log10(e + 1e-12); - const abs = z.filter((e) => L(e) > -70); - if (!abs.length) return -70; - const rel = L(abs.reduce((a, e) => a + e, 0) / abs.length) - 10; - const gated = abs.filter((e) => L(e) > rel); - return L(gated.reduce((a, e) => a + e, 0) / Math.max(1, gated.length)); } - function normalize(b, target = -14, ceiling = 0.84) { - const L = loudness(b); - const g2 = Math.pow(10, (target - L) / 20); - const n = b.length, ch = Array.from({ length: b.numberOfChannels }, (_, c2) => b.getChannelData(c2)); - const want = new Float32Array(n); - for (let i = 0; i < n; i++) { - let m = 0; - for (const d of ch) { - d[i] *= g2; - const a = Math.abs(d[i]); - if (a > m) m = a; - } - want[i] = m > ceiling ? ceiling / m : 1; + function growl(m, t, dur = 1.4, vel = 0.12) { + m.at(t, () => { + const c2 = m.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(78, t); + o.frequency.linearRampToValueAtTime(95, t + dur * 0.6); + o.frequency.linearRampToValueAtTime(70, t + dur); + const n = c2.createBufferSource(); + n.buffer = m.noise; + n.loop = true; + const am = c2.createOscillator(); + am.frequency.value = 17; + const amg = c2.createGain(); + amg.gain.value = 0.5; + const mix = c2.createGain(); + mix.gain.value = 0.5; + am.connect(amg); + amg.connect(mix.gain); + const lp3 = c2.createBiquadFilter(); + lp3.type = "lowpass"; + lp3.frequency.value = 650; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.25); + g2.gain.setValueAtTime(vel, t + dur - 0.3); + g2.gain.linearRampToValueAtTime(0, t + dur); + o.connect(mix); + n.connect(mix); + mix.connect(lp3); + lp3.connect(g2); + m.out(g2, m.sfx, 0.3, 0.2); + o.start(t); + o.stop(t + dur + 0.05); + n.start(t, t * 0.37 % 1.5); + n.stop(t + dur + 0.05); + am.start(t); + am.stop(t + dur + 0.05); + }); + } + function howl(m, t, vel = 0.09) { + m.at(t, () => { + const c2 = m.ctx, dur = 2.4; + const o = c2.createOscillator(); + o.type = "triangle"; + o.frequency.setValueAtTime(330, t); + o.frequency.linearRampToValueAtTime(600, t + 0.6); + o.frequency.setValueAtTime(600, t + 1.4); + o.frequency.linearRampToValueAtTime(420, t + dur); + const lfo = c2.createOscillator(); + lfo.frequency.value = 5.5; + const lg = c2.createGain(); + lg.gain.value = 8; + lfo.connect(lg); + lg.connect(o.frequency); + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = 900; + bp.Q.value = 0.8; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.4); + g2.gain.setValueAtTime(vel, t + 1.6); + g2.gain.linearRampToValueAtTime(0, t + dur); + o.connect(bp); + bp.connect(g2); + m.out(g2, m.sfx, 0.4, 0.9); + o.start(t); + o.stop(t + dur + 0.05); + lfo.start(t); + lfo.stop(t + dur + 0.05); + }); + } + function thunder(m, t, vel = 0.3) { + m.noiseHit(t, 0.25, "lowpass", 3e3, 300, vel, 0, 0.6, 0.5); + for (let i = 0; i < 5; i++) m.noiseHit(t + 0.15 + i * 0.32, 0.9, "lowpass", 400, 60, vel * 0.6 * (1 - i * 0.15), i % 2 ? 0.3 : -0.3, 0.7, 0.8); + } + var whip = (m, t, pan = 0) => { + m.noiseHit(t, 0.04, "highpass", 5e3, 2500, 0.25, pan, 0.7, 0.2); + m.tone(t, 2400, 700, 0.05, 0.05, "triangle", pan, 0.2); + }; + var thud = (m, t, vel = 0.12, pan = 0) => { + m.tone(t, 110, 60, 0.12, vel, "sine", pan, 0.1); + m.noiseHit(t, 0.06, "lowpass", 900, 200, vel * 0.7, pan, 0.7, 0.05); + }; + var steps = (m, t0, t1, rate, vel = 0.06, pan = 0) => { + for (let x = t0; x < t1; x += 1 / rate) m.noiseHit(x, 0.05, "lowpass", 700, 180, vel, pan, 0.7, 0.05); + }; + var crickets = (m, t0, t1, r) => { + for (let x = t0; x < t1; x += 0.6 + r() * 0.8) for (let k = 0; k < 3; k++) m.tone(x + k * 0.05, 4600, 4500, 0.035, 0.012, "sine", r() - 0.5, 0.3); + }; + function echoLines(m, sceneId, gain = 0.3) { + const sc = TL.scenes.find((s) => s.id === sceneId); + for (const l of sc?.lines ?? []) if (l.who === "boy" && /狼|救命|快来/.test(l.text)) m.echo(l.t0, l.dur, 0.45, gain, 3); + } + function hoeHits(m, t0, t1, rate, offset, vel = 0.06, pan = 0) { + for (let n = 0; n < 200; n++) { + const x = (Math.PI / 2 + n * Math.PI * 2 - offset) / rate; + if (x < 0) continue; + const tt = t0 + x; + if (tt > t1) break; + thud(m, tt, vel, pan); } - const att = 1 / (4e-3 * b.sampleRate), rel = 1 / (0.12 * b.sampleRate); - for (let i = n - 2; i >= 0; i--) want[i] = Math.min(want[i], want[i + 1] + att); - let cur = 1; - for (let i = 0; i < n; i++) { - cur = Math.min(want[i], cur + rel); - for (const d of ch) d[i] *= cur; + } + function effects2(m) { + const S = sceneStart, Dd = sceneDur; + const r = rng(77); + let t = S("title"); + m.drop(t + CUE2.title.drop); + m.swish(t + CUE2.title.write); + m.swish(t + CUE2.title.write + 0.45); + m.swish(t + CUE2.title.write + 0.9); + m.thump(t + CUE2.title.seal + 0.01, 0.45); + baa(m, t + 2.6, 0.06, 0.2); + baa(m, t + 4, 0.05, -0.1, 1.15); + whip(m, t + 3.3); + t = S("village"); + for (let x = t + 0.5; x < t + Dd("village"); x += 1.2 + r() * 1.5) m.chirp(x, 0.03, r() * 1.4 - 0.7); + for (let i = 0; i < 3; i++) hoeHits(m, t, t + Dd("village"), 2.2, i * 1.7 + 2.2 * 0, 0.05, -0.2 + i * 0.25); + baa(m, t + 5.6, 0.07, -0.3); + baa(m, t + 7.8, 0.06, -0.2, 1.1); + whip(m, t + 6.4, -0.3); + t = S("bored"); + m.wind(t, Dd("bored"), 0.03); + baa(m, t + 1.8, 0.06, 0.4); + baa(m, t + 4.6, 0.05, -0.4, 1.12); + baa(m, t + 7.2, 0.05, 0.3, 0.95); + t = S("shout1"); + m.noiseHit(t + CUE2.shout1.hop, 0.3, "bandpass", 600, 1800, 0.06, 0, 1, 0.2); + thud(m, t + CUE2.shout1.hop + 0.7, 0.12); + echoLines(m, "shout1"); + baa(m, t + CUE2.shout1.shouts[0] + 0.4, 0.07, 0.3, 1.1); + baa(m, t + CUE2.shout1.shouts[1] + 0.3, 0.06, -0.3, 1.2); + t = S("run1"); + for (let i = 0; i < 3; i++) hoeHits(m, t, t + CUE2.run1.hear, 2.2, i, 0.05, -0.3 + i * 0.3); + m.noiseHit(t + CUE2.run1.grab, 0.08, "bandpass", 1200, 600, 0.08, 0, 1, 0.1); + steps(m, t + CUE2.run1.run, t + CUE2.run1.arrive + 0.6, 7, 0.07, -0.1); + steps(m, t + CUE2.run1.run + 0.07, t + CUE2.run1.arrive + 0.8, 6.3, 0.05, 0.2); + for (let i = 0; i < 4; i++) m.noiseHit(t + CUE2.run1.arrive + 0.2 + i * 0.4, 0.25, "bandpass", 900, 500, 0.03, 0, 1.2, 0.1); + t = S("laugh1"); + steps(m, t + CUE2.laugh1.leave, t + Dd("laugh1") + 0.5, 3, 0.05, -0.3); + t = S("shout2"); + echoLines(m, "shout2", 0.27); + steps(m, t + CUE2.shout2.run, t + Dd("shout2"), 7, 0.07, -0.3); + t = S("warn"); + m.thump(t + CUE2.warn.point, 0.25, 80); + t = S("wolf"); + thunder(m, t + 0.4, 0.18); + m.wind(t, Dd("wolf") + 0.5, 0.09, 0.2); + m.noiseHit(t + CUE2.wolf.bush, 0.8, "bandpass", 2500, 1500, 0.05, 0.4, 0.8, 0.2); + growl(m, t + CUE2.wolf.creep, 1.6, 0.1); + thunder(m, t + CUE2.wolf.reveal, 0.35); + howl(m, t + CUE2.wolf.reveal + 0.4, 0.08); + t = S("cry"); + m.wind(t, Dd("cry") + 0.5, 0.12, -0.2); + echoLines(m, "cry", 0.12); + growl(m, t + 5.5, 1.2, 0.06); + t = S("noone"); + m.wind(t, Dd("noone"), 0.05, 0.3); + for (let i = 0; i < 3; i++) { + hoeHits(m, t, t + CUE2.noone.look, 2.2, i * 1.7, 0.05, -0.2 + i * 0.25); + hoeHits(m, t + CUE2.noone.work, t + Dd("noone"), 2.2, i * 1.7 + CUE2.noone.work * 2.2, 0.05, -0.2 + i * 0.25); } - return { before: L, gain: g2 }; + t = S("chaos"); + growl(m, t + 0.2, 0.9, 0.12); + for (let x = t + CUE2.chaos.scatter; x < t + CUE2.chaos.scatter + 3; x += 0.05 + r() * 0.07) m.noiseHit(x, 0.05, "lowpass", 800, 200, 0.05 + r() * 0.04, r() * 1.6 - 0.8, 0.7, 0.05); + for (let i = 0; i < 9; i++) baa(m, t + CUE2.chaos.scatter + 0.1 + i * 0.3 + r() * 0.15, 0.06, r() * 1.4 - 0.7, 1 + r() * 0.3, 0.45); + m.noiseHit(t + CUE2.chaos.grab, 0.2, "lowpass", 1500, 300, 0.12, 0.4, 0.7, 0.2); + baa(m, t + CUE2.chaos.grab + 0.1, 0.07, 0.5, 1.35, 0.8); + t = S("regret"); + crickets(m, t + 0.5, t + Dd("regret"), r); + baa(m, t + CUE2.regret.hug + 0.6, 0.05, 0.1, 1.3, 0.7); + for (let i = 0; i < 3; i++) m.tone(t + 1.2 + i * 1.7, 520, 380, 0.25, 0.02, "sawtooth", -0.5, 0.6); + t = S("moral"); + crickets(m, t, t + Dd("moral"), r); + m.thump(t + CUE2.moral.end + 0.12, 0.4); + void TOTAL; } - // src/film/live.json - var live_default = [ - { id: "01-egg", scene: "bigegg", from: 2.2, dur: 3, cover: 2.4, caption: "\u6700\u5927\u7684\u86CB", index: "\u5176\u4E00", xhs: "\u6700\u540E\u4E00\u9897\u86CB\u6700\u5927\uFF0C\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u6765\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19\u3002" }, - { id: "02-laugh", scene: "bullied", from: 3, dur: 3, cover: 1, caption: "\u4E11\u5C0F\u9E2D", index: "\u5176\u4E8C", xhs: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6\uFF0C\u5927\u5BB6\u90FD\u53EB\u4ED6\u300C\u4E11\u5C0F\u9E2D\u300D\u3002" }, - { id: "03-swans", scene: "autumn", from: 4.6, dur: 3, cover: 1.6, caption: "\u4EF0\u671B", index: "\u5176\u4E09", xhs: "\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u98DE\u8FC7\uFF0C\u4ED6\u671B\u7740\u5929\u7A7A\uFF0C\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80\u3002" }, - { id: "04-snow", scene: "winter", from: 6.4, dur: 3, cover: 1.6, caption: "\u5BD2\u51AC", index: "\u5176\u56DB", xhs: "\u96EA\u591C\u91CC\u6E56\u9762\u7ED3\u4E86\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\uFF0C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u3002" }, - { id: "05-reflect", scene: "reflection", from: 5.4, dur: 3, cover: 2.4, caption: "\u5012\u5F71", index: "\u5176\u4E94", xhs: "\u4ED6\u4F4E\u4E0B\u5934\uFF0C\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71\u2014\u2014\u90A3\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u3002" }, - { id: "06-fly", scene: "fly", from: 2.6, dur: 3, cover: 1.6, caption: "\u5C55\u7FC5", index: "\u5176\u516D", xhs: "\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002\u5F20\u5F00\u7FC5\u8180\uFF0C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u3002" } + // src/films/wolf/content.ts + var COVERS = (tl3) => [ + { id: "shout", name: "\u5927\u558A\u72FC\u6765\u4E86", t: sceneAt(tl3, "shout1", 5), title: "\u72FC\u6765\u4E86", sub: "\u653E\u7F8A\u5A03\u7684\u73A9\u7B11", side: "right", vertical: true }, + { id: "wolf", name: "\u72FC\u771F\u7684\u6765\u4E86", t: sceneAt(tl3, "wolf", 10.2), title: "\u72FC\u6765\u4E86", sub: "\u8FD9\u4E00\u6B21\uFF0C\u662F\u771F\u7684", side: "left", vertical: true }, + { id: "run", name: "\u519C\u6C11\u4E0A\u5C71", t: sceneAt(tl3, "run1", 6.4), title: "\u72FC\u6765\u4E86", sub: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, + { id: "regret", name: "\u5915\u9633\u540E\u6094", t: sceneAt(tl3, "regret", 5.5), title: "\u8BDA\u5B9E", sub: "\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u8BF4\u771F\u8BDD\u4E5F\u6CA1\u4EBA\u4FE1", side: "right", vertical: true } ]; + var PLATFORMS2 = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66", + titleMax: 20, + tagMax: 10, + color: "#ff2442", + titles: ["\u6C34\u58A8\u7248\u72FC\u6765\u4E86\uFF5C\u50CF\u8BED\u6587\u4E66\u91CC\u7684\u63D2\u56FE", "\u7761\u524D\u6545\u4E8B\uFF5C\u653E\u7F8A\u5A03\u548C\u72FC\u6765\u4E86", "\u8BDA\u5B9E\u7684\u5B69\u5B50\u624D\u6709\u4EBA\u4FE1\uFF5C\u72FC\u6765\u4E86"], + desc: `\u8FD8\u8BB0\u5F97\u90A3\u4E2A\u653E\u7F8A\u5A03\u5417\uFF1F +\u8FD9\u6B21\u628A\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86\u4E00\u90E8\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF1A\u5C71\u6751\u3001\u68AF\u7530\u3001\u7F8A\u7FA4\uFF0C\u8FD8\u6709\u4E00\u4E2A\u7231\u5F00\u73A9\u7B11\u7684\u5C0F\u5B69\u3002 - // src/film/overlays.ts - var at = (id, dt) => (TL.scenes.find((s) => s.id === id)?.start ?? 0) + dt; - var COVER_DESIGNS = [ - { id: "reflect", name: "\u5012\u5F71\u63ED\u6653", t: at("reflection", 7.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u539F\u6765\uFF0C\u4ED6\u662F\u4E00\u53EA\u767D\u5929\u9E45", side: "left", vertical: true }, - { id: "hatch", name: "\u7834\u58F3\u800C\u51FA", t: at("bigegg", 4.4), title: "\u4E11\u5C0F\u9E2D", sub: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, - { id: "fly", name: "\u5C55\u7FC5\u9AD8\u98DE", t: at("fly", 4.2), title: "\u76F8\u4FE1\u81EA\u5DF1", sub: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180", side: "right", vertical: true }, - { id: "autumn", name: "\u79CB\u65E5\u4EF0\u671B", t: at("autumn", 6.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", side: "left", vertical: true } +\u{1F411} \u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u4ED6\u773C\u73E0\u4E00\u8F6C +\u{1F4E2} \u300C\u72FC\u6765\u4E86\uFF01\u300D\u2014\u2014\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71 +\u{1F602} \u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D +\u{1F43A} \u6709\u4E00\u5929\uFF0C\u7EFF\u83B9\u83B9\u7684\u773C\u775B\u4ECE\u6811\u4E1B\u91CC\u63A2\u51FA\u6765\u2026\u2026 + +\u8FD9\u4E00\u6B21\u4ED6\u558A\u7834\u4E86\u55D3\u5B50\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u6765\u3002 +\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002 + +\u65C1\u767D\u3001\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u4E09\u4E2A\u58F0\u97F3\uFF0C\u9002\u5408\u5F53\u7761\u524D\u6545\u4E8B\u548C\u5B69\u5B50\u4E00\u8D77\u770B \u{1F440}`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u513F\u7AE5\u6545\u4E8B", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8\u52A8\u753B", "\u4EB2\u5B50\u9605\u8BFB", "\u8BDA\u5B9E", "\u5BD3\u8A00\u6545\u4E8B"], + tip: "\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\uFF1B\u8BDD\u9898\u7C98\u8D34\u540E\u5728 App \u91CC\u70B9\u9009\u4E00\u4E0B\u5373\u53EF\u53D8\u6210\u84DD\u8272\u8BDD\u9898\u3002" + }, + { + id: "dy", + name: "\u6296\u97F3", + titleMax: 30, + tagMax: 5, + color: "#111111", + titles: ["\u7528\u4EE3\u7801\u4E00\u7B14\u4E00\u753B\uFF0C\u753B\u4E86\u4E00\u90E8\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B", "\u5C0F\u65F6\u5019\u542C\u8FC7\u7684\u300A\u72FC\u6765\u4E86\u300B\uFF0C\u8FD9\u6B21\u662F\u6C34\u58A8\u624B\u7ED8\u7248", "\u8BF4\u4E86\u4E24\u6B21\u8C0E\uFF0C\u72FC\u771F\u7684\u6765\u4E86\uFF1A\u6C34\u58A8\u7248\u653E\u7F8A\u5A03"], + desc: `\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u2014\u2014\u628A\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\u3002 +\u653E\u7F8A\u5A03\u4E24\u6B21\u558A\u300C\u72FC\u6765\u4E86\u300D\u9A97\u5927\u5BB6\u4E0A\u5C71\uFF0C\u7B2C\u4E09\u6B21\u72FC\u771F\u7684\u6765\u4E86\uFF0C\u5374\u518D\u4E5F\u6CA1\u6709\u4EBA\u76F8\u4FE1\u4ED6\u3002 +\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u6545\u4E8B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE\u52A8\u753B"], + tip: "\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + }, + { + id: "sph", + name: "\u5FAE\u4FE1\u89C6\u9891\u53F7", + titleMax: 16, + tagMax: null, + color: "#07c160", + titles: ["\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B", "\u653E\u7F8A\u5A03\u8BF4\u8C0E\uFF0C\u72FC\u771F\u7684\u6765\u4E86", "\u505A\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50"], + desc: `\u4E00\u90E8\u50CF\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u7684\u6C34\u58A8\u52A8\u753B\u3002 +\u5C71\u5761\u4E0A\u653E\u7F8A\u7684\u5B69\u5B50\u89C9\u5F97\u65E0\u804A\uFF0C\u5927\u558A\u300C\u72FC\u6765\u4E86\u300D\u9A97\u519C\u6C11\u4EEC\u4E0A\u5C71\uFF1B\u7B2C\u4E8C\u6B21\u53C8\u9A97\u4E86\u4E00\u56DE\u3002\u7B49\u72FC\u771F\u7684\u6765\u4E86\uFF0C\u4ED6\u558A\u7834\u55D3\u5B50\uFF0C\u8C01\u4E5F\u6CA1\u6709\u6765\u3002 +\u8F6C\u7ED9\u5BB6\u91CC\u7684\u5C0F\u670B\u53CB\uFF0C\u5F53\u4F5C\u4ECA\u665A\u7684\u7761\u524D\u6545\u4E8B\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u5BD3\u8A00\u6545\u4E8B", "\u56FD\u98CE", "\u624B\u7ED8", "\u4EB2\u5B50", "\u8BDA\u5B9E", "\u8BED\u6587\u8BFE\u672C", "\u6210\u957F"], + tip: "\u6807\u9898 \u226416 \u5B57\uFF0C\u8BDD\u9898\u6570\u91CF\u4E0D\u9650\u3002" + }, + { + id: "bili", + name: "\u54D4\u54E9\u54D4\u54E9", + titleMax: 80, + tagMax: 10, + color: "#fb7299", + titles: ["\u3010\u6C34\u58A8\u624B\u7ED8\u3011\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\uFF5C\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u591A\u89D2\u8272\u914D\u97F3\uFF0B\u539F\u521B\u56FD\u98CE\u914D\u4E50\uFF0C\u5168\u7A0B\u4EE3\u7801\u9010\u5E27\u7ED8\u5236", "\u3010\u513F\u7AE5\u52A8\u753B\u3011\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B\uFF1A\u653E\u7F8A\u5A03\u7684\u4E09\u58F0\u300C\u72FC\u6765\u4E86\u300D"], + desc: `\u4E00\u90E8\u7EA6 2 \u5206 18 \u79D2\u7684\u6C34\u58A8\u624B\u7ED8\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u6539\u7F16\u81EA\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\uFF08\u653E\u7F8A\u7684\u5B69\u5B50\uFF09\u3002 + +\u753B\u9762\uFF1A\u5168\u90E8\u7531\u4EE3\u7801\u5728\u6D4F\u89C8\u5668\u91CC\u9010\u5E27\u7ED8\u5236\u2014\u2014\u5BA3\u7EB8\u7EB9\u7406\u3001\u53D8\u5BBD\u6BDB\u7B14\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u3001\u6731\u7802\u5370\u7AE0\uFF1B\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u3001\u7F8A\u7FA4\u548C\u72FC\u90FD\u662F\u7A0B\u5E8F\u753B\u51FA\u6765\u7684\uFF0C\u6A2A\u7AD6\u5C4F\u5404\u81EA\u6784\u56FE\u3002\u300C\u72FC\u6765\u4E86\u300D\u4E09\u4E2A\u6731\u7EA2\u5927\u5B57\u4ECE\u753B\u9762\u91CC\u558A\u51FA\u6765\uFF0C\u7B2C\u4E09\u6B21\u88AB\u98CE\u5439\u6563\u3002 +\u58F0\u97F3\uFF1A\u65C1\u767D\u3001\u653E\u7F8A\u5A03\uFF08\u7AE5\u58F0\uFF09\u3001\u519C\u6C11\u4E09\u4E2A\u58F0\u97F3\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF1B\u7F8A\u53EB\u3001\u5C71\u8C37\u56DE\u58F0\u3001\u811A\u6B65\u3001\u9504\u5934\u3001\u96F7\u58F0\u3001\u72FC\u568E\u3001\u7F8A\u7FA4\u5954\u9003\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\u3002 + +\u7AE0\u8282\uFF1A +00:00 \u7247\u5934 +00:06 \u5C71\u6751 +00:17 \u5C71\u5761\u4E0A +00:29 \u7B2C\u4E00\u6B21\u558A +00:39 \u519C\u6C11\u4E0A\u5C71 +00:51 \u54C8\u54C8\u5927\u7B11 +01:00 \u53C8\u558A\u72FC\u6765\u4E86 +01:11 \u8B66\u544A +01:19 \u72FC\u771F\u7684\u6765\u4E86 +01:31 \u547C\u6551 +01:40 \u6CA1\u4EBA\u6765 +01:51 \u7F8A\u7FA4\u56DB\u6563 +01:59 \u540E\u6094 +02:07 \u5BD3\u610F`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u56FD\u98CE", "\u624B\u7ED8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u539F\u521B\u914D\u4E50", "\u4EE3\u7801\u827A\u672F", "\u5BD3\u8A00"], + tip: "\u6807\u9898 \u226480 \u5B57\uFF0C\u6807\u7B7E \u226410 \u4E2A\uFF1B\u7AE0\u8282\u65F6\u95F4\u53EF\u76F4\u63A5\u7C98\u8FDB\u7B80\u4ECB\u3002" + } + ]; + var PHOTO_POSTS2 = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 20, + tagMax: 10, + titles: ["\u72FC\u6765\u4E86\u7684\u516D\u4E2A\u77AC\u95F4\uFF5C\u6C34\u58A8\u5B9E\u51B5\u56FE", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF5C\u72FC\u6765\u4E86"], + body: `\u628A\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86 6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u957F\u6309\u5C31\u80FD\u770B\u5B83\u52A8\u8D77\u6765 \u2728 + +\u5176\u4E00 \xB7 \u574F\u4E3B\u610F\uFF1A\u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u653E\u7F8A\u5A03\u773C\u73E0\u4E00\u8F6C +\u5176\u4E8C \xB7 \u72FC\u6765\u4E86\uFF1A\u8DF3\u4E0A\u77F3\u5934\u5927\u558A\uFF0C\u6731\u7EA2\u5927\u5B57\u98DE\u5411\u5C71\u4E0B +\u5176\u4E09 \xB7 \u6025\u6025\u5FD9\u5FD9\uFF1A\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71 +\u5176\u56DB \xB7 \u54C8\u54C8\u5927\u7B11\uFF1A\u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D +\u5176\u4E94 \xB7 \u771F\u7684\u6765\u4E86\uFF1A\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B +\u5176\u516D \xB7 \u540E\u6094\uFF1A\u5915\u9633\u4E0B\uFF0C\u4ED6\u62B1\u7740\u5C0F\u7F8A\u54ED\u4E86 + +\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002 +\u4F60\u5C0F\u65F6\u5019\u542C\u8FC7\u8FD9\u4E2A\u6545\u4E8B\u5417\uFF1F`, + tags: ["\u72FC\u6765\u4E86", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8", "\u5BD3\u8A00\u6545\u4E8B", "\u7761\u524D\u6545\u4E8B", "\u8BDA\u5B9E", "\u52A8\u6001\u58C1\u7EB8"], + tip: "\u53D1\u5E03\u65F6\u9009\u62E9\u300C\u5B9E\u51B5\u56FE\u300D\u4E0A\u4F20 6 \u7EC4 JPG+MOV\uFF1B\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\u3002" + }, + { + id: "dy", + name: "\u6296\u97F3\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 30, + tagMax: 5, + titles: ["\u6C34\u58A8\u5B9E\u51B5\u56FE\uFF1A\u653E\u7F8A\u5A03\u558A\u72FC\u6765\u4E86\u7684\u516D\u4E2A\u77AC\u95F4", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF0C\u72FC\u771F\u7684\u6765\u4E86"], + body: `6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u8BB2\u5B8C\u4E00\u4E2A\u300A\u72FC\u6765\u4E86\u300B\u3002 +\u574F\u4E3B\u610F \u2192 \u5927\u558A\u72FC\u6765\u4E86 \u2192 \u519C\u6C11\u4E0A\u5C71 \u2192 \u54C8\u54C8\u5927\u7B11 \u2192 \u72FC\u771F\u7684\u6765\u4E86 \u2192 \u5915\u9633\u4E0B\u540E\u6094\u3002 +\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE"], + tip: "\u56FE\u6587\u6A21\u5F0F\u4E0A\u4F20\u5B9E\u51B5\u56FE\uFF1B\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + } ]; + + // src/films/wolf/index.ts + var tl2 = timeline_default2; + var wolf2 = { + id: "wolf", + title: "\u72FC\u6765\u4E86", + tagline: "\u653E\u7F8A\u7684\u5B69\u5B50", + intro: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u6C34\u58A8\u624B\u7ED8\u513F\u7AE5\u52A8\u753B\u3002\u5C71\u6751\u3001\u68AF\u7530\u3001\u7F8A\u7FA4\u548C\u4E00\u4E2A\u7231\u5F00\u73A9\u7B11\u7684\u653E\u7F8A\u5A03\u2014\u2014\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\uFF0C\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE\u4E00\u6837\u5728\u6D4F\u89C8\u5668\u91CC\u5B9E\u65F6\u753B\u51FA\u6765\uFF1B\u65C1\u767D\u3001\u653E\u7F8A\u5A03\u548C\u519C\u6C11\u5404\u6709\u58F0\u97F3\uFF0C\u914D\u4E50\u4E0E\u97F3\u6548\u540C\u6B65\u64AD\u653E\u3002", + credit: "\u6545\u4E8B\u6539\u7F16\u81EA\u300A\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u72FC\u6765\u4E86\u300B\uFF08\u516C\u6709\u9886\u57DF\uFF09", + assets: "wolf/", + slug: "wolf-coming", + timeline: tl2, + scenes: SCENES3, + seal: ["\u5BD3\u8A00"], + series: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u72FC\u6765\u4E86", + subColors: { boy: "#9a2f22", farmer: "#2f4a5a" }, + score: score2, + effects: effects2, + covers: COVERS(tl2), + live: live_default2, + platforms: PLATFORMS2, + photoPosts: PHOTO_POSTS2, + qa: qa_default2, + making: [ + ["\u753B\u9762", "\u4E0E\u300A\u4E11\u5C0F\u9E2D\u300B\u5171\u7528\u4E00\u5957\u4EE3\u7801\u6C34\u58A8\u5F15\u64CE\uFF08\u5BA3\u7EB8\u3001\u53D8\u5BBD\u6BDB\u7B14\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u3001\u5370\u7AE0\uFF09\uFF0C\u672C\u7247\u65B0\u753B\u4E86\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u3001\u7F8A\u7FA4\u3001\u72FC\u3001\u68AF\u7530\u548C\u8349\u5C4B\u3002\u300C\u72FC\u6765\u4E86\u300D\u4E09\u4E2A\u6731\u7EA2\u5927\u5B57\u4ECE\u753B\u9762\u91CC\u558A\u51FA\u6765\uFF0C\u7B2C\u4E09\u6B21\u558A\u65F6\u88AB\u98CE\u5439\u6563\u2014\u2014\u6CA1\u4EBA\u4FE1\u4E86\u3002"], + ["\u58F0\u97F3", "\u4E09\u4E2A\u58F0\u97F3\uFF1A\u65C1\u767D\uFF08\u6653\u6653\uFF09\u3001\u653E\u7F8A\u5A03\uFF08\u4E91\u590F\u7AE5\u58F0\uFF09\u3001\u519C\u6C11\uFF08\u4E91\u5065\uFF09\u9010\u53E5\u5408\u6210\u540E\u6309\u65F6\u95F4\u8F74\u62FC\u63A5\u3002\u539F\u521B\u914D\u4E50\u7531\u8C03\u76AE\u7684\u7AF9\u7B1B\u6728\u9C7C\u3001\u7D27\u5F20\u7684\u4F4E\u97F3\u98A4\u594F\u3001\u60CB\u60DC\u7684\u53E4\u7B5D\u7EC4\u6210\uFF1B\u7F8A\u53EB\u3001\u558A\u58F0\u56DE\u58F0\u3001\u811A\u6B65\u3001\u72FC\u568E\u3001\u7F8A\u7FA4\u5954\u9003\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\uFF0C\u6574\u4F53 \u221214 LUFS\u3002"], + ["\u6D41\u7A0B", "\u6309 vibe-video \u6D41\u7A0B\uFF1A\u5206\u955C \u2192 \u591A\u89D2\u8272\u914D\u97F3\u751F\u6210\u65F6\u95F4\u8F74 \u2192 \u63A5\u89E6\u8868 / \u9759\u5E27 \u2192 \u786E\u5B9A\u6027\u81EA\u68C0 \u2192 \u5168\u7247\u6E32\u67D3 \u2192 \u6DF7\u97F3 \u2192 vibe qa\uFF1B\u5B9E\u51B5\u56FE\u6309 livecanvas \u6D41\u7A0B\u914D\u5BF9\u5C01\u88C5\u3002"] + ] + }; + + // src/films/index.ts + var FILMS = [duckling, wolf2]; + + // src/film/overlays.ts + var at2 = (id, dt) => (TL.scenes.find((s) => s.id === id)?.start ?? 0) + dt; function titleBlock(g2, W2, H2, title, sub, side, o = {}) { const u = Math.min(W2, H2) / 1080; const chars = [...title]; @@ -3718,7 +7304,7 @@ }); g2.restore(); const sy = y0 + colH - fs * 0.15; - seal(g2, x + (side === "left" ? -fs * 0.05 : fs * 0.05), sy + fs * 0.1, fs * 0.42, o.seal ?? ["\u7AE5\u8BDD"]); + seal(g2, x + (side === "left" ? -fs * 0.05 : fs * 0.05), sy + fs * 0.1, fs * 0.42, o.seal ?? FILM.seal); if (o.index) { g2.save(); g2.font = `${sfs * 1.1}px ${FONT_BRUSH}`; @@ -3728,15 +7314,15 @@ g2.restore(); } } - var LIVE = live_default; - var liveStart = (l) => at(l.scene, l.from); + var liveStart = (l) => at2(l.scene, l.from); function drawLive(g2, W2, H2, l, lt) { renderFrame(g2, W2, H2, liveStart(l) + lt, { subs: false, fadeEnd: false }); - titleBlock(g2, W2, H2, l.caption, "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u4E11\u5C0F\u9E2D", "right", { big: 120, index: l.index }); + titleBlock(g2, W2, H2, l.caption, FILM.series, "right", { big: 120, index: l.index }); } // src/film/standalone.ts var q = new URLSearchParams(location.search); + setFilm(FILMS.find((f) => f.id === (q.get("film") || "duckling"))); var W = Number(q.get("w")) || innerWidth; var H = Number(q.get("h")) || innerHeight; var c = document.createElement("canvas"); @@ -3755,19 +7341,19 @@ }); window.renderAudio = async () => { const t0 = performance.now(); - const b = await buildSoundtrack((q.get("base") || "./") + "film/voice.mp3", (s2) => console.log("stage", s2, Math.round(performance.now() - t0))); + const b = await buildSoundtrack((q.get("base") || "./") + FILM.assets + "voice.mp3", (s2) => console.log("stage", s2, Math.round(performance.now() - t0))); const bytes = new Uint8Array(toWav(b)); let s = ""; for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode(...bytes.subarray(i, i + 32768)); return btoa(s); }; - window.__dbg = { ...actors_exports, ...core_exports }; + window.__dbg = { ...actors_exports, ...core_exports, W: actors_exports2 }; window.__g = g; window.__W = W; window.__H = H; - window.__live = LIVE; + window.__live = FILM.live; window.renderLive = (id, lt) => { - const l = LIVE.find((x) => x.id === id); + const l = FILM.live.find((x) => x.id === id); if (l) drawLive(g, W, H, l, lt); }; })(); diff --git a/ugly-duckling/video-portrait/film/fonts/mashanzheng.woff2 b/ugly-duckling/video-portrait/film/fonts/mashanzheng.woff2 index c6ebf28b26ed73e01389a2ba5bcee1e199f70a24..336f627eea468eaf298942d59a3c615369298a25 100644 --- a/ugly-duckling/video-portrait/film/fonts/mashanzheng.woff2 +++ b/ugly-duckling/video-portrait/film/fonts/mashanzheng.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009f88eccbb75b93ece3214992da9dbab3c96e3e003d1078a88d753a001b630 -size 343228 +oid sha256:fe7ce130e02290a7dff93743e8a7492fb7c41bdbe9d6b338945144e018735476 +size 420940 diff --git a/ugly-duckling/video-portrait/film/fonts/wenkai.woff2 b/ugly-duckling/video-portrait/film/fonts/wenkai.woff2 index a9a12a80e05800bdfaaf761ad87db25d19e2e1cf..d3eee58aa0e17d95557ba0ac444a80499e4a0fef 100644 --- a/ugly-duckling/video-portrait/film/fonts/wenkai.woff2 +++ b/ugly-duckling/video-portrait/film/fonts/wenkai.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c673c2d90b5d3e48ff76694316aec488e221d610b6269440d795d093207566fe -size 200844 +oid sha256:14374c15ca95593a0fc159be8f69f4435a3e7c4206b1817fd7bfba9495222f02 +size 240976 diff --git a/ugly-duckling/video/film.bundle.js b/ugly-duckling/video/film.bundle.js index 11a6781e4319cbea1ba8d2c8719b9545b342d4d1..016068fd7b2e3f32e934d76059baa35b44ca9e2e 100644 --- a/ugly-duckling/video/film.bundle.js +++ b/ugly-duckling/video/film.bundle.js @@ -6,1093 +6,1503 @@ __defProp(target, name, { get: all[name], enumerable: true }); }; - // src/film/timeline.json - var timeline_default = { - title: "\u4E11\u5C0F\u9E2D", - style: "ink-wash textbook illustration", - w: 1920, - h: 1080, - fps: 30, - total: 156.3, - frames: 4689, - voice: "zh-CN-XiaoxiaoNeural", - scenes: [ - { - id: "title", - start: 0, - dur: 6.5, - voice_start: 0.5, - voice_dur: 2.832, - subs: [ - { - t0: 0.5, - t1: 2.199, - text: "\u5B89\u5F92\u751F\u7AE5\u8BDD" - }, - { - t0: 2.199, - t1: 3.332, - text: "\u4E11\u5C0F\u9E2D" - } - ], - cue: { - type: "title" - }, - sfx: [ - { - at: 0.3, - type: "drop" - }, - { - at: 4.6, - type: "seal" - } - ] - }, - { - id: "summer", - start: 6.5, - dur: 11.566666666666666, - voice_start: 7, - voice_dur: 10.176, - subs: [ - { - t0: 7, - t1: 8.42, - text: "\u590F\u5929\u7684\u4E61\u4E0B" - }, - { - t0: 8.42, - t1: 9.603, - text: "\u9EA6\u5B50\u91D1\u9EC4" - }, - { - t0: 9.603, - t1: 12.68, - text: "\u6C60\u5858\u8FB9\u7684\u82A6\u82C7\u957F\u5F97\u6BD4\u4EBA\u8FD8\u9AD8" - }, - { - t0: 12.68, - t1: 14.573, - text: "\u9E2D\u5988\u5988\u5C31\u5728\u8FD9\u91CC" - }, - { - t0: 14.573, - t1: 17.176, - text: "\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB" - } - ], - cue: { - type: "summer" - }, - sfx: [ - { - at: 0.2, - type: "breeze" - } - ] - }, - { - id: "hatch", - start: 18.0667, - dur: 11.566666666666666, - voice_start: 18.5667, - voice_dur: 10.176, - subs: [ - { - t0: 18.567, - t1: 19.26, - text: "\u5494\u5693" - }, - { - t0: 19.26, - t1: 19.954, - text: "\u5494\u5693\uFF01" - }, - { - t0: 19.954, - t1: 22.267, - text: "\u86CB\u58F3\u4E00\u4E2A\u63A5\u4E00\u4E2A\u88C2\u5F00" - }, - { - t0: 22.267, - t1: 25.042, - text: "\u6BDB\u8338\u8338\u7684\u5C0F\u9EC4\u9E2D\u94BB\u4E86\u51FA\u6765" - }, - { - t0: 25.042, - t1: 26.43, - text: "\u5527\u5527\u5730\u53EB\u7740" - }, - { - t0: 26.43, - t1: 28.743, - text: "\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C" - } - ], - cue: { - type: "hatch" - }, - sfx: [ - { - at: 0.5, - type: "crack" - }, - { - at: 1.2, - type: "crack" - }, - { - at: 2.2, - type: "peep" - } - ] - }, - { - id: "bigegg", - start: 29.6333, - dur: 11.8, - voice_start: 30.1333, - voice_dur: 10.416, - subs: [ - { - t0: 30.133, - t1: 32.217, - text: "\u53EA\u5269\u6700\u5927\u7684\u90A3\u9897\u86CB" - }, - { - t0: 32.217, - t1: 33.374, - text: "\u8FC7\u4E86\u597D\u4E45" - }, - { - t0: 33.374, - t1: 34.763, - text: "\u624D\u6162\u6162\u88C2\u5F00" - }, - { - t0: 34.763, - t1: 36.614, - text: "\u94BB\u51FA\u6765\u7684\u5C0F\u5BB6\u4F19" - }, - { - t0: 36.614, - t1: 37.772, - text: "\u53C8\u5927\u53C8\u7070" - }, - { - t0: 37.772, - t1: 40.549, - text: "\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0" - } - ], - cue: { - type: "bigegg" - }, - sfx: [ - { - at: 2.6, - type: "bigcrack" - } - ] - }, - { - id: "bullied", - start: 41.4333, - dur: 10.366666666666667, - voice_start: 41.9333, - voice_dur: 8.952, - subs: [ - { - t0: 41.933, - t1: 44.111, - text: "\u5927\u5BB6\u90FD\u53EB\u4ED6\u4E11\u5C0F\u9E2D" - }, - { - t0: 44.111, - t1: 46.046, - text: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6" - }, - { - t0: 46.046, - t1: 47.74, - text: "\u516C\u9E21\u8FFD\u7740\u5544\u4ED6" - }, - { - t0: 47.74, - t1: 50.885, - text: "\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B" - } - ], - cue: { - type: "bullied" - }, - sfx: [ - { - at: 0.8, - type: "quack" - }, - { - at: 3.2, - type: "flap" - } - ] - }, - { - id: "leave", - start: 51.8, - dur: 7.533333333333333, - voice_start: 52.3, - voice_dur: 6.144, - subs: [ - { - t0: 52.3, - t1: 54.348, - text: "\u4E11\u5C0F\u9E2D\u96BE\u8FC7\u6781\u4E86" - }, - { - t0: 54.348, - t1: 55.116, - text: "\u4E00\u5929" - }, - { - t0: 55.116, - t1: 57.164, - text: "\u4ED6\u6084\u6084\u94BB\u8FC7\u7BF1\u7B06" - }, - { - t0: 57.164, - t1: 58.444, - text: "\u79BB\u5F00\u4E86\u5BB6" - } - ], - cue: { - type: "leave" - }, - sfx: [ - { - at: 0.3, - type: "wind" + // src/film/core.ts + var core_exports = {}; + __export(core_exports, { + FONT_BRUSH: () => FONT_BRUSH, + FONT_KAI: () => FONT_KAI, + INK: () => INK, + PAPER: () => PAPER, + SEAL: () => SEAL, + backOut: () => backOut, + blobLoop: () => blobLoop, + blot: () => blot, + brush: () => brush, + clamp: () => clamp, + ctx2d: () => ctx2d, + easeIn: () => easeIn, + easeInOut: () => easeInOut, + easeOut: () => easeOut, + hash: () => hash, + inv: () => inv, + lerp: () => lerp, + loadFonts: () => loadFonts, + makeCanvas: () => makeCanvas, + mixHex: () => mixHex, + mountains: () => mountains, + noise1: () => noise1, + nz: () => nz, + outline: () => outline, + paper: () => paper, + rgba: () => rgba, + rng: () => rng, + seal: () => seal, + smooth: () => smooth, + spline: () => spline, + spring: () => spring, + stroke: () => stroke, + subPath: () => subPath, + tracePath: () => tracePath, + wash: () => wash + }); + function rng(seed) { + let a = seed >>> 0 || 1; + return () => { + a = a + 1831565813 >>> 0; + let t = a; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + }; + } + function hash(n) { + const s = Math.sin(n * 127.1 + 311.7) * 43758.5453; + return s - Math.floor(s); + } + function noise1(x, seed = 0) { + const i = Math.floor(x), f = x - i; + const a = hash(i + seed * 101.3), b = hash(i + 1 + seed * 101.3); + const u = f * f * (3 - 2 * f); + return a + (b - a) * u; + } + var nz = (x, seed = 0) => noise1(x, seed) * 2 - 1; + var clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x)); + var lerp = (a, b, t) => a + (b - a) * t; + var inv = (a, b, x) => clamp((x - a) / (b - a)); + var smooth = (x) => { + x = clamp(x); + return x * x * (3 - 2 * x); + }; + var easeOut = (x) => 1 - Math.pow(1 - clamp(x), 3); + var easeIn = (x) => Math.pow(clamp(x), 3); + var easeInOut = (x) => { + x = clamp(x); + return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; + }; + var backOut = (x) => { + x = clamp(x); + const c1 = 1.70158, c3 = c1 + 1; + return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2); + }; + var spring = (x) => { + x = Math.max(0, x); + return 1 - Math.exp(-6 * x) * Math.cos(11 * x); + }; + var INK = "#1d1a17"; + var PAPER = "#f6efdf"; + var SEAL = "#b8322a"; + function rgba(hex, a) { + const n = parseInt(hex.slice(1), 16); + return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`; + } + function mixHex(a, b, t) { + const x = parseInt(a.slice(1), 16), y = parseInt(b.slice(1), 16); + const c2 = (s) => Math.round(lerp(x >> s & 255, y >> s & 255, clamp(t))); + return "#" + (1 << 24 | c2(16) << 16 | c2(8) << 8 | c2(0)).toString(16).slice(1); + } + function makeCanvas(w, h) { + if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(w, h); + const c2 = document.createElement("canvas"); + c2.width = w; + c2.height = h; + return c2; + } + function ctx2d(c2) { + return c2.getContext("2d"); + } + var paperCache = /* @__PURE__ */ new Map(); + function paper(W2, H2) { + const key = W2 + "x" + H2; + const hit = paperCache.get(key); + if (hit) return hit; + const c2 = makeCanvas(W2, H2), g2 = ctx2d(c2), r = rng(20261017); + g2.fillStyle = PAPER; + g2.fillRect(0, 0, W2, H2); + const u = Math.min(W2, H2) / 1080; + for (let i = 0; i < 46; i++) { + const x = r() * W2, y = r() * H2, rad = (120 + r() * 420) * u; + const gr = g2.createRadialGradient(x, y, 0, x, y, rad); + const dark = r() < 0.55; + gr.addColorStop(0, dark ? "rgba(170,140,90,0.028)" : "rgba(255,252,240,0.08)"); + gr.addColorStop(1, "rgba(0,0,0,0)"); + g2.fillStyle = gr; + g2.fillRect(x - rad, y - rad, rad * 2, rad * 2); + } + g2.strokeStyle = "rgba(150,125,85,0.035)"; + g2.lineWidth = Math.max(1, u); + for (let y = 0; y < H2; y += 9 * u) { + g2.beginPath(); + g2.moveTo(0, y + r() * 2); + g2.lineTo(W2, y + r() * 2); + g2.stroke(); + } + const nf = Math.round(W2 * H2 / 2600); + for (let i = 0; i < nf; i++) { + const x = r() * W2, y = r() * H2, len = (6 + r() * 26) * u, ang = r() * Math.PI * 2; + g2.strokeStyle = r() < 0.7 ? `rgba(150,120,75,${0.05 + r() * 0.08})` : `rgba(255,252,240,${0.2 + r() * 0.25})`; + g2.lineWidth = (0.4 + r() * 0.9) * u; + g2.beginPath(); + g2.moveTo(x, y); + g2.quadraticCurveTo(x + Math.cos(ang + 0.6) * len * 0.5, y + Math.sin(ang + 0.6) * len * 0.5, x + Math.cos(ang) * len, y + Math.sin(ang) * len); + g2.stroke(); + } + try { + const id = g2.getImageData(0, 0, W2, H2), d = id.data; + let s = 12345; + for (let i = 0; i < d.length; i += 4) { + s = s * 1103515245 + 12345 & 2147483647; + const n = (s >> 8) % 15 - 7; + d[i] += n; + d[i + 1] += n; + d[i + 2] += n; + } + g2.putImageData(id, 0, 0); + } catch { + } + const vg = g2.createRadialGradient(W2 / 2, H2 / 2, Math.min(W2, H2) * 0.35, W2 / 2, H2 / 2, Math.hypot(W2, H2) * 0.62); + vg.addColorStop(0, "rgba(0,0,0,0)"); + vg.addColorStop(1, "rgba(120,90,50,0.13)"); + g2.fillStyle = vg; + g2.fillRect(0, 0, W2, H2); + paperCache.set(key, c2); + if (paperCache.size > 4) paperCache.delete(paperCache.keys().next().value); + return c2; + } + function spline(pts, seg = 8, closed = false) { + const out = []; + const n = pts.length; + if (n < 3) return pts.slice(); + const get = (i) => closed ? pts[(i + n) % n] : pts[Math.max(0, Math.min(n - 1, i))]; + const last = closed ? n : n - 1; + for (let i = 0; i < last; i++) { + const p0 = get(i - 1), p1 = get(i), p2 = get(i + 1), p3 = get(i + 2); + for (let j = 0; j < seg; j++) { + const t = j / seg, t2 = t * t, t3 = t2 * t; + out.push([ + 0.5 * (2 * p1[0] + (-p0[0] + p2[0]) * t + (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 + (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3), + 0.5 * (2 * p1[1] + (-p0[1] + p2[1]) * t + (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 + (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3) + ]); + } + } + if (!closed) out.push(pts[n - 1]); + return out; + } + function subPath(pts, a, b) { + if (pts.length < 2) return pts; + const L = [0]; + for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); + const tot = L[L.length - 1], A = a * tot, B2 = b * tot, out = []; + const at3 = (d) => { + for (let i = 1; i < L.length; i++) if (L[i] >= d) { + const f = (d - L[i - 1]) / Math.max(1e-6, L[i] - L[i - 1]); + return [lerp(pts[i - 1][0], pts[i][0], f), lerp(pts[i - 1][1], pts[i][1], f)]; + } + return pts[pts.length - 1]; + }; + out.push(at3(A)); + for (let i = 0; i < pts.length; i++) if (L[i] > A && L[i] < B2) out.push(pts[i]); + out.push(at3(B2)); + return out; + } + function tracePath(g2, pts, close = false) { + g2.beginPath(); + g2.moveTo(pts[0][0], pts[0][1]); + for (let i = 1; i < pts.length; i++) g2.lineTo(pts[i][0], pts[i][1]); + if (close) g2.closePath(); + } + function brush(g2, pts0, w, o = {}) { + const grow = o.grow ?? 1; + if (grow <= 0 || pts0.length < 2) return; + const pts = grow < 1 ? subPath(pts0, 0, grow) : pts0; + const n = pts.length; + const L = [0]; + for (let i = 1; i < n; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); + const tot = Math.max(1e-6, L[n - 1]); + const tin = o.taperIn ?? 0.18, tout = o.taperOut ?? 0.35, jit = o.jitter ?? 0.18, seed = o.seed ?? 1; + const left = [], right = []; + for (let i = 0; i < n; i++) { + const s = L[i] / tot; + const a = pts[Math.max(0, i - 1)], b = pts[Math.min(n - 1, i + 1)]; + let dx = b[0] - a[0], dy = b[1] - a[1]; + const d = Math.hypot(dx, dy) || 1; + dx /= d; + dy /= d; + let prof = Math.min(1, 0.35 + 0.65 * smooth(s / tin)) * Math.min(1, Math.pow(clamp((1 - s) / tout), 0.7) * 0.92 + 0.08); + if (o.press) prof *= 1 + o.press * Math.exp(-s * 9); + const ww = w / 2 * prof * (1 + jit * nz(s * 7 + seed * 3.1, seed)); + left.push([pts[i][0] - dy * ww, pts[i][1] + dx * ww]); + right.push([pts[i][0] + dy * ww, pts[i][1] - dx * ww]); + } + g2.fillStyle = rgba(o.color ?? INK, o.alpha ?? 0.92); + g2.beginPath(); + g2.moveTo(left[0][0], left[0][1]); + for (let i = 1; i < n; i++) g2.lineTo(left[i][0], left[i][1]); + for (let i = n - 1; i >= 0; i--) g2.lineTo(right[i][0], right[i][1]); + g2.closePath(); + g2.fill(); + if ((o.alpha ?? 0.92) > 0.5 && w > 3) { + g2.fillStyle = rgba(o.color ?? INK, 0.12); + g2.beginPath(); + g2.arc(pts[0][0], pts[0][1], w / 2 * 0.55, 0, Math.PI * 2); + g2.fill(); + } + if (o.dry && o.dry > 0) { + g2.strokeStyle = rgba(PAPER, 0.75 * o.dry); + const lines = 4; + for (let k = 0; k < lines; k++) { + const off = (k + 0.5) / lines - 0.5; + g2.lineWidth = Math.max(0.6, w * 0.07); + g2.beginPath(); + let started = false; + for (let i = Math.floor(n * (0.45 + 0.1 * hash(seed + k))); i < n; i++) { + const sx = lerp(left[i][0], right[i][0], 0.5 + off * 0.8), sy = lerp(left[i][1], right[i][1], 0.5 + off * 0.8); + if (hash(i * 0.37 + k * 13 + seed) < 0.18) { + started = false; + continue; } - ] - }, + if (!started) { + g2.moveTo(sx, sy); + started = true; + } else g2.lineTo(sx, sy); + } + g2.stroke(); + } + } + } + function stroke(g2, ctrl, w, o = {}) { + brush(g2, spline(ctrl, 10), w, o); + } + function outline(g2, loop, w, o = {}) { + const pts = spline(loop, 8, true); + pts.push(pts[0]); + const br = o.breaks ?? [0, 0.36, 0.7, 1]; + for (let i = 0; i < br.length - 1; i++) { + const a = br[i] + 0.012, b = br[i + 1] - 0.012; + if (b > a) brush(g2, subPath(pts, a, b), w, { taperIn: 0.12, taperOut: 0.25, ...o, seed: (o.seed ?? 1) + i * 7 }); + } + } + function wash(g2, loop, color, alpha = 0.6, o = {}) { + const pts = spline(loop, 8, o.closed ?? true); + g2.save(); + g2.translate(o.dx ?? 0, o.dy ?? 0); + tracePath(g2, pts, true); + g2.fillStyle = rgba(color, alpha); + g2.fill(); + if ((o.edge ?? 1) > 0) { + g2.strokeStyle = rgba(color, Math.min(1, alpha * 0.5 * (o.edge ?? 1))); + g2.lineWidth = o.lw ?? 2.2; + g2.lineJoin = "round"; + g2.stroke(); + } + g2.restore(); + } + function blot(g2, x, y, r, color, a, sx = 1, sy = 1) { + if (r <= 0 || a <= 0) return; + g2.save(); + g2.translate(x, y); + g2.scale(sx, sy); + const gr = g2.createRadialGradient(0, 0, 0, 0, 0, r); + gr.addColorStop(0, rgba(color, a)); + gr.addColorStop(0.55, rgba(color, a * 0.6)); + gr.addColorStop(1, rgba(color, 0)); + g2.fillStyle = gr; + g2.fillRect(-r, -r, r * 2, r * 2); + g2.restore(); + } + function blobLoop(cx, cy, r, seed, rough = 0.18, n = 28) { + const out = []; + for (let i = 0; i < n; i++) { + const a = i / n * Math.PI * 2; + const rr = r * (1 + rough * nz(i * 0.7 + seed, seed) + rough * 0.5 * nz(i * 2.3 + seed * 3, seed + 1)); + out.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]); + } + return out; + } + function mountains(g2, W2, baseY, height, color, alpha, seed, o = {}) { + const f = o.freq ?? 2.2; + const pts = []; + const steps2 = 60; + for (let i = 0; i <= steps2; i++) { + const x = i / steps2 * W2; + const v = 0.55 * noise1(i / steps2 * f + seed, seed) + 0.3 * noise1(i / steps2 * f * 2.7 + seed * 2, seed + 1) + 0.15 * noise1(i / steps2 * f * 7 + seed * 5, seed + 2); + pts.push([x, baseY - height * Math.pow(v, 1.4) * 1.6]); + } + const top = Math.min(...pts.map((p) => p[1])); + const gr = g2.createLinearGradient(0, top, 0, baseY); + gr.addColorStop(0, rgba(color, alpha)); + gr.addColorStop(0.6, rgba(color, alpha * 0.55)); + gr.addColorStop(1, rgba(color, 0)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, baseY + 2); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, baseY + 2); + g2.closePath(); + g2.fill(); + if ((o.soft ?? 0) < 1) { + const r = rng(seed * 991); + for (let k = 0; k < 6; k++) { + const a = r() * 0.85, b = a + 0.06 + r() * 0.12; + brush(g2, subPath(pts, a, Math.min(1, b)), 2.5 + r() * 3, { color, alpha: alpha * 0.9, seed: seed + k, jitter: 0.4, dry: 0.4 }); + } + } + } + function seal(g2, x, y, size, text, o = {}) { + const a = o.alpha ?? 1; + if (a <= 0) return; + g2.save(); + g2.translate(x, y); + const s = size; + g2.globalAlpha = a; + const loop = [[-s / 2, -s / 2], [s / 2, -s / 2], [s / 2, s / 2], [-s / 2, s / 2]]; + g2.fillStyle = SEAL; + g2.beginPath(); + if (o.round) g2.arc(0, 0, s / 2, 0, Math.PI * 2); + else { + g2.moveTo(loop[0][0], loop[0][1]); + for (const p of loop.slice(1)) g2.lineTo(p[0], p[1]); + g2.closePath(); + } + g2.fill(); + g2.fillStyle = PAPER; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const cols = text.length; + const rows = Math.max(...text.map((c2) => c2.length)); + const cell = s * 0.84 / Math.max(cols, rows); + g2.font = `${cell * 0.95}px ${o.font ?? FONT_BRUSH}`; + for (let c2 = 0; c2 < cols; c2++) { + const col = text[c2]; + for (let r2 = 0; r2 < col.length; r2++) { + const cx = (cols - 1) / 2 * cell - c2 * cell; + const cy = (r2 - (col.length - 1) / 2) * cell * (rows / col.length); + g2.fillText(col[r2], cx, cy + cell * 0.04); + } + } + const r = rng(Math.round(x * 7 + y * 13)); + g2.fillStyle = PAPER; + for (let i = 0; i < 26; i++) { + g2.globalAlpha = a * (0.25 + r() * 0.5); + g2.beginPath(); + g2.arc((r() - 0.5) * s, (r() - 0.5) * s, r() * s * 0.025 + 0.5, 0, Math.PI * 2); + g2.fill(); + } + g2.restore(); + } + var FONT_KAI = '"WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; + var FONT_BRUSH = '"MaShanZhengFilm", "WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; + var fontsPromise = null; + function loadFonts(base) { + if (fontsPromise) return fontsPromise; + if (typeof FontFace === "undefined") return fontsPromise = Promise.resolve(); + const list = [["WenKaiFilm", base + "film/fonts/wenkai.woff2"], ["MaShanZhengFilm", base + "film/fonts/mashanzheng.woff2"]]; + fontsPromise = Promise.all(list.map(async ([fam, url]) => { + try { + const f = new FontFace(fam, `url(${url})`); + await f.load(); + document.fonts.add(f); + } catch (e) { + console.warn("font load failed", fam, e); + } + })).then(() => void 0); + return fontsPromise; + } + + // src/film/film.ts + var FILM = null; + var TL = { total: 1, fps: 30, scenes: [] }; + var TOTAL = 1; + var SCENES = {}; + function setFilm(f) { + if (FILM === f) return; + FILM = f; + TL = f.timeline; + TOTAL = f.timeline.total; + SCENES = f.scenes; + bgCache.clear(); + } + var bgCache = /* @__PURE__ */ new Map(); + function cachedBg(id, W2, H2, draw) { + const k = `${id}:${W2}x${H2}`; + let c2 = bgCache.get(k); + if (c2) { + bgCache.delete(k); + bgCache.set(k, c2); + } + if (!c2) { + c2 = makeCanvas(W2, H2); + const g2 = ctx2d(c2); + draw(g2, W2, H2); + bgCache.set(k, c2); + if (bgCache.size > 8) bgCache.delete(bgCache.keys().next().value); + } + return c2; + } + function sceneIndexAt(T) { + const s = TL.scenes; + for (let i = s.length - 1; i >= 0; i--) if (T >= s[i].start) return i; + return 0; + } + function drawScene(g2, W2, H2, i, T) { + const tl3 = TL.scenes[i]; + const def = SCENES[tl3.id]; + const u = Math.min(W2, H2) / 1080; + const portrait = H2 > W2 * 1.05; + const t = T - tl3.start; + const sc = { g: g2, W: W2, H: H2, u, t, D: tl3.dur, T, portrait, gy: H2 * (portrait ? 0.62 : 0.6), X: (f) => W2 / 2 + f * Math.min(W2 / 2, H2 * 0.85) }; + g2.drawImage(paper(W2, H2), 0, 0); + g2.save(); + const cam3 = def.cam ? def.cam(sc) : { z: 1, x: W2 / 2, y: H2 / 2, dx: 0, dy: 0 }; + const z = Math.max(1, cam3.z); + const cx = Math.min(Math.max(cam3.x, W2 / (2 * z)), W2 - W2 / (2 * z)); + const cy = Math.min(Math.max(cam3.y, H2 / (2 * z)), H2 - H2 / (2 * z)); + g2.translate(W2 / 2 + (cam3.dx ?? 0), H2 / 2 + (cam3.dy ?? 0)); + g2.scale(z, z); + g2.translate(-cx, -cy); + if (def.bg) g2.drawImage(cachedBg(tl3.id, W2, H2, (bg, w, h) => def.bg({ ...sc, g: bg, W: w, H: h })), 0, 0); + def.draw(sc); + g2.restore(); + if (def.overlay) def.overlay(sc); + } + var TR = 1.1; + function renderFrame(g2, W2, H2, T, opts = {}) { + T = clamp(T, 0, TOTAL - 1e-4); + const i = sceneIndexAt(T); + const tl3 = TL.scenes[i]; + const lt = T - tl3.start; + g2.save(); + g2.setTransform(1, 0, 0, 1, 0, 0); + g2.globalAlpha = 1; + g2.globalCompositeOperation = "source-over"; + if (i > 0 && lt < TR) { + drawScene(g2, W2, H2, i - 1, T); + const p = easeInOut(lt / TR); + const R = Math.hypot(W2, H2) * 0.62 * p / 0.8 + 1; + const def = SCENES[tl3.id]; + const fx = (def.enterAt?.[0] ?? 0.5) * W2, fy = (def.enterAt?.[1] ?? 0.5) * H2; + const loop = spline(blobLoop(fx, fy, R, i * 17 + 3, 0.16, 26), 4, true); + g2.save(); + tracePath(g2, loop, true); + g2.clip(); + drawScene(g2, W2, H2, i, T); + g2.restore(); + g2.save(); + tracePath(g2, loop, true); + g2.strokeStyle = rgba(INK, 0.16 * (1 - p)); + g2.lineWidth = Math.min(W2, H2) * 0.02 * (1 - p) + 1; + g2.stroke(); + g2.strokeStyle = rgba("#7a6a50", 0.12 * (1 - p)); + g2.lineWidth = Math.min(W2, H2) * 0.05 * (1 - p) + 1; + g2.stroke(); + g2.restore(); + } else { + drawScene(g2, W2, H2, i, T); + } + if (opts.subs !== false) drawSubs(g2, W2, H2, T); + if (opts.fadeEnd !== false) { + const f = smooth(inv(TOTAL - 1.2, TOTAL - 0.1, T)); + if (f > 0) { + g2.globalAlpha = f; + g2.drawImage(paper(W2, H2), 0, 0); + g2.globalAlpha = 1; + } + } + g2.restore(); + } + function currentSub(T) { + const i = sceneIndexAt(T); + const subs = TL.scenes[i].subs ?? []; + for (let k = 0; k < subs.length; k++) { + const s = subs[k]; + const end = k + 1 < subs.length ? subs[k + 1].t0 : s.t1 + 0.35; + if (T >= s.t0 - 0.05 && T < end) { + const joinedPrev = k > 0 && s.t0 - subs[k - 1].t1 < 0.12; + const joinedNext = k + 1 < subs.length && subs[k + 1].t0 - s.t1 < 0.12; + const fin = joinedPrev ? 1 : smooth((T - s.t0 + 0.05) / 0.18); + const fout = joinedNext ? 1 : smooth((end - T) / 0.15); + return { text: s.text, a: Math.min(fin, fout), who: s.who }; + } + } + return null; + } + function drawSubs(g2, W2, H2, T) { + const s = currentSub(T); + if (!s || s.a <= 0) return; + if (SCENES[TL.scenes[sceneIndexAt(T)].id]?.noSubs) return; + const portrait = H2 > W2 * 1.05; + const u = Math.min(W2, H2) / 1080; + const fs = (portrait ? 58 : 50) * u; + const y = portrait ? H2 * 0.785 : H2 - 86 * u; + g2.save(); + g2.globalAlpha = s.a; + g2.font = `${fs}px ${FONT_KAI}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const w = g2.measureText(s.text).width; + const rx = w / 2 + fs * 2.2, ry = fs * 1.35; + g2.save(); + g2.translate(W2 / 2, y); + g2.scale(rx / ry, 1); + const gr = g2.createRadialGradient(0, 0, 0, 0, 0, ry); + gr.addColorStop(0, rgba(PAPER, 0.95)); + gr.addColorStop(0.72, rgba(PAPER, 0.9)); + gr.addColorStop(1, rgba(PAPER, 0)); + g2.fillStyle = gr; + g2.fillRect(-ry, -ry, ry * 2, ry * 2); + g2.restore(); + g2.lineJoin = "round"; + g2.strokeStyle = rgba(PAPER, 0.96); + g2.lineWidth = fs * 0.2; + g2.strokeText(s.text, W2 / 2, y); + g2.fillStyle = rgba(s.who && FILM.subColors?.[s.who] || INK, 0.97); + g2.fillText(s.text, W2 / 2, y); + g2.restore(); + } + + // src/film/types.ts + var sceneAt = (tl3, id, dt) => (tl3.scenes.find((s) => s.id === id)?.start ?? 0) + dt; + + // src/films/duckling/timeline.json + var timeline_default = { + title: "\u4E11\u5C0F\u9E2D", + style: "ink-wash textbook illustration", + w: 1920, + h: 1080, + fps: 30, + total: 156.3, + frames: 4689, + voice: "zh-CN-XiaoxiaoNeural", + scenes: [ { - id: "marsh", - start: 59.3333, - dur: 13.666666666666666, - voice_start: 59.8333, - voice_dur: 12.264, + id: "title", + start: 0, + dur: 6.5, + voice_start: 0.5, + voice_dur: 2.832, subs: [ { - t0: 59.833, - t1: 61.921, - text: "\u4ED6\u6765\u5230\u4E00\u7247\u6CBC\u6CFD" - }, - { - t0: 61.921, - t1: 64.269, - text: "\u91CE\u9E2D\u4EEC\u7B11\u4ED6\u957F\u5F97\u602A" - }, - { - t0: 64.269, - t1: 65.052, - text: "\u5FFD\u7136" - }, - { - t0: 65.052, - t1: 65.574, - text: "\u7830\uFF01" - }, - { - t0: 65.574, - t1: 66.096, - text: "\u7830\uFF01" - }, - { - t0: 66.096, - t1: 68.183, - text: "\u730E\u4EBA\u7684\u67AA\u58F0\u54CD\u8D77" - }, - { - t0: 68.183, - t1: 70.271, - text: "\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7" + t0: 0.5, + t1: 2.199, + text: "\u5B89\u5F92\u751F\u7AE5\u8BDD" }, { - t0: 70.271, - t1: 72.097, - text: "\u4E00\u52A8\u4E5F\u4E0D\u6562\u52A8" + t0: 2.199, + t1: 3.332, + text: "\u4E11\u5C0F\u9E2D" } ], cue: { - type: "marsh" + type: "title" }, sfx: [ { - at: 3.4, - type: "shot" - }, - { - at: 3.9, - type: "shot" + at: 0.3, + type: "drop" }, { - at: 4.1, - type: "scatter" + at: 4.6, + type: "seal" } ] }, { - id: "autumn", - start: 73, - dur: 15.8, - voice_start: 73.5, - voice_dur: 14.4, + id: "summer", + start: 6.5, + dur: 11.566666666666666, + voice_start: 7, + voice_dur: 10.176, subs: [ { - t0: 73.5, - t1: 74.7, - text: "\u79CB\u5929\u5230\u4E86" - }, - { - t0: 74.7, - t1: 76.38, - text: "\u6811\u53F6\u53D8\u9EC4\u53D8\u7EA2" - }, - { - t0: 76.38, - t1: 77.58, - text: "\u4E00\u5929\u508D\u665A" - }, - { - t0: 77.58, - t1: 80.7, - text: "\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7" + t0: 7, + t1: 8.42, + text: "\u590F\u5929\u7684\u4E61\u4E0B" }, { - t0: 80.7, - t1: 82.14, - text: "\u8116\u5B50\u957F\u957F\u7684" + t0: 8.42, + t1: 9.603, + text: "\u9EA6\u5B50\u91D1\u9EC4" }, { - t0: 82.14, - t1: 83.82, - text: "\u7FC5\u8180\u53C8\u5927\u53C8\u7F8E" + t0: 9.603, + t1: 12.68, + text: "\u6C60\u5858\u8FB9\u7684\u82A6\u82C7\u957F\u5F97\u6BD4\u4EBA\u8FD8\u9AD8" }, { - t0: 83.82, - t1: 85.74, - text: "\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC" + t0: 12.68, + t1: 14.573, + text: "\u9E2D\u5988\u5988\u5C31\u5728\u8FD9\u91CC" }, { - t0: 85.74, - t1: 87.9, - text: "\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80" + t0: 14.573, + t1: 17.176, + text: "\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB" } ], cue: { - type: "autumn" + type: "summer" }, sfx: [ { - at: 2.4, - type: "call" + at: 0.2, + type: "breeze" } ] }, { - id: "winter", - start: 88.8, - dur: 13.433333333333334, - voice_start: 89.3, - voice_dur: 12.024, + id: "hatch", + start: 18.0667, + dur: 11.566666666666666, + voice_start: 18.5667, + voice_dur: 10.176, subs: [ { - t0: 89.3, - t1: 90.502, - text: "\u51AC\u5929\u6765\u4E86" - }, - { - t0: 90.502, - t1: 92.186, - text: "\u96EA\u82B1\u7EB7\u7EB7\u626C\u626C" + t0: 18.567, + t1: 19.26, + text: "\u5494\u5693" }, { - t0: 92.186, - t1: 93.629, - text: "\u6E56\u9762\u7ED3\u4E86\u51B0" + t0: 19.26, + t1: 19.954, + text: "\u5494\u5693\uFF01" }, { - t0: 93.629, - t1: 96.033, - text: "\u4E11\u5C0F\u9E2D\u51BB\u5F97\u7F29\u6210\u4E00\u56E2" + t0: 19.954, + t1: 22.267, + text: "\u86CB\u58F3\u4E00\u4E2A\u63A5\u4E00\u4E2A\u88C2\u5F00" }, { - t0: 96.033, - t1: 96.755, - text: "\u53EF\u662F" + t0: 22.267, + t1: 25.042, + text: "\u6BDB\u8338\u8338\u7684\u5C0F\u9EC4\u9E2D\u94BB\u4E86\u51FA\u6765" }, { - t0: 96.755, - t1: 97.957, - text: "\u4ED6\u54AC\u7740\u7259" + t0: 25.042, + t1: 26.43, + text: "\u5527\u5527\u5730\u53EB\u7740" }, { - t0: 97.957, - t1: 101.324, - text: "\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A" + t0: 26.43, + t1: 28.743, + text: "\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C" } ], cue: { - type: "winter" + type: "hatch" }, sfx: [ { - at: 0.2, - type: "wind" + at: 0.5, + type: "crack" }, { - at: 3, - type: "ice" + at: 1.2, + type: "crack" + }, + { + at: 2.2, + type: "peep" } ] }, { - id: "spring", - start: 102.2333, - dur: 12.366666666666667, - voice_start: 102.7333, - voice_dur: 10.968, + id: "bigegg", + start: 29.6333, + dur: 11.8, + voice_start: 30.1333, + voice_dur: 10.416, subs: [ { - t0: 102.733, - t1: 104.519, - text: "\u6625\u5929\u7EC8\u4E8E\u6765\u4E86\uFF01" - }, - { - t0: 104.519, - t1: 105.794, - text: "\u67F3\u6811\u53D1\u82BD" + t0: 30.133, + t1: 32.217, + text: "\u53EA\u5269\u6700\u5927\u7684\u90A3\u9897\u86CB" }, { - t0: 105.794, - t1: 107.07, - text: "\u6843\u82B1\u76DB\u5F00" + t0: 32.217, + t1: 33.374, + text: "\u8FC7\u4E86\u597D\u4E45" }, { - t0: 107.07, - t1: 109.365, - text: "\u4E11\u5C0F\u9E2D\u6247\u4E86\u6247\u7FC5\u8180" + t0: 33.374, + t1: 34.763, + text: "\u624D\u6162\u6162\u88C2\u5F00" }, { - t0: 109.365, - t1: 109.875, - text: "\u54A6\uFF1F" + t0: 34.763, + t1: 36.614, + text: "\u94BB\u51FA\u6765\u7684\u5C0F\u5BB6\u4F19" }, { - t0: 109.875, - t1: 112.171, - text: "\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B" + t0: 36.614, + t1: 37.772, + text: "\u53C8\u5927\u53C8\u7070" }, { - t0: 112.171, - t1: 113.701, - text: "\u4ED6\u98DE\u8D77\u6765\u4E86\uFF01" + t0: 37.772, + t1: 40.549, + text: "\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0" } ], cue: { - type: "spring" + type: "bigegg" }, sfx: [ { - at: 0.2, - type: "chime" - }, - { - at: 4.8, - type: "rise" + at: 2.6, + type: "bigcrack" } ] }, { - id: "reflection", - start: 114.6, - dur: 13.2, - voice_start: 115.1, - voice_dur: 11.784, + id: "bullied", + start: 41.4333, + dur: 10.366666666666667, + voice_start: 41.9333, + voice_dur: 8.952, subs: [ { - t0: 115.1, - t1: 116.686, - text: "\u4ED6\u843D\u5230\u6E56\u9762\u4E0A" - }, - { - t0: 116.686, - t1: 118.952, - text: "\u51E0\u53EA\u767D\u5929\u9E45\u5411\u4ED6\u6E38\u6765" - }, - { - t0: 118.952, - t1: 120.086, - text: "\u4ED6\u4F4E\u4E0B\u5934" + t0: 41.933, + t1: 44.111, + text: "\u5927\u5BB6\u90FD\u53EB\u4ED6\u4E11\u5C0F\u9E2D" }, { - t0: 120.086, - t1: 121.898, - text: "\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71" + t0: 44.111, + t1: 46.046, + text: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6" }, { - t0: 121.898, - t1: 124.391, - text: "\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50" + t0: 46.046, + t1: 47.74, + text: "\u516C\u9E21\u8FFD\u7740\u5544\u4ED6" }, { - t0: 124.391, - t1: 126.884, - text: "\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\uFF01" + t0: 47.74, + t1: 50.885, + text: "\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B" } ], cue: { - type: "reflection" + type: "bullied" }, sfx: [ { - at: 0.6, - type: "splash" + at: 0.8, + type: "quack" }, { - at: 5.6, - type: "reveal" + at: 3.2, + type: "flap" } ] }, { - id: "accepted", - start: 127.8, - dur: 11.5, - voice_start: 128.3, - voice_dur: 10.104, + id: "leave", + start: 51.8, + dur: 7.533333333333333, + voice_start: 52.3, + voice_dur: 6.144, subs: [ { - t0: 128.3, - t1: 129.945, - text: "\u5929\u9E45\u4EEC\u56F4\u7740\u4ED6" - }, - { - t0: 129.945, - t1: 132.53, - text: "\u7528\u5634\u8F7B\u8F7B\u68B3\u7406\u4ED6\u7684\u7FBD\u6BDB" + t0: 52.3, + t1: 54.348, + text: "\u4E11\u5C0F\u9E2D\u96BE\u8FC7\u6781\u4E86" }, { - t0: 132.53, - t1: 135.114, - text: "\u5CB8\u8FB9\u7684\u5B69\u5B50\u4EEC\u62CD\u7740\u624B\u558A" + t0: 54.348, + t1: 55.116, + text: "\u4E00\u5929" }, { - t0: 135.114, - t1: 135.819, - text: "\u5FEB\u770B" + t0: 55.116, + t1: 57.164, + text: "\u4ED6\u6084\u6084\u94BB\u8FC7\u7BF1\u7B06" }, { - t0: 135.819, - t1: 138.404, - text: "\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\uFF01" + t0: 57.164, + t1: 58.444, + text: "\u79BB\u5F00\u4E86\u5BB6" } ], cue: { - type: "accepted" + type: "leave" }, sfx: [ { - at: 3.6, - type: "cheer" + at: 0.3, + type: "wind" } ] }, { - id: "fly", - start: 139.3, - dur: 7, - voice_start: 139.8, - voice_dur: 5.472, + id: "marsh", + start: 59.3333, + dur: 13.666666666666666, + voice_start: 59.8333, + voice_dur: 12.264, subs: [ { - t0: 139.8, - t1: 141.852, - text: "\u4ED6\u5F20\u5F00\u96EA\u767D\u7684\u7FC5\u8180" + t0: 59.833, + t1: 61.921, + text: "\u4ED6\u6765\u5230\u4E00\u7247\u6CBC\u6CFD" }, { - t0: 141.852, - t1: 143.448, - text: "\u548C\u4F19\u4F34\u4EEC\u4E00\u8D77" + t0: 61.921, + t1: 64.269, + text: "\u91CE\u9E2D\u4EEC\u7B11\u4ED6\u957F\u5F97\u602A" }, { - t0: 143.448, - t1: 145.272, - text: "\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929" + t0: 64.269, + t1: 65.052, + text: "\u5FFD\u7136" + }, + { + t0: 65.052, + t1: 65.574, + text: "\u7830\uFF01" + }, + { + t0: 65.574, + t1: 66.096, + text: "\u7830\uFF01" + }, + { + t0: 66.096, + t1: 68.183, + text: "\u730E\u4EBA\u7684\u67AA\u58F0\u54CD\u8D77" + }, + { + t0: 68.183, + t1: 70.271, + text: "\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7" + }, + { + t0: 70.271, + t1: 72.097, + text: "\u4E00\u52A8\u4E5F\u4E0D\u6562\u52A8" } ], cue: { - type: "fly" + type: "marsh" }, sfx: [ { - at: 0.4, - type: "flap" + at: 3.4, + type: "shot" }, { - at: 1, - type: "rise" + at: 3.9, + type: "shot" + }, + { + at: 4.1, + type: "scatter" } ] }, { - id: "ending", - start: 146.3, - dur: 10, - voice_start: 146.8, - voice_dur: 8.376, + id: "autumn", + start: 73, + dur: 15.8, + voice_start: 73.5, + voice_dur: 14.4, subs: [ { - t0: 146.8, - t1: 148.429, - text: "\u53EA\u8981\u5FC3\u91CC\u6709\u68A6" + t0: 73.5, + t1: 74.7, + text: "\u79CB\u5929\u5230\u4E86" }, { - t0: 148.429, - t1: 149.592, - text: "\u4E0D\u6015\u98CE\u96EA" + t0: 74.7, + t1: 76.38, + text: "\u6811\u53F6\u53D8\u9EC4\u53D8\u7EA2" }, { - t0: 149.592, - t1: 150.988, - text: "\u4E5F\u4E0D\u6015\u5632\u7B11" + t0: 76.38, + t1: 77.58, + text: "\u4E00\u5929\u508D\u665A" }, { - t0: 150.988, - t1: 152.151, - text: "\u603B\u6709\u4E00\u5929" + t0: 77.58, + t1: 80.7, + text: "\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7" }, { - t0: 152.151, - t1: 155.176, - text: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180" + t0: 80.7, + t1: 82.14, + text: "\u8116\u5B50\u957F\u957F\u7684" + }, + { + t0: 82.14, + t1: 83.82, + text: "\u7FC5\u8180\u53C8\u5927\u53C8\u7F8E" + }, + { + t0: 83.82, + t1: 85.74, + text: "\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC" + }, + { + t0: 85.74, + t1: 87.9, + text: "\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80" } ], cue: { - type: "ending" + type: "autumn" }, sfx: [ { - at: 6.2, - type: "seal" + at: 2.4, + type: "call" } ] - } - ] - }; - - // src/film/core.ts - var core_exports = {}; - __export(core_exports, { - FONT_BRUSH: () => FONT_BRUSH, - FONT_KAI: () => FONT_KAI, - INK: () => INK, - PAPER: () => PAPER, - SEAL: () => SEAL, - backOut: () => backOut, - blobLoop: () => blobLoop, - blot: () => blot, - brush: () => brush, - clamp: () => clamp, - ctx2d: () => ctx2d, - easeIn: () => easeIn, - easeInOut: () => easeInOut, - easeOut: () => easeOut, - hash: () => hash, - inv: () => inv, - lerp: () => lerp, - loadFonts: () => loadFonts, - makeCanvas: () => makeCanvas, - mixHex: () => mixHex, - mountains: () => mountains, - noise1: () => noise1, - nz: () => nz, - outline: () => outline, - paper: () => paper, - rgba: () => rgba, - rng: () => rng, - seal: () => seal, - smooth: () => smooth, - spline: () => spline, - spring: () => spring, - stroke: () => stroke, - subPath: () => subPath, - tracePath: () => tracePath, - wash: () => wash - }); - function rng(seed) { - let a = seed >>> 0 || 1; - return () => { - a = a + 1831565813 >>> 0; - let t = a; - t = Math.imul(t ^ t >>> 15, t | 1); - t ^= t + Math.imul(t ^ t >>> 7, t | 61); - return ((t ^ t >>> 14) >>> 0) / 4294967296; - }; - } - function hash(n) { - const s = Math.sin(n * 127.1 + 311.7) * 43758.5453; - return s - Math.floor(s); - } - function noise1(x, seed = 0) { - const i = Math.floor(x), f = x - i; - const a = hash(i + seed * 101.3), b = hash(i + 1 + seed * 101.3); - const u = f * f * (3 - 2 * f); - return a + (b - a) * u; - } - var nz = (x, seed = 0) => noise1(x, seed) * 2 - 1; - var clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x)); - var lerp = (a, b, t) => a + (b - a) * t; - var inv = (a, b, x) => clamp((x - a) / (b - a)); - var smooth = (x) => { - x = clamp(x); - return x * x * (3 - 2 * x); - }; - var easeOut = (x) => 1 - Math.pow(1 - clamp(x), 3); - var easeIn = (x) => Math.pow(clamp(x), 3); - var easeInOut = (x) => { - x = clamp(x); - return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; - }; - var backOut = (x) => { - x = clamp(x); - const c1 = 1.70158, c3 = c1 + 1; - return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2); - }; - var spring = (x) => { - x = Math.max(0, x); - return 1 - Math.exp(-6 * x) * Math.cos(11 * x); - }; - var INK = "#1d1a17"; - var PAPER = "#f6efdf"; - var SEAL = "#b8322a"; - function rgba(hex, a) { - const n = parseInt(hex.slice(1), 16); - return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`; - } - function mixHex(a, b, t) { - const x = parseInt(a.slice(1), 16), y = parseInt(b.slice(1), 16); - const c2 = (s) => Math.round(lerp(x >> s & 255, y >> s & 255, clamp(t))); - return "#" + (1 << 24 | c2(16) << 16 | c2(8) << 8 | c2(0)).toString(16).slice(1); - } - function makeCanvas(w, h) { - if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(w, h); - const c2 = document.createElement("canvas"); - c2.width = w; - c2.height = h; - return c2; - } - function ctx2d(c2) { - return c2.getContext("2d"); - } - var paperCache = /* @__PURE__ */ new Map(); - function paper(W2, H2) { - const key = W2 + "x" + H2; - const hit = paperCache.get(key); - if (hit) return hit; - const c2 = makeCanvas(W2, H2), g2 = ctx2d(c2), r = rng(20261017); - g2.fillStyle = PAPER; - g2.fillRect(0, 0, W2, H2); - const u = Math.min(W2, H2) / 1080; - for (let i = 0; i < 46; i++) { - const x = r() * W2, y = r() * H2, rad = (120 + r() * 420) * u; - const gr = g2.createRadialGradient(x, y, 0, x, y, rad); - const dark = r() < 0.55; - gr.addColorStop(0, dark ? "rgba(170,140,90,0.028)" : "rgba(255,252,240,0.08)"); - gr.addColorStop(1, "rgba(0,0,0,0)"); - g2.fillStyle = gr; - g2.fillRect(x - rad, y - rad, rad * 2, rad * 2); - } - g2.strokeStyle = "rgba(150,125,85,0.035)"; - g2.lineWidth = Math.max(1, u); - for (let y = 0; y < H2; y += 9 * u) { - g2.beginPath(); - g2.moveTo(0, y + r() * 2); - g2.lineTo(W2, y + r() * 2); - g2.stroke(); - } - const nf = Math.round(W2 * H2 / 2600); - for (let i = 0; i < nf; i++) { - const x = r() * W2, y = r() * H2, len = (6 + r() * 26) * u, ang = r() * Math.PI * 2; - g2.strokeStyle = r() < 0.7 ? `rgba(150,120,75,${0.05 + r() * 0.08})` : `rgba(255,252,240,${0.2 + r() * 0.25})`; - g2.lineWidth = (0.4 + r() * 0.9) * u; - g2.beginPath(); - g2.moveTo(x, y); - g2.quadraticCurveTo(x + Math.cos(ang + 0.6) * len * 0.5, y + Math.sin(ang + 0.6) * len * 0.5, x + Math.cos(ang) * len, y + Math.sin(ang) * len); - g2.stroke(); - } - try { - const id = g2.getImageData(0, 0, W2, H2), d = id.data; - let s = 12345; - for (let i = 0; i < d.length; i += 4) { - s = s * 1103515245 + 12345 & 2147483647; - const n = (s >> 8) % 15 - 7; - d[i] += n; - d[i + 1] += n; - d[i + 2] += n; - } - g2.putImageData(id, 0, 0); - } catch { - } - const vg = g2.createRadialGradient(W2 / 2, H2 / 2, Math.min(W2, H2) * 0.35, W2 / 2, H2 / 2, Math.hypot(W2, H2) * 0.62); - vg.addColorStop(0, "rgba(0,0,0,0)"); - vg.addColorStop(1, "rgba(120,90,50,0.13)"); - g2.fillStyle = vg; - g2.fillRect(0, 0, W2, H2); - paperCache.set(key, c2); - if (paperCache.size > 4) paperCache.delete(paperCache.keys().next().value); - return c2; - } - function spline(pts, seg = 8, closed = false) { - const out = []; - const n = pts.length; - if (n < 3) return pts.slice(); - const get = (i) => closed ? pts[(i + n) % n] : pts[Math.max(0, Math.min(n - 1, i))]; - const last = closed ? n : n - 1; - for (let i = 0; i < last; i++) { - const p0 = get(i - 1), p1 = get(i), p2 = get(i + 1), p3 = get(i + 2); - for (let j = 0; j < seg; j++) { - const t = j / seg, t2 = t * t, t3 = t2 * t; - out.push([ - 0.5 * (2 * p1[0] + (-p0[0] + p2[0]) * t + (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 + (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3), - 0.5 * (2 * p1[1] + (-p0[1] + p2[1]) * t + (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 + (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3) - ]); - } - } - if (!closed) out.push(pts[n - 1]); - return out; - } - function subPath(pts, a, b) { - if (pts.length < 2) return pts; - const L = [0]; - for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); - const tot = L[L.length - 1], A = a * tot, B = b * tot, out = []; - const at2 = (d) => { - for (let i = 1; i < L.length; i++) if (L[i] >= d) { - const f = (d - L[i - 1]) / Math.max(1e-6, L[i] - L[i - 1]); - return [lerp(pts[i - 1][0], pts[i][0], f), lerp(pts[i - 1][1], pts[i][1], f)]; - } - return pts[pts.length - 1]; - }; - out.push(at2(A)); - for (let i = 0; i < pts.length; i++) if (L[i] > A && L[i] < B) out.push(pts[i]); - out.push(at2(B)); - return out; - } - function tracePath(g2, pts, close = false) { - g2.beginPath(); - g2.moveTo(pts[0][0], pts[0][1]); - for (let i = 1; i < pts.length; i++) g2.lineTo(pts[i][0], pts[i][1]); - if (close) g2.closePath(); - } - function brush(g2, pts0, w, o = {}) { - const grow = o.grow ?? 1; - if (grow <= 0 || pts0.length < 2) return; - const pts = grow < 1 ? subPath(pts0, 0, grow) : pts0; - const n = pts.length; - const L = [0]; - for (let i = 1; i < n; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); - const tot = Math.max(1e-6, L[n - 1]); - const tin = o.taperIn ?? 0.18, tout = o.taperOut ?? 0.35, jit = o.jitter ?? 0.18, seed = o.seed ?? 1; - const left = [], right = []; - for (let i = 0; i < n; i++) { - const s = L[i] / tot; - const a = pts[Math.max(0, i - 1)], b = pts[Math.min(n - 1, i + 1)]; - let dx = b[0] - a[0], dy = b[1] - a[1]; - const d = Math.hypot(dx, dy) || 1; - dx /= d; - dy /= d; - let prof = Math.min(1, 0.35 + 0.65 * smooth(s / tin)) * Math.min(1, Math.pow(clamp((1 - s) / tout), 0.7) * 0.92 + 0.08); - if (o.press) prof *= 1 + o.press * Math.exp(-s * 9); - const ww = w / 2 * prof * (1 + jit * nz(s * 7 + seed * 3.1, seed)); - left.push([pts[i][0] - dy * ww, pts[i][1] + dx * ww]); - right.push([pts[i][0] + dy * ww, pts[i][1] - dx * ww]); - } - g2.fillStyle = rgba(o.color ?? INK, o.alpha ?? 0.92); - g2.beginPath(); - g2.moveTo(left[0][0], left[0][1]); - for (let i = 1; i < n; i++) g2.lineTo(left[i][0], left[i][1]); - for (let i = n - 1; i >= 0; i--) g2.lineTo(right[i][0], right[i][1]); - g2.closePath(); - g2.fill(); - if ((o.alpha ?? 0.92) > 0.5 && w > 3) { - g2.fillStyle = rgba(o.color ?? INK, 0.12); - g2.beginPath(); - g2.arc(pts[0][0], pts[0][1], w / 2 * 0.55, 0, Math.PI * 2); - g2.fill(); - } - if (o.dry && o.dry > 0) { - g2.strokeStyle = rgba(PAPER, 0.75 * o.dry); - const lines = 4; - for (let k = 0; k < lines; k++) { - const off = (k + 0.5) / lines - 0.5; - g2.lineWidth = Math.max(0.6, w * 0.07); - g2.beginPath(); - let started = false; - for (let i = Math.floor(n * (0.45 + 0.1 * hash(seed + k))); i < n; i++) { - const sx = lerp(left[i][0], right[i][0], 0.5 + off * 0.8), sy = lerp(left[i][1], right[i][1], 0.5 + off * 0.8); - if (hash(i * 0.37 + k * 13 + seed) < 0.18) { - started = false; - continue; + }, + { + id: "winter", + start: 88.8, + dur: 13.433333333333334, + voice_start: 89.3, + voice_dur: 12.024, + subs: [ + { + t0: 89.3, + t1: 90.502, + text: "\u51AC\u5929\u6765\u4E86" + }, + { + t0: 90.502, + t1: 92.186, + text: "\u96EA\u82B1\u7EB7\u7EB7\u626C\u626C" + }, + { + t0: 92.186, + t1: 93.629, + text: "\u6E56\u9762\u7ED3\u4E86\u51B0" + }, + { + t0: 93.629, + t1: 96.033, + text: "\u4E11\u5C0F\u9E2D\u51BB\u5F97\u7F29\u6210\u4E00\u56E2" + }, + { + t0: 96.033, + t1: 96.755, + text: "\u53EF\u662F" + }, + { + t0: 96.755, + t1: 97.957, + text: "\u4ED6\u54AC\u7740\u7259" + }, + { + t0: 97.957, + t1: 101.324, + text: "\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A" + } + ], + cue: { + type: "winter" + }, + sfx: [ + { + at: 0.2, + type: "wind" + }, + { + at: 3, + type: "ice" + } + ] + }, + { + id: "spring", + start: 102.2333, + dur: 12.366666666666667, + voice_start: 102.7333, + voice_dur: 10.968, + subs: [ + { + t0: 102.733, + t1: 104.519, + text: "\u6625\u5929\u7EC8\u4E8E\u6765\u4E86\uFF01" + }, + { + t0: 104.519, + t1: 105.794, + text: "\u67F3\u6811\u53D1\u82BD" + }, + { + t0: 105.794, + t1: 107.07, + text: "\u6843\u82B1\u76DB\u5F00" + }, + { + t0: 107.07, + t1: 109.365, + text: "\u4E11\u5C0F\u9E2D\u6247\u4E86\u6247\u7FC5\u8180" + }, + { + t0: 109.365, + t1: 109.875, + text: "\u54A6\uFF1F" + }, + { + t0: 109.875, + t1: 112.171, + text: "\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B" + }, + { + t0: 112.171, + t1: 113.701, + text: "\u4ED6\u98DE\u8D77\u6765\u4E86\uFF01" + } + ], + cue: { + type: "spring" + }, + sfx: [ + { + at: 0.2, + type: "chime" + }, + { + at: 4.8, + type: "rise" + } + ] + }, + { + id: "reflection", + start: 114.6, + dur: 13.2, + voice_start: 115.1, + voice_dur: 11.784, + subs: [ + { + t0: 115.1, + t1: 116.686, + text: "\u4ED6\u843D\u5230\u6E56\u9762\u4E0A" + }, + { + t0: 116.686, + t1: 118.952, + text: "\u51E0\u53EA\u767D\u5929\u9E45\u5411\u4ED6\u6E38\u6765" + }, + { + t0: 118.952, + t1: 120.086, + text: "\u4ED6\u4F4E\u4E0B\u5934" + }, + { + t0: 120.086, + t1: 121.898, + text: "\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71" + }, + { + t0: 121.898, + t1: 124.391, + text: "\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50" + }, + { + t0: 124.391, + t1: 126.884, + text: "\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\uFF01" + } + ], + cue: { + type: "reflection" + }, + sfx: [ + { + at: 0.6, + type: "splash" + }, + { + at: 5.6, + type: "reveal" + } + ] + }, + { + id: "accepted", + start: 127.8, + dur: 11.5, + voice_start: 128.3, + voice_dur: 10.104, + subs: [ + { + t0: 128.3, + t1: 129.945, + text: "\u5929\u9E45\u4EEC\u56F4\u7740\u4ED6" + }, + { + t0: 129.945, + t1: 132.53, + text: "\u7528\u5634\u8F7B\u8F7B\u68B3\u7406\u4ED6\u7684\u7FBD\u6BDB" + }, + { + t0: 132.53, + t1: 135.114, + text: "\u5CB8\u8FB9\u7684\u5B69\u5B50\u4EEC\u62CD\u7740\u624B\u558A" + }, + { + t0: 135.114, + t1: 135.819, + text: "\u5FEB\u770B" + }, + { + t0: 135.819, + t1: 138.404, + text: "\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\uFF01" + } + ], + cue: { + type: "accepted" + }, + sfx: [ + { + at: 3.6, + type: "cheer" + } + ] + }, + { + id: "fly", + start: 139.3, + dur: 7, + voice_start: 139.8, + voice_dur: 5.472, + subs: [ + { + t0: 139.8, + t1: 141.852, + text: "\u4ED6\u5F20\u5F00\u96EA\u767D\u7684\u7FC5\u8180" + }, + { + t0: 141.852, + t1: 143.448, + text: "\u548C\u4F19\u4F34\u4EEC\u4E00\u8D77" + }, + { + t0: 143.448, + t1: 145.272, + text: "\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929" + } + ], + cue: { + type: "fly" + }, + sfx: [ + { + at: 0.4, + type: "flap" + }, + { + at: 1, + type: "rise" + } + ] + }, + { + id: "ending", + start: 146.3, + dur: 10, + voice_start: 146.8, + voice_dur: 8.376, + subs: [ + { + t0: 146.8, + t1: 148.429, + text: "\u53EA\u8981\u5FC3\u91CC\u6709\u68A6" + }, + { + t0: 148.429, + t1: 149.592, + text: "\u4E0D\u6015\u98CE\u96EA" + }, + { + t0: 149.592, + t1: 150.988, + text: "\u4E5F\u4E0D\u6015\u5632\u7B11" + }, + { + t0: 150.988, + t1: 152.151, + text: "\u603B\u6709\u4E00\u5929" + }, + { + t0: 152.151, + t1: 155.176, + text: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180" } - if (!started) { - g2.moveTo(sx, sy); - started = true; - } else g2.lineTo(sx, sy); - } - g2.stroke(); - } - } - } - function stroke(g2, ctrl, w, o = {}) { - brush(g2, spline(ctrl, 10), w, o); - } - function outline(g2, loop, w, o = {}) { - const pts = spline(loop, 8, true); - pts.push(pts[0]); - const br = o.breaks ?? [0, 0.36, 0.7, 1]; - for (let i = 0; i < br.length - 1; i++) { - const a = br[i] + 0.012, b = br[i + 1] - 0.012; - if (b > a) brush(g2, subPath(pts, a, b), w, { taperIn: 0.12, taperOut: 0.25, ...o, seed: (o.seed ?? 1) + i * 7 }); - } - } - function wash(g2, loop, color, alpha = 0.6, o = {}) { - const pts = spline(loop, 8, o.closed ?? true); - g2.save(); - g2.translate(o.dx ?? 0, o.dy ?? 0); - tracePath(g2, pts, true); - g2.fillStyle = rgba(color, alpha); - g2.fill(); - if ((o.edge ?? 1) > 0) { - g2.strokeStyle = rgba(color, Math.min(1, alpha * 0.5 * (o.edge ?? 1))); - g2.lineWidth = o.lw ?? 2.2; - g2.lineJoin = "round"; - g2.stroke(); - } - g2.restore(); - } - function blot(g2, x, y, r, color, a, sx = 1, sy = 1) { - if (r <= 0 || a <= 0) return; - g2.save(); - g2.translate(x, y); - g2.scale(sx, sy); - const gr = g2.createRadialGradient(0, 0, 0, 0, 0, r); - gr.addColorStop(0, rgba(color, a)); - gr.addColorStop(0.55, rgba(color, a * 0.6)); - gr.addColorStop(1, rgba(color, 0)); - g2.fillStyle = gr; - g2.fillRect(-r, -r, r * 2, r * 2); - g2.restore(); - } - function blobLoop(cx, cy, r, seed, rough = 0.18, n = 28) { - const out = []; - for (let i = 0; i < n; i++) { - const a = i / n * Math.PI * 2; - const rr = r * (1 + rough * nz(i * 0.7 + seed, seed) + rough * 0.5 * nz(i * 2.3 + seed * 3, seed + 1)); - out.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]); - } - return out; - } - function mountains(g2, W2, baseY, height, color, alpha, seed, o = {}) { - const f = o.freq ?? 2.2; - const pts = []; - const steps = 60; - for (let i = 0; i <= steps; i++) { - const x = i / steps * W2; - const v = 0.55 * noise1(i / steps * f + seed, seed) + 0.3 * noise1(i / steps * f * 2.7 + seed * 2, seed + 1) + 0.15 * noise1(i / steps * f * 7 + seed * 5, seed + 2); - pts.push([x, baseY - height * Math.pow(v, 1.4) * 1.6]); - } - const top = Math.min(...pts.map((p) => p[1])); - const gr = g2.createLinearGradient(0, top, 0, baseY); - gr.addColorStop(0, rgba(color, alpha)); - gr.addColorStop(0.6, rgba(color, alpha * 0.55)); - gr.addColorStop(1, rgba(color, 0)); - g2.fillStyle = gr; - g2.beginPath(); - g2.moveTo(0, baseY + 2); - for (const p of pts) g2.lineTo(p[0], p[1]); - g2.lineTo(W2, baseY + 2); - g2.closePath(); - g2.fill(); - if ((o.soft ?? 0) < 1) { - const r = rng(seed * 991); - for (let k = 0; k < 6; k++) { - const a = r() * 0.85, b = a + 0.06 + r() * 0.12; - brush(g2, subPath(pts, a, Math.min(1, b)), 2.5 + r() * 3, { color, alpha: alpha * 0.9, seed: seed + k, jitter: 0.4, dry: 0.4 }); - } - } - } - function seal(g2, x, y, size, text, o = {}) { - const a = o.alpha ?? 1; - if (a <= 0) return; - g2.save(); - g2.translate(x, y); - const s = size; - g2.globalAlpha = a; - const loop = [[-s / 2, -s / 2], [s / 2, -s / 2], [s / 2, s / 2], [-s / 2, s / 2]]; - g2.fillStyle = SEAL; - g2.beginPath(); - if (o.round) g2.arc(0, 0, s / 2, 0, Math.PI * 2); - else { - g2.moveTo(loop[0][0], loop[0][1]); - for (const p of loop.slice(1)) g2.lineTo(p[0], p[1]); - g2.closePath(); - } - g2.fill(); - g2.fillStyle = PAPER; - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const cols = text.length; - const rows = Math.max(...text.map((c2) => c2.length)); - const cell = s * 0.84 / Math.max(cols, rows); - g2.font = `${cell * 0.95}px ${o.font ?? FONT_BRUSH}`; - for (let c2 = 0; c2 < cols; c2++) { - const col = text[c2]; - for (let r2 = 0; r2 < col.length; r2++) { - const cx = (cols - 1) / 2 * cell - c2 * cell; - const cy = (r2 - (col.length - 1) / 2) * cell * (rows / col.length); - g2.fillText(col[r2], cx, cy + cell * 0.04); + ], + cue: { + type: "ending" + }, + sfx: [ + { + at: 6.2, + type: "seal" + } + ] } - } - const r = rng(Math.round(x * 7 + y * 13)); - g2.fillStyle = PAPER; - for (let i = 0; i < 26; i++) { - g2.globalAlpha = a * (0.25 + r() * 0.5); - g2.beginPath(); - g2.arc((r() - 0.5) * s, (r() - 0.5) * s, r() * s * 0.025 + 0.5, 0, Math.PI * 2); - g2.fill(); - } - g2.restore(); - } - var FONT_KAI = '"WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; - var FONT_BRUSH = '"MaShanZhengFilm", "WenKaiFilm", "Kaiti SC", "STKaiti", "KaiTi", serif'; - var fontsPromise = null; - function loadFonts(base) { - if (fontsPromise) return fontsPromise; - if (typeof FontFace === "undefined") return fontsPromise = Promise.resolve(); - const list = [["WenKaiFilm", base + "film/fonts/wenkai.woff2"], ["MaShanZhengFilm", base + "film/fonts/mashanzheng.woff2"]]; - fontsPromise = Promise.all(list.map(async ([fam, url]) => { - try { - const f = new FontFace(fam, `url(${url})`); - await f.load(); - document.fonts.add(f); - } catch (e) { - console.warn("font load failed", fam, e); + ] + }; + + // src/films/duckling/live.json + var live_default = [ + { id: "01-egg", scene: "bigegg", from: 2.2, dur: 3, cover: 2.4, caption: "\u6700\u5927\u7684\u86CB", index: "\u5176\u4E00", xhs: "\u6700\u540E\u4E00\u9897\u86CB\u6700\u5927\uFF0C\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u6765\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19\u3002" }, + { id: "02-laugh", scene: "bullied", from: 3, dur: 3, cover: 1, caption: "\u4E11\u5C0F\u9E2D", index: "\u5176\u4E8C", xhs: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6\uFF0C\u5927\u5BB6\u90FD\u53EB\u4ED6\u300C\u4E11\u5C0F\u9E2D\u300D\u3002" }, + { id: "03-swans", scene: "autumn", from: 4.6, dur: 3, cover: 1.6, caption: "\u4EF0\u671B", index: "\u5176\u4E09", xhs: "\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u98DE\u8FC7\uFF0C\u4ED6\u671B\u7740\u5929\u7A7A\uFF0C\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80\u3002" }, + { id: "04-snow", scene: "winter", from: 6.4, dur: 3, cover: 1.6, caption: "\u5BD2\u51AC", index: "\u5176\u56DB", xhs: "\u96EA\u591C\u91CC\u6E56\u9762\u7ED3\u4E86\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\uFF0C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u3002" }, + { id: "05-reflect", scene: "reflection", from: 5.4, dur: 3, cover: 2.4, caption: "\u5012\u5F71", index: "\u5176\u4E94", xhs: "\u4ED6\u4F4E\u4E0B\u5934\uFF0C\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71\u2014\u2014\u90A3\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u3002" }, + { id: "06-fly", scene: "fly", from: 2.6, dur: 3, cover: 1.6, caption: "\u5C55\u7FC5", index: "\u5176\u516D", xhs: "\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002\u5F20\u5F00\u7FC5\u8180\uFF0C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u3002" } + ]; + + // src/films/duckling/qa.json + var qa_default = { + status: "done", + generated: "2026-10-17", + meta: { + w: 1080, + h: 1920, + fps: 30, + dur: 156.3, + audio: true + }, + verdict: "PASS", + results: [ + { + level: "PASS", + check: "\u7AD6\u5C4F \u5206\u8FA8\u7387", + msg: "1080\xD71920\uFF08\u65F6\u95F4\u8F74 1080\xD71920\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u5E27\u7387", + msg: "30.0 fps" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u65F6\u957F", + msg: "156.30s\uFF08\u65F6\u95F4\u8F74 156.30s\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u97F3\u8F68", + msg: "\u6709" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u54CD\u5EA6", + msg: "-14.0 LUFS\uFF08\u76EE\u6807 \u221214\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u771F\u5CF0\u503C", + msg: "-1.4 dBTP\uFF08\u5E94 \u2264 \u22121\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u9ED1\u573A", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u51BB\u7ED3\u753B\u9762", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@title", + msg: "OCR \u8BFB\u51FA 16 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@summer", + msg: "OCR \u8BFB\u51FA 37 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 88%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@hatch", + msg: "OCR \u8BFB\u51FA 31 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@bigegg", + msg: "OCR \u8BFB\u51FA 33 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@bullied", + msg: "OCR \u8BFB\u51FA 35 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@leave", + msg: "OCR \u8BFB\u51FA 21 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@marsh", + msg: "OCR \u8BFB\u51FA 14 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 86%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@autumn", + msg: "OCR \u8BFB\u51FA 20 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@winter", + msg: "OCR \u8BFB\u51FA 40 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 91%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@spring", + msg: "OCR \u8BFB\u51FA 22 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "INFO", + check: "\u7AD6\u5C4F \u6587\u5B57@reflection", + msg: "OCR \u8BFB\u51FA 47 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 53%\uFF08\u68C0\u67E5\u7A97\u53E3\u540C\u65F6\u8BA1\u5165\u4E86\u4E0A\u4E00\u53E5\u5B57\u5E55\uFF1B\u5F53\u524D\u5B57\u5E55\u300C\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u300D\u5DF2\u88AB\u8BFB\u51FA\uFF09" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@accepted", + msg: "OCR \u8BFB\u51FA 33 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@fly", + msg: "OCR \u8BFB\u51FA 11 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u7AD6\u5C4F \u6587\u5B57@ending", + msg: "OCR \u8BFB\u51FA 28 \u5B57\uFF1B\u5B57\u5E55\u547D\u4E2D 100%" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5206\u8FA8\u7387", + msg: "1920\xD71080\uFF08\u65F6\u95F4\u8F74 1920\xD71080\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5E27\u7387", + msg: "30.0 fps" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u65F6\u957F", + msg: "156.30s\uFF08\u65F6\u95F4\u8F74 156.30s\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u97F3\u8F68", + msg: "\u6709" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u54CD\u5EA6", + msg: "-14.0 LUFS\uFF08\u76EE\u6807 \u221214\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u771F\u5CF0\u503C", + msg: "-1.4 dBTP\uFF08\u5E94 \u2264 \u22121\uFF09" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u9ED1\u573A", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u51BB\u7ED3\u753B\u9762", + msg: "\u65E0" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@summer", + msg: "\u300C\u5B89\u5B89\u9759\u9759\u5730\u5B75\u7740\u5979\u7684\u86CB\u300DOCR \u547D\u4E2D 88%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.5:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@hatch", + msg: "\u300C\u597D\u5947\u5730\u770B\u7740\u8FD9\u4E2A\u4E16\u754C\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.4:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@bigegg", + msg: "\u300C\u4E00\u70B9\u4E5F\u4E0D\u50CF\u4ED6\u7684\u54E5\u54E5\u59D0\u59D0\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.7:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@bullied", + msg: "\u300C\u8FDE\u5582\u9E2D\u7684\u5C0F\u59D1\u5A18\u4E5F\u5ACC\u4ED6\u788D\u4E8B\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 13.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@leave", + msg: "\u300C\u79BB\u5F00\u4E86\u5BB6\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 3.5:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@marsh", + msg: "\u300C\u4ED6\u5413\u5F97\u8EB2\u8FDB\u82A6\u82C7\u300DOCR \u547D\u4E2D 0%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 11.6:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@autumn", + msg: "\u300C\u4E11\u5C0F\u9E2D\u671B\u7740\u4ED6\u4EEC\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 11.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@winter", + msg: "\u300C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u300DOCR \u547D\u4E2D 91%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.9:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@spring", + msg: "\u300C\u7FC5\u8180\u53D8\u5F97\u8FD9\u4E48\u6709\u529B\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@reflection", + msg: "\u300C\u90A3\u4E0D\u662F\u53C8\u4E11\u53C8\u7070\u7684\u9E2D\u5B50\u800C\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u300DOCR \u547D\u4E2D 53%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.1:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@accepted", + msg: "\u300C\u65B0\u6765\u7684\u90A3\u53EA\u5929\u9E45\u6700\u6F02\u4EAE\u300DOCR \u547D\u4E2D 90%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 12.9:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@fly", + msg: "\u300C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 10.2:1" + }, + { + level: "PASS", + check: "\u6A2A\u5C4F \u5B57\u5E55@ending", + msg: "\u300C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u300DOCR \u547D\u4E2D 100%\uFF1B\u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6 13.1:1" + }, + { + level: "INFO", + check: "\u8BF4\u660E", + msg: "\u6A2A\u5C4F\u5B57\u5E55\u4F4D\u4E8E\u753B\u9762\u5E95\u90E8 ~92%\uFF0Cvibe qa \u7684 OCR \u53EA\u626B 60\u201384% \u9AD8\u5EA6\u5E26\uFF0C\u6545\u6A2A\u5C4F\u6539\u7528\u300C\u5B57\u5E55\u5E26 OCR + \u58A8\u8272/\u7EB8\u5E26\u5BF9\u6BD4\u5EA6\uFF08\u22657:1\uFF09\u300D\u8865\u5145\u68C0\u67E5\u3002" + }, + { + level: "PASS", + check: "\u786E\u5B9A\u6027", + msg: "vibe det\uFF1A\u540C\u4E00\u5E27\u4E71\u5E8F\u91CD\u6E32\u9010\u50CF\u7D20\u4E00\u81F4" + }, + { + level: "PASS", + check: "\u5B9E\u51B5\u56FE", + msg: "6 \u7EC4 JPG+MOV\uFF1Acontent identifier \u4E00\u81F4\u3001still-image-time \u8BFB\u56DE\u6B63\u786E\u3001MOV \u5B8C\u6574\u89E3\u7801\u3001\u54C8\u5E0C\u6821\u9A8C\u901A\u8FC7\uFF08PhotoKit \u672C\u673A\u52A0\u8F7D\uFF1ALinux \u73AF\u5883\u4E0D\u53EF\u7528\uFF09" } - })).then(() => void 0); - return fontsPromise; - } + ] + }; // src/film/actors.ts var actors_exports = {}; @@ -1198,13 +1608,13 @@ const sgn = k === 0 ? 1 : -1; const swing = walk ? Math.sin(ph + (k ? Math.PI : 0)) * 0.35 : 0; const hip = [0.02 + 0.08 * sgn, bodyRy - 0.04]; - const foot = [hip[0] + Math.sin(swing) * legLen, hip[1] + Math.cos(swing) * legLen + bob - (walk && Math.sin(ph + (k ? Math.PI : 0)) > 0 ? 0.03 : 0)]; - brush(g2, [hip, lp(hip, foot, 0.5), foot], iw * 1.3, { color: "#c8731f", alpha: 0.95, taperOut: 0.1, seed: seed + k }); + const foot2 = [hip[0] + Math.sin(swing) * legLen, hip[1] + Math.cos(swing) * legLen + bob - (walk && Math.sin(ph + (k ? Math.PI : 0)) > 0 ? 0.03 : 0)]; + brush(g2, [hip, lp(hip, foot2, 0.5), foot2], iw * 1.3, { color: "#c8731f", alpha: 0.95, taperOut: 0.1, seed: seed + k }); g2.fillStyle = rgba("#e08a2c", 0.9); g2.beginPath(); - g2.moveTo(foot[0] - 0.04, foot[1]); - g2.lineTo(foot[0] + 0.11, foot[1] + 0.012); - g2.lineTo(foot[0] + 0.02, foot[1] - 0.035); + g2.moveTo(foot2[0] - 0.04, foot2[1]); + g2.lineTo(foot2[0] + 0.11, foot2[1] + 0.012); + g2.lineTo(foot2[0] + 0.02, foot2[1] - 0.035); g2.closePath(); g2.fill(); } @@ -1756,10 +2166,10 @@ g2.arc(x, y, r * 0.18, 0, Math.PI * 2); g2.fill(); } - function leaf(g2, x, y, s, rot, color, alpha = 0.85) { + function leaf(g2, x, y, s, rot2, color, alpha = 0.85) { g2.save(); g2.translate(x, y); - g2.rotate(rot); + g2.rotate(rot2); g2.fillStyle = rgba(color, alpha); g2.beginPath(); g2.moveTo(-s, 0); @@ -1796,7 +2206,7 @@ g2.stroke(); } - // src/film/cues.ts + // src/films/duckling/cues.ts var CUE = { title: { drop: 0.32, write: 1.1, seal: 4.55 }, hatch: { first: 0.4, gap: 0.75 }, @@ -1812,7 +2222,7 @@ ending: { end: 8.5 } }; - // src/film/scenes.ts + // src/films/duckling/scenes.ts var UGLY = "#8d8a84"; var UGLY_BEAK = "#9c7f5c"; var DUCKY = "#ecc75a"; @@ -1942,1735 +2352,4911 @@ const [nx, ny] = nestPos(s); blot(g2, nx, ny + 30 * u, 380 * u, "#7a8a50", 0.18, 1.3, 0.35); } - var SCENES = { + var SCENES2 = { title: { label: "\u7247\u5934", draw(s) { - const { g: g2, W: W2, H: H2, u, t, portrait } = s; - const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.43; - if (t < 0.32) { - const y = lerp(-30 * u, cy, easeIn(t / 0.32)); - g2.fillStyle = rgba(INK, 0.95); - g2.beginPath(); - g2.ellipse(cx, y, 9 * u, 15 * u, 0, 0, Math.PI * 2); - g2.fill(); + const { g: g2, W: W2, H: H2, u, t, portrait } = s; + const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.43; + if (t < 0.32) { + const y = lerp(-30 * u, cy, easeIn(t / 0.32)); + g2.fillStyle = rgba(INK, 0.95); + g2.beginPath(); + g2.ellipse(cx, y, 9 * u, 15 * u, 0, 0, Math.PI * 2); + g2.fill(); + } + const p = easeOut(inv(0.3, 2.4, t)); + if (p > 0) { + const R = (portrait ? 420 : 350) * u * p; + wash(g2, blobLoop(cx, cy, R, 5, 0.05, 32), "#a39c90", 0.14 + 0.18 * (1 - p), { edge: 1.4 }); + blot(g2, cx, cy, R * 0.85, "#8a8276", 0.12 * p); + particles(10, 77, (r, i) => { + const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(0.3, 0.8, t)); + const rr = (3 + r() * 8) * u; + g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + g2.beginPath(); + g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, rr, 0, Math.PI * 2); + g2.fill(); + void i; + }); + } + const a = smooth(inv(0.8, 2.6, t)); + if (a > 0) { + g2.save(); + g2.globalAlpha = a; + const by = portrait ? H2 * 0.9 : H2 * 0.95; + lotusLeaf(g2, W2 * 0.1, by, 150 * u, 3); + lotusLeaf(g2, W2 * 0.24, by + 30 * u, 110 * u, 5, "#7fa070"); + for (let i = 0; i < 9; i++) reed(g2, W2 * (0.8 + i * 0.025), by + 20 * u, (260 + hash(i) * 200) * u * (portrait ? 1.2 : 1), t, 40 + i); + g2.restore(); + } + const fs = (portrait ? 230 : 210) * u; + writeText(g2, "\u4E11\u5C0F\u9E2D", cx, cy, fs, 1.1, 0.45, t, { vertical: portrait }); + const sa = smooth(inv(2.9, 3.6, t)); + if (sa > 0) { + g2.save(); + g2.globalAlpha = sa; + g2.font = `${46 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; + g2.fillText("\u5B89 \u5F92 \u751F \u7AE5 \u8BDD", cx, sy); + brush(g2, [[cx - 170 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 170 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); + g2.restore(); + } + const sp = inv(4.55, 4.85, t); + if (sp > 0) { + const sc = lerp(1.5, 1, easeOut(sp)); + const sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; + g2.save(); + g2.translate(sx, sy); + g2.scale(sc, sc); + g2.rotate(-0.04); + seal(g2, 0, 0, 92 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + const wy = portrait ? H2 * 0.74 : H2 * 0.86; + const wp = inv(2, 6.6, t); + if (wp > 0) { + const x0 = lerp(-120 * u, W2 * 0.62, wp); + bird(g2, { x: x0, y: wy, s: 70 * u, t, walk: 2.4, body: DUCKY, seed: 3 }); + bird(g2, { x: x0 - 120 * u, y: wy + 6 * u, s: 92 * u, t: t + 0.2, walk: 2, body: UGLY, beak: UGLY_BEAK, fluff: 1, sw: 0.1, seed: 9, neckUp: -0.2 }); + } + }, + enterAt: [0.5, 0.45] + }, + summer: { + label: "\u590F\u65E5\u4E61\u6751", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X, portrait } = s; + sky(g2, W2, gy, "#9fbccb", 0.2, "#ffffff", 0); + sun(g2, X(portrait ? -0.55 : -0.7), H2 * (portrait ? 0.13 : 0.17), 58 * u); + mountains(g2, W2, gy - 110 * u, 150 * u, "#7d8a8c", 0.32, 3); + mountains(g2, W2, gy - 40 * u, 110 * u, "#56645c", 0.42, 7); + band(g2, W2, gy - 70 * u, gy + 40 * u, "#d9b45a", 0.42, 21, 14 * u); + particles(260, 5, (r) => { + const x = r() * W2, y = gy - 60 * u + r() * 90 * u, h = (14 + r() * 26) * u; + brush(g2, [[x, y + h], [x + 2 * u, y + h * 0.5], [x + 4 * u, y]], 2.4 * u, { color: r() < 0.5 ? "#b88a2e" : "#d8a940", alpha: 0.55, seed: Math.floor(x), taperIn: 0.05 }); + }); + band(g2, W2, gy + 35 * u, H2, "#9fb07a", 0.3, 31); + const px = X(-0.15), py = gy + 190 * u, rx = Math.min(W2 * 0.45, 720 * u), ry = 120 * u; + wash(g2, blobLoop(px, py, 1, 4, 0.04, 26).map(([x, y]) => [px + (x - px) * rx, py + (y - py) * ry]), "#94adb5", 0.4, { edge: 1.5 }); + strokesH(g2, W2 * 0.9, py - ry * 0.6, py + ry * 0.7, 18, "#4f6670", 0.35, 41, u); + lotusLeaf(g2, px - rx * 0.6, py + 30 * u, 70 * u, 1); + lotusLeaf(g2, px - rx * 0.35, py + 70 * u, 55 * u, 2); + lotusLeaf(g2, px + rx * 0.1, py + 10 * u, 60 * u, 3); + blossom(g2, px - rx * 0.48, py - 30 * u, 22 * u, 4, "#e7a0b0"); + tree(g2, X(portrait ? -0.95 : -0.88), gy + 40 * u, 560 * u, 17, { + color: "#3a3128", + depth: 4, + leaves: (x, y, l) => { + blot(g2, x, y, l * 1.4, "#5f8a4c", 0.32); + blot(g2, x + l * 0.4, y - l * 0.3, l * 0.9, "#86a85e", 0.3); + } + }); + }, + draw(s) { + const { g: g2, W: W2, u, t, gy, X } = s; + const k = ks(s); + cloud(g2, X(-0.2) + t * 14 * u, gy - 430 * u, 420 * u, 2, 0.3); + cloud(g2, X(0.5) + t * 9 * u, gy - 520 * u, 300 * u, 5, 0.25); + const nx = X(0.55), ny = gy + 95 * u; + for (let i = 0; i < 12; i++) reed(g2, nx - 220 * u + i * 42 * u + hash(i) * 20 * u, ny - 10 * u + hash(i + 3) * 30 * u, (260 + hash(i * 7) * 180) * u, t, 70 + i); + nest(g2, nx, ny + 10 * u, 270 * k, 3); + egg(g2, nx + 95 * k, ny + 4 * u, 30 * k, { seed: 2 }); + bird(g2, { x: nx, y: ny - 8 * u, s: 230 * k, t, sw: 0.34, body: "#f7f2e6", swim: true, face: -1, seed: 5, neckUp: 0.15 + 0.15 * Math.sin(t * 0.8), look: 0.15 * Math.sin(t * 0.5), fluff: 0 }); + egg(g2, nx - 100 * k, ny + 12 * u, 28 * k, { seed: 4 }); + for (let i = 0; i < 5; i++) reed(g2, nx - 160 * u + i * 85 * u, ny + 60 * u, (150 + hash(i * 11) * 90) * u, t, 90 + i, { alpha: 0.85 }); + const dx = X(0.05) + 280 * u * Math.sin(t * 0.55), dy = gy - 140 * u + 60 * u * Math.sin(t * 1.3); + g2.save(); + g2.translate(dx, dy); + g2.rotate(0.15 * Math.sin(t)); + brush(g2, [[-28 * u, 0], [0, 0], [26 * u, 1 * u]], 6 * u, { color: "#b8322a", alpha: 0.9, taperIn: 0.05, taperOut: 0.9 }); + g2.fillStyle = rgba("#8aa0a8", 0.35); + for (const sgn of [-1, 1]) for (const off of [0, 9 * u]) { + g2.beginPath(); + g2.ellipse(2 * u - off, sgn * (13 * u + 3 * u * Math.sin(t * 40)), 7 * u, 16 * u, sgn * 0.3, 0, Math.PI * 2); + g2.fill(); + } + g2.restore(); + for (let b = 0; b < 2; b++) { + const bx = X(-0.5 + b * 0.35) + 120 * u * Math.sin(t * 0.7 + b * 2), by = gy - 60 * u + 50 * u * Math.sin(t * 1.1 + b); + const f = Math.abs(Math.sin(t * 9 + b)); + g2.fillStyle = rgba(b ? "#e0a040" : "#f1f0ea", 0.85); + for (const sgn of [-1, 1]) { + g2.beginPath(); + g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.fill(); + } + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = 1.5 * u; + for (const sgn of [-1, 1]) { + g2.beginPath(); + g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.stroke(); + } + } + void W2; + }, + cam(s) { + const p = easeInOut(inv(0, s.D, s.t)); + return cam(s, lerp(1, 1.4, p), lerp(s.W / 2, s.X(0.52), p), lerp(s.H / 2, s.gy + 20 * s.u, p)); + }, + enterAt: [0.5, 0.4] + }, + hatch: { + label: "\u7834\u58F3", + bg: bgNestSide, + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks(s); + const [nx, ny] = nestPos(s); + bird(g2, { x: X(-0.6), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: -0.25 + 0.1 * Math.sin(t * 0.7), fluff: 0 }); + nest(g2, nx, ny + 10 * u, 470 * k, 8); + const eggs = [[-150, -6], [-70, 8], [110, 10], [180, -6]]; + egg(g2, nx + 20 * k, ny - 2 * u, 62 * k, { color: "#e6e3d6", seed: 99, wobble: t > s.D - 2.5 ? 0.04 * Math.sin(t * 13) : 0 }); + const targets = [[X(-0.38), ny + 150 * u], [X(-0.2), ny + 175 * u], [X(0.32), ny + 165 * u], [X(0.5), ny + 140 * u]]; + eggs.forEach(([ex, ey], i) => { + const x = nx + ex * k, y = ny + ey * u; + const tc = 0.4 + i * 0.75; + const crack = inv(tc, tc + 0.35, t); + const open = easeOut(inv(tc + 0.55, tc + 1.05, t)); + egg(g2, x, y, 40 * k, { crack, open, seed: 10 + i, wobble: crack > 0 && open === 0 ? 0.08 * Math.sin(t * 30) : 0 }); + if (open > 0) { + const em = spring(inv(tc + 0.55, tc + 1.3, t)); + const hop = inv(tc + 2, tc + 3, t); + const [tx, ty] = targets[i]; + const bx = lerp(x, tx, easeInOut(hop)), by = lerp(y + 10 * u, ty, easeInOut(hop)) - Math.sin(Math.PI * hop) * 70 * u; + const peep = (t - tc) % 2.3; + bird(g2, { + x: bx, + y: by + (1 - em) * 40 * u, + s: 88 * k * lerp(0.6, 1, em), + t, + body: DUCKY, + seed: 20 + i, + face: i < 2 ? -1 : 1, + walk: hop > 0 && hop < 1 ? 3 : 0, + mouth: peep > 1 && peep < 1.25 ? 1 : 0, + squash: (1 - em) * 0.2, + neckUp: 0.15, + legs: hop > 0 + }); + if (hop >= 1 && peep > 1 && peep < 1.5) soundMarks(g2, bx + (i < 2 ? -1 : 1) * 40 * u, by - 95 * u, 40 * u, 1 - (peep - 1) / 0.5, i, i < 2 ? -1 : 1); + } + }); + }, + cam(s) { + const [nx, ny] = nestPos(s); + return cam(s, lerp(1.3, 1.42, easeInOut(s.t / s.D)), nx - 20 * s.u, ny + 30 * s.u); + }, + enterAt: [0.55, 0.65] + }, + bigegg: { + label: "\u6700\u5927\u7684\u86CB", + bg: bgNestSide, + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks(s); + const [nx, ny] = nestPos(s); + const C = CUE.bigegg; + const surprise = smooth(inv(C.surprise, C.surprise + 0.3, t)) * (1 - smooth(inv(C.surprise + 2.2, C.surprise + 3.2, t))); + bird(g2, { x: X(-0.62), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: lerp(-0.2, 0.75, surprise), fluff: 0 }); + nest(g2, nx, ny + 10 * u, 470 * k, 8); + [[-150, -6], [-70, 8], [110, 10], [180, -6]].forEach(([ex2, ey2], i) => egg(g2, nx + ex2 * k, ny + ey2 * u, 40 * k, { open: 1, seed: 10 + i })); + const amp = 0.12 * smooth(inv(0.3, C.crackFrom, t)); + const wob = Math.sin(t * 14) * amp; + const ex = nx + 20 * k, ey = ny - 2 * u; + const crack = inv(C.crackFrom, C.open - 0.05, t), open = easeOut(inv(C.open, C.open + 0.5, t)); + egg(g2, ex, ey - (open ? 0 : Math.abs(Math.sin(t * 7)) * 8 * u * amp * 6), 62 * k, { color: "#e6e3d6", crack, open, seed: 99, wobble: open ? 0 : wob }); + const back = inv(C.walk, C.walk + 2.6, t); + if (open > 0) { + const em = spring(inv(C.open + 0.1, C.open + 1, t)); + const ux = lerp(ex, X(-0.02), easeInOut(back)), uy = lerp(ey + 8 * u, ny + 175 * u, easeInOut(back)) - Math.sin(Math.PI * clamp(back * 3)) * 40 * u; + bird(g2, { + x: ux, + y: uy + (1 - em) * 60 * u, + s: 132 * k * lerp(0.7, 1, em), + t, + sw: 0.12, + body: UGLY, + beak: UGLY_BEAK, + fluff: 1.1, + seed: 7, + face: -1, + squash: (1 - em) * 0.25, + legs: back > 0, + walk: back > 0 && back < 1 ? 1.6 : 0, + tilt: t > C.question && t < C.walk ? 0.18 * Math.sin((t - C.question) * 2) : 0, + neckUp: t > C.question && t < C.walk ? 0.2 : 0 + }); + question(g2, ux + 10 * u, uy - 230 * u * k, 90 * u, smooth(inv(C.question, C.question + 0.3, t)) * (1 - smooth(inv(C.walk - 0.4, C.walk - 0.1, t)))); + } + for (let i = 0; i < 4; i++) { + const recoil = surprise * 0.3 + easeInOut(back) * 0.5; + const x = X(-0.42) + i * 70 * u - recoil * 60 * u; + const y = ny + 150 * u + i % 2 * 22 * u; + bird(g2, { x, y, s: 86 * k, t: t + i * 0.3, body: DUCKY, seed: 30 + i, face: 1, neckUp: 0.25 - surprise * 0.2, tilt: -0.2 * surprise, walk: back > 0 && back < 1 ? 1.2 : 0 }); + exclaim(g2, x + 10 * u, y - 150 * u, 46 * u, smooth(inv(C.surprise, C.surprise + 0.15, t)) * (1 - smooth(inv(C.surprise + 1.4, C.surprise + 1.8, t))), i); + } + }, + cam(s) { + const [nx, ny] = nestPos(s); + const zin = easeInOut(inv(0, CUE.bigegg.open + 0.6, s.t)), zout = easeInOut(inv(CUE.bigegg.walk - 0.4, CUE.bigegg.walk + 2.4, s.t)); + return cam(s, lerp(lerp(1.35, 1.9, zin), 1.35, zout), lerp(nx + 20 * s.u, s.X(-0.18), zout), ny + lerp(-30, 70, zout) * s.u); + }, + enterAt: [0.55, 0.62] + }, + bullied: { + label: "\u88AB\u5632\u7B11", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#c9c2b0", 0.18, "#ffffff", 0); + const rx = X(0.78), ry = gy - 360 * u; + wash(g2, [[rx - 260 * u, ry + 40 * u], [rx + 400 * u, ry - 40 * u], [rx + 420 * u, ry + 70 * u], [rx - 240 * u, ry + 120 * u]], "#4a4a4f", 0.6); + for (let i = 0; i < 12; i++) brush(g2, [[rx - 240 * u + i * 55 * u, ry + 115 * u - i * 6 * u], [rx - 250 * u + i * 55 * u, ry + 40 * u - i * 6 * u]], 4 * u, { alpha: 0.5, seed: i }); + wash(g2, [[rx - 200 * u, ry + 115 * u], [rx + 400 * u, ry + 65 * u], [rx + 400 * u, gy - 10 * u], [rx - 200 * u, gy - 10 * u]], "#b3a48a", 0.4); + const hx = X(-0.78), hy = gy - 10 * u; + wash(g2, [[hx - 150 * u, hy], [hx - 110 * u, hy - 150 * u], [hx, hy - 210 * u], [hx + 110 * u, hy - 150 * u], [hx + 150 * u, hy]], "#d6b060", 0.55); + particles(60, 12, (r) => { + const a = r() * Math.PI - Math.PI, rr = r() * 140 * u; + const x = hx + Math.cos(a) * rr, y = hy - 20 * u + Math.sin(a) * rr; + brush(g2, [[x, y], [x + 18 * u * (r() - 0.5), y - 25 * u]], 2 * u, { color: "#8a6a2a", alpha: 0.6 }); + }); + band(g2, W2, gy - 20 * u, H2, "#c9b48a", 0.32, 51); + fence(g2, -20 * u, W2 + 20 * u, gy + 10 * u, 160 * u, 5); + strokesH(g2, W2, gy + 20 * u, H2, 34, "#8a7450", 0.3, 13, u); + }, + draw(s) { + const { g: g2, u, t, gy, X } = s; + const k = ks(s); + const C = CUE.bullied; + const laugh = t > C.laugh1[0] && t < C.laugh1[1] || t > C.laugh2[0] && t < C.laugh2[1]; + const ga = inv(C.girlIn, C.girlIn + 0.9, t); + if (ga > 0) child(g2, lerp(X(-1.15), X(-0.78), easeOut(ga)), gy + 200 * u, 360 * k, t, { face: 1, pose: t > C.shoo ? "shoo" : "basket", dress: "#b8483e", seed: 4 }); + for (let i = 0; i < 3; i++) { + const x = X(-0.52) + i * 85 * u, y = gy + 150 * u + i % 2 * 20 * u; + const b = laugh ? Math.abs(Math.sin(t * 9 + i)) : 0; + bird(g2, { x, y, s: 92 * k, t: t + i, body: DUCKY, seed: 40 + i, face: 1, mouth: laugh ? 0.4 + 0.6 * b : 0, squash: 0.06 * b, tilt: laugh ? -0.15 : 0, neckUp: laugh ? 0.3 : 0 }); + if (laugh) soundMarks(g2, x + 50 * u, y - 120 * u, 36 * u, 0.8, i); } - const p = easeOut(inv(0.3, 2.4, t)); - if (p > 0) { - const R = (portrait ? 420 : 350) * u * p; - wash(g2, blobLoop(cx, cy, R, 5, 0.05, 32), "#a39c90", 0.14 + 0.18 * (1 - p), { edge: 1.4 }); - blot(g2, cx, cy, R * 0.85, "#8a8276", 0.12 * p); - particles(10, 77, (r, i) => { - const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(0.3, 0.8, t)); - const rr = (3 + r() * 8) * u; - g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + const ra = inv(C.roosterIn, C.roosterIn + 0.6, t); + const pecks = C.pecks; + let peck = 0, pushed = 0; + for (const p of pecks) { + const q2 = inv(p - 0.12, p + 0.12, t); + peck = Math.max(peck, Math.sin(Math.PI * q2)); + if (t > p) pushed += 1; + } + const ux = X(0.1) - pushed * 30 * u; + const cower = t > C.shoo ? 0.2 : 0.08; + bird(g2, { x: ux, y: gy + 170 * u, s: 140 * k, t, sw: 0.16, body: UGLY, beak: UGLY_BEAK, fluff: 1.1, seed: 7, face: t > C.girlIn ? -1 : 1, neckUp: -0.45, shiver: t > C.pecks[0] ? 1 : 0.4, squash: cower }); + tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(1.2, 1.5, t)) * (1 - smooth(inv(3.4, 3.7, t)))); + tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(7.6, 7.9, t)) * (1 - smooth(inv(9.6, 10, t)))); + if (ra > 0) { + const rx = lerp(X(1.3), X(0.42), easeOut(ra)) - (t > C.pecks[3] + 0.3 ? (t - C.pecks[3] - 0.3) * 20 * u : 0); + rooster(g2, rx, gy + 175 * u, 240 * k, t, { face: -1, peck, flap: t > C.roosterIn + 0.6 && t < C.pecks[3] + 0.5 ? t * 18 : 0 }); + } + pecks.forEach((p, j) => { + const q2 = t - p; + if (q2 < 0 || q2 > 2) return; + for (let m = 0; m < 3; m++) { + const x = ux + 30 * u + (m - 1) * 30 * u + Math.sin(q2 * 4 + m) * 20 * u; + const y = gy + 40 * u - 80 * u * easeOut(q2 * 2) + q2 * q2 * 40 * u; + leaf(g2, x, y, 10 * u, q2 * 3 + m + j, "#9a968e", 0.8 * (1 - q2 / 2)); + } + }); + }, + cam(s) { + return cam(s, lerp(1.28, 1.36, easeInOut(s.t / s.D)), s.X(-0.08), s.gy + 110 * s.u); + }, + enterAt: [0.3, 0.6] + }, + leave: { + label: "\u79BB\u5BB6", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#b9a48a", 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 70 * u, 140 * u, "#8a8478", 0.3, 23); + mountains(g2, W2, gy - 20 * u, 90 * u, "#6a665e", 0.35, 29); + band(g2, W2, gy - 20 * u, H2, "#b8ad8a", 0.28, 61); + const path = [[X(-0.75), H2 + 20 * u], [X(-0.42), gy + 170 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 30 * u], [X(0.68), gy - 25 * u]]; + const pts = spline(path, 10); + for (let i = 0; i < pts.length - 1; i++) { + const f = i / pts.length; + brush(g2, [pts[i], pts[i + 1]], lerp(150, 16, f) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + } + tree(g2, X(0.8), gy - 10 * u, 380 * u, 33, { depth: 4, leaves: (x, y, l) => blot(g2, x, y, l * 0.8, "#8a7a50", 0.25) }); + fence(g2, -20 * u, X(-0.12), gy + 120 * u, 170 * u, 8, X(-0.42), 120 * u); + strokesH(g2, W2, gy + 30 * u, H2, 30, "#7a6a48", 0.28, 15, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X, D: D2 } = s; + const p = Math.pow(inv(CUE.leave.walk, D2 + 1.2, t), 1.15); + const path = spline([[X(-0.62), gy + 200 * u], [X(-0.42), gy + 175 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 32 * u], [X(0.62), gy - 20 * u]], 12); + const idx = Math.min(path.length - 1, Math.floor(p * (path.length - 1))); + const [bx, by] = path[idx]; + const sc = lerp(150, 52, p) * ks(s); + bird(g2, { x: bx, y: by, s: sc, t, sw: 0.18, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: 1, walk: t > CUE.leave.walk ? 1.7 : 0, neckUp: -0.35, shiver: t < CUE.leave.walk ? 0.4 : 0 }); + tear(g2, bx + 45 * u * (sc / 150), by - 95 * u * (sc / 150) + t * 25 % 25 * u, 9 * u, smooth(inv(0.3, 0.6, t)) * (1 - smooth(inv(2.2, 2.6, t)))); + for (let i = 0; i < 14; i++) grass(g2, i / 14 * W2 + hash(i) * 50 * u, H2 - hash(i * 3) * 120 * u, 70 * u, t, 200 + i, "#5d6a44", 0.6, 2); + fallingThings(g2, W2, H2, t, 14, 8, u, (x, y, rot2, r) => leaf(g2, x, y * 0.8, (8 + r() * 6) * u, rot2, r() < 0.5 ? "#b08a4a" : "#8a7a50", 0.75), 50); + }, + cam(s) { + return cam(s, lerp(1.08, 1.16, s.t / s.D), s.W / 2, s.gy + 60 * s.u); + }, + enterAt: [0.25, 0.7] + }, + marsh: { + label: "\u6CBC\u6CFD", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy } = s; + sky(g2, W2, gy, "#7e878a", 0.25, "#ffffff", 0.02); + mountains(g2, W2, gy - 50 * u, 100 * u, "#6e7672", 0.3, 37, { soft: 1 }); + for (let i = 0; i < 46; i++) reed(g2, i / 46 * W2 + hash(i) * 30 * u, gy - 10 * u, (90 + hash(i * 3) * 90) * u, 0, 500 + i, { color: "#5d6658", alpha: 0.4, wind: 0.2 }); + band(g2, W2, gy - 15 * u, H2, "#8fa3a6", 0.32, 71, 6 * u); + particles(8, 19, (r) => blot(g2, r() * W2, gy + r() * (H2 - gy), (80 + r() * 160) * u, "#5d6a62", 0.12, 2.2, 0.4)); + strokesH(g2, W2, gy + 20 * u, H2, 36, "#3f4c52", 0.35, 17, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const M = CUE.marsh; + water(g2, 0, W2, gy + 10 * u, H2 - gy, t, 3, { alpha: 0.3, lines: 12 }); + for (let i = 0; i < 12; i++) reed(g2, X(0.62) + i * 38 * u, gy + 40 * u + hash(i) * 30 * u, (280 + hash(i * 5) * 160) * u, t, 120 + i, { wind: t > M.shots[0] ? 1 + 2 * Math.exp(-(t - M.shots[0]) * 2) : 1 }); + for (const [ts, fx, fy] of [[M.shots[0], 0.86, -130], [M.shots[1], 0.72, -170]]) { + const q2 = t - ts; + if (q2 > 0 && q2 < 0.09) { + g2.fillStyle = rgba("#e0a040", 0.9); g2.beginPath(); - g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, rr, 0, Math.PI * 2); + g2.arc(X(fx), gy + fy * u, 26 * u, 0, Math.PI * 2); g2.fill(); - void i; - }); + } + if (q2 > 0) smoke(g2, X(fx) - q2 * 30 * u, gy + fy * u - q2 * 25 * u, (30 + 110 * easeOut(q2 / 2)) * u, 0.6 * Math.max(0, 1 - q2 / 4), Math.floor(ts * 10)); } - const a = smooth(inv(0.8, 2.6, t)); - if (a > 0) { - g2.save(); - g2.globalAlpha = a; - const by = portrait ? H2 * 0.9 : H2 * 0.95; - lotusLeaf(g2, W2 * 0.1, by, 150 * u, 3); - lotusLeaf(g2, W2 * 0.24, by + 30 * u, 110 * u, 5, "#7fa070"); - for (let i = 0; i < 9; i++) reed(g2, W2 * (0.8 + i * 0.025), by + 20 * u, (260 + hash(i) * 200) * u * (portrait ? 1.2 : 1), t, 40 + i); - g2.restore(); + for (let i = 0; i < 3; i++) { + const takeoff = easeIn(inv(M.scatter + i * 0.12, M.scatter + 2.1 + i * 0.12, t)); + const bx = X(0.2 + i * 0.16) + takeoff * 380 * u, by = gy + (110 + i * 25) * u - takeoff * (gy + 300 * u); + const laugh = t > M.laugh[0] && t < M.laugh[1]; + const o = { x: bx, y: by, s: 140 * k, t: t + i, sw: 0.32, body: "#9b7b55", head: "#3d6b4a", beak: "#d8b03a", seed: 60 + i, face: takeoff > 0 ? 1 : -1, mouth: laugh ? 0.5 + 0.5 * Math.abs(Math.sin(t * 9 + i)) : 0, fluff: 0 }; + if (takeoff > 0) bird(g2, { ...o, legs: false, wing: 1, flap: t * 16 + i, neckUp: -0.7 }); + else { + swimBird(g2, o); + if (laugh) soundMarks(g2, bx - 70 * u, by - 150 * u, 34 * u, 0.8, i, -1); + } + } + particles(7, 33, (r, i) => { + const q2 = t - M.scatter + 0.1 - r() * 0.4; + if (q2 <= 0) return; + farBird(g2, X(0.8) - q2 * (120 + r() * 120) * u, gy - 120 * u - q2 * (150 + r() * 120) * u, (18 + r() * 12) * u, t * 14 + i); + }); + const hide = easeInOut(inv(M.hide, M.hide + 1.4, t)); + const startle = Math.exp(-Math.max(0, t - M.shots[0]) * 5) * (t > M.shots[0] ? 1 : 0); + const ux = lerp(X(-0.08), X(-0.55), hide), uy = gy + 175 * u; + swimBird(g2, { x: ux, y: uy, s: 150 * k, t, sw: 0.24, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: hide > 0 && hide < 1 ? -1 : 1, neckUp: lerp(0, -0.6, hide), tuck: 0.3 * hide, shiver: t > M.shots[0] ? 1 : 0, squash: startle * 0.2 }); + exclaim(g2, ux + 20 * u, uy - 220 * u, 50 * u, startle > 0.05 ? 1 : 0, 3); + for (let i = 0; i < 9; i++) reed(g2, X(-0.78) + i * 40 * u + hash(i * 9) * 20 * u, H2 + 20 * u, (420 + hash(i * 13) * 220) * u, t, 150 + i, { color: "#2f3b2c", alpha: 0.85, wind: t > M.shots[0] ? 1 + 1.5 * Math.exp(-(t - M.shots[0])) : 1 }); + }, + cam(s) { + const { t, u } = s; + let dx = 0; + for (const ts of CUE.marsh.shots) { + const q2 = t - ts; + if (q2 > 0) dx += Math.sin(q2 * 90) * 10 * u * Math.exp(-q2 * 9); + } + const p = easeInOut(inv(9.2, s.D, t)); + return cam(s, lerp(1.15, 1.55, p), lerp(s.X(0.1), s.X(-0.52), p), s.gy + lerp(90, 120, p) * u, { dx, dy: dx * 0.5 }); + }, + enterAt: [0.5, 0.6] + }, + autumn: { + label: "\u79CB\u5929\u7684\u5927\u9E1F", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#e3a868", 0.12, "#e8b878", 0.38); + sun(g2, X(0.3), gy - 150 * u, 72 * u, "#c8463a", 0.85); + mountains(g2, W2, gy - 40 * u, 170 * u, "#8a7a6a", 0.32, 41); + mountains(g2, W2, gy, 100 * u, "#5e5248", 0.45, 47); + band(g2, W2, gy, gy + 190 * u, "#b8a990", 0.32, 81, 4 * u); + particles(9, 3, (r) => { + const y = gy + 20 * u + r() * 120 * u, w = (80 - (y - gy) / u * 0.3) * u; + brush(g2, [[X(0.3) - w, y], [X(0.3), y + 2 * u], [X(0.3) + w, y]], 5 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); + }); + band(g2, W2, gy + 185 * u, H2, "#b8925a", 0.32, 83); + strokesH(g2, W2, gy + 200 * u, H2, 30, "#7a5a30", 0.3, 19, u); + tree(g2, X(-0.88), gy + 260 * u, 640 * u, 9, { + depth: 5, + color: "#3a2a20", + leaves: (x, y, l) => { + blot(g2, x, y, l * 1.3, "#c0582e", 0.35); + blot(g2, x + l * 0.5, y - l * 0.2, l * 0.9, "#e0a040", 0.3); + } + }); + for (let i = 0; i < 10; i++) reed(g2, X(0.7) + i * 30 * u, gy + 200 * u, (180 + hash(i) * 120) * u, 0, 600 + i, { color: "#8a6a3a", alpha: 0.7 }); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X, portrait } = s; + const k = ks(s); + fallingThings(g2, W2, H2, t, 22, 12, u, (x, y, rot2, r) => leaf(g2, x * 0.55, y, (9 + r() * 7) * u, rot2, ["#c0582e", "#d98a3a", "#b8402a", "#e0a040"][Math.floor(r() * 4)], 0.85), 55); + const sp = (W2 + 1400 * u) / 11.5; + let lead = 0; + for (let i = 0; i < 5; i++) { + const x = W2 + 160 * u + i * 170 * u - (t - CUE.autumn.swans) * sp; + const y = H2 * (portrait ? 0.2 : 0.17) + i * (portrait ? 70 : 52) * u + 10 * u * Math.sin(t * 1.3 + i); + if (i === 0) lead = x; + if (x < -300 * u || x > W2 + 300 * u) continue; + bird(g2, { x, y, s: 105 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 4.2 + i * 0.9, face: -1, neckUp: -0.85, seed: 80 + i }); + } + const lookUp = smooth(inv(CUE.autumn.lookUp, CUE.autumn.lookUp + 1, t)); + const turn = lead < X(0.42); + const walkP = inv(CUE.autumn.walk, CUE.autumn.walk + 2.2, t); + const ux = X(0.42) - easeInOut(walkP) * 110 * u; + bird(g2, { x: ux, y: gy + 230 * u, s: 168 * k, t, sw: 0.42, body: UGLY, beak: UGLY_BEAK, fluff: 0.7, seed: 7, face: turn ? -1 : 1, neckUp: lookUp * 0.95, walk: walkP > 0 && walkP < 1 ? 1.4 : 0 }); + }, + cam(s) { + return cam(s, lerp(1.04, 1.12, easeInOut(s.t / s.D)), lerp(s.X(0.15), s.X(0.05), easeInOut(s.t / s.D)), s.H * 0.48); + }, + enterAt: [0.8, 0.3] + }, + winter: { + label: "\u5BD2\u51AC", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#5f6a74", 0.35, "#c9d0d4", 0.1); + mountains(g2, W2, gy - 30 * u, 160 * u, "#5a6268", 0.3, 53); + particles(10, 5, (r) => blot(g2, r() * W2, gy - 120 * u - r() * 60 * u, (50 + r() * 60) * u, "#ffffff", 0.45, 2, 0.5)); + band(g2, W2, gy - 10 * u, gy + 300 * u, "#c9d4d8", 0.5, 91, 5 * u); + particles(10, 77, (r, i) => { + const x = r() * W2, y = gy + 20 * u + r() * 250 * u; + brush(g2, [[x, y], [x + (60 + r() * 120) * u, y + (r() - 0.5) * 20 * u]], 1.6 * u, { color: "#6a7a86", alpha: 0.35, seed: i }); + }); + band(g2, W2, gy + 290 * u, H2, "#ffffff", 0.6, 93, 14 * u); + for (const [fx, h, sd] of [[-0.86, 520, 3], [0.88, 460, 7]]) { + tree(g2, X(fx), gy + 40 * u, h * u, sd, { depth: 5, color: "#2a2622", leaves: (x, y, l) => blot(g2, x, y - 3 * u, l * 0.35, "#ffffff", 0.85, 1.6, 0.6) }); } - const fs = (portrait ? 230 : 210) * u; - writeText(g2, "\u4E11\u5C0F\u9E2D", cx, cy, fs, 1.1, 0.45, t, { vertical: portrait }); - const sa = smooth(inv(2.9, 3.6, t)); - if (sa > 0) { - g2.save(); - g2.globalAlpha = sa; - g2.font = `${46 * u}px ${FONT_KAI}`; - g2.fillStyle = rgba(INK, 0.85); - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; - g2.fillText("\u5B89 \u5F92 \u751F \u7AE5 \u8BDD", cx, sy); - brush(g2, [[cx - 170 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 170 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); - g2.restore(); + for (let i = 0; i < 12; i++) reed(g2, X(-0.5) + i * 26 * u, gy + 290 * u, (140 + hash(i) * 90) * u, 0, 700 + i, { color: "#6a6458", alpha: 0.65, wind: 0.6 }); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const night = 0.34 * smooth(inv(4, 6.5, t)) - 0.24 * smooth(inv(8.2, 9, t)) + 0.24 * smooth(inv(9.6, 10.6, t)) - 0.3 * smooth(inv(11.8, 13.2, t)); + if (night > 0) { + g2.fillStyle = rgba("#26303c", night); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + blot(g2, X(0.6), H2 * 0.16, 70 * u, "#fffbe8", night * 2.2); + g2.fillStyle = rgba("#fffbe8", night * 2.4); + g2.beginPath(); + g2.arc(X(0.6), H2 * 0.16, 30 * u, 0, Math.PI * 2); + g2.fill(); } - const sp = inv(4.55, 4.85, t); - if (sp > 0) { - const sc = lerp(1.5, 1, easeOut(sp)); - const sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; - g2.save(); - g2.translate(sx, sy); - g2.scale(sc, sc); - g2.rotate(-0.04); - seal(g2, 0, 0, 92 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); - g2.restore(); + const ux = X(-0.04), uy = gy + 150 * u; + for (let i = 0; i < 6; i++) { + const a = i / 6 * Math.PI * 2 + 0.4, L = (150 + hash(i) * 160) * u; + const pts = [[ux + Math.cos(a) * 60 * u, uy + 10 * u + Math.sin(a) * 15 * u]]; + for (let j = 1; j < 5; j++) pts.push([pts[0][0] + Math.cos(a + nz(j, i) * 0.4) * L * j / 4, pts[0][1] + Math.sin(a + nz(j, i) * 0.4) * L * j / 4 * 0.25]); + brush(g2, pts, 2.2 * u, { color: "#3e4c58", alpha: 0.75, grow: easeOut(inv(CUE.winter.ice + i * 0.05, CUE.winter.ice + 0.7 + i * 0.05, t)), taperIn: 0.05, seed: i }); } - const wy = portrait ? H2 * 0.74 : H2 * 0.86; - const wp = inv(2, 6.6, t); - if (wp > 0) { - const x0 = lerp(-120 * u, W2 * 0.62, wp); - bird(g2, { x: x0, y: wy, s: 70 * u, t, walk: 2.4, body: DUCKY, seed: 3 }); - bird(g2, { x: x0 - 120 * u, y: wy + 6 * u, s: 92 * u, t: t + 0.2, walk: 2, body: UGLY, beak: UGLY_BEAK, fluff: 1, sw: 0.1, seed: 9, neckUp: -0.2 }); + bird(g2, { x: ux, y: uy, s: 175 * k, t, sw: 0.5, body: UGLY, beak: UGLY_BEAK, fluff: 0.8, seed: 7, face: 1, swim: true, tuck: 1, shiver: 1, snow: smooth(inv(0, s.D, t)), noBlink: true }); + for (let i = 0; i < 4; i++) { + const q2 = (t * 0.45 + i * 0.27) % 1; + const y = H2 * (0.2 + 0.18 * i); + brush(g2, [[lerp(-300, W2 + 300, q2) * 1, y], [lerp(-300, W2 + 300, q2) + 180 * u, y + 6 * u], [lerp(-300, W2 + 300, q2) + 360 * u, y]], 3 * u, { color: "#d8dde0", alpha: 0.5, dry: 0.9, seed: i }); } + fallingThings(g2, W2, H2, t, 170, 31, u, (x, y, _r, r) => { + const rr = (2 + r() * 5) * u; + g2.fillStyle = rgba("#ffffff", 0.92); + g2.beginPath(); + g2.arc(x, y, rr, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba("#8a96a0", 0.35); + g2.lineWidth = 0.8 * u; + g2.stroke(); + }, 95); }, - enterAt: [0.5, 0.45] + cam(s) { + return cam(s, lerp(1.2, 1.5, easeInOut(s.t / s.D)), s.X(-0.04), s.gy + 70 * s.u); + }, + enterAt: [0.5, 0.2] }, - summer: { - label: "\u590F\u65E5\u4E61\u6751", + spring: { + label: "\u6625\u56DE", bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X, portrait } = s; - sky(g2, W2, gy, "#9fbccb", 0.2, "#ffffff", 0); - sun(g2, X(portrait ? -0.55 : -0.7), H2 * (portrait ? 0.13 : 0.17), 58 * u); - mountains(g2, W2, gy - 110 * u, 150 * u, "#7d8a8c", 0.32, 3); - mountains(g2, W2, gy - 40 * u, 110 * u, "#56645c", 0.42, 7); - band(g2, W2, gy - 70 * u, gy + 40 * u, "#d9b45a", 0.42, 21, 14 * u); - particles(260, 5, (r) => { - const x = r() * W2, y = gy - 60 * u + r() * 90 * u, h = (14 + r() * 26) * u; - brush(g2, [[x, y + h], [x + 2 * u, y + h * 0.5], [x + 4 * u, y]], 2.4 * u, { color: r() < 0.5 ? "#b88a2e" : "#d8a940", alpha: 0.55, seed: Math.floor(x), taperIn: 0.05 }); + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#bcd3c8", 0.2, "#ffffff", 0); + sun(g2, X(0.5), H2 * 0.15, 50 * u, "#d65a40", 0.7); + mountains(g2, W2, gy - 40 * u, 150 * u, "#86a87a", 0.35, 61); + mountains(g2, W2, gy, 90 * u, "#5e8a50", 0.4, 67); + band(g2, W2, gy - 5 * u, H2, "#a9c48a", 0.35, 101); + const pts = spline([[X(-1.1), gy + 120 * u], [X(-0.3), gy + 60 * u], [X(0.3), gy + 160 * u], [X(1.1), gy + 90 * u]], 12); + brush(g2, pts, 70 * u, { color: "#9fc0c8", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.2 }); + brush(g2, pts.map(([x, y]) => [x, y - 10 * u]), 6 * u, { color: "#5e7a86", alpha: 0.3, dry: 0.7, taperIn: 0, taperOut: 0 }); + strokesH(g2, W2, gy + 200 * u, H2, 26, "#5e8a50", 0.3, 23, u); + }, + draw(s) { + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; + const k = ks(s); + const C = CUE.spring; + willow(g2, X(-0.9), gy + 220 * u, 620 * u, t, 3, "#8fb86b", 0.15 + 0.85 * smooth(inv(C.buds, C.buds + 1.4, t))); + const bloom = smooth(inv(C.bloom, C.bloom + 1.3, t)); + tree(g2, X(0.86), gy + 220 * u, 520 * u, 21, { depth: 4, color: "#3a2c26", t, leaves: (x, y, l) => { + if (bloom > 0) { + blossom(g2, x, y, l * 0.28 * bloom, Math.floor(x), "#ec9aa8", 0.75); + blossom(g2, x + l * 0.3, y - l * 0.2, l * 0.2 * bloom, Math.floor(y), "#f4b8c0", 0.7); + } else blot(g2, x, y, l * 0.12, "#b86a74", 0.5); + } }); + for (let i = 0; i < 2; i++) farBird(g2, X(-0.2 + i * 0.4) + Math.sin(t * 0.5 + i) * 100 * u, H2 * 0.25 + i * 40 * u, 20 * u, t * 9 + i, "#3a3a3a"); + const spread = easeOut(inv(C.spread, C.spread + 0.9, t)); + const lift = easeIn(inv(C.lift, C.lift + 4.2, t)); + const fx = X(-0.12) + lift * W2 * 0.55, fy = gy + 170 * u - lift * (gy + 260 * u); + const flapRate = t < C.flapHard ? 2.6 : t < C.lift ? lerp(3, 6, inv(C.flapHard, C.lift, t)) : 4.6; + bird(g2, { + x: fx, + y: fy, + s: 205 * k, + t, + sw: 0.8, + body: "#b5b1a9", + beak: "#b88a5c", + fluff: 0.2, + seed: 7, + face: 1, + wing: spread, + flap: t * flapRate * 1 + (t > 3.4 ? Math.sin(t) : 0), + legs: lift < 0.05, + neckUp: lift > 0 ? -0.6 * smooth(lift * 4) : lerp(0, 0.3, spread), + look: t < C.spread ? 0.2 * Math.sin(t * 1.5) : 0 }); - band(g2, W2, gy + 35 * u, H2, "#9fb07a", 0.3, 31); - const px = X(-0.15), py = gy + 190 * u, rx = Math.min(W2 * 0.45, 720 * u), ry = 120 * u; - wash(g2, blobLoop(px, py, 1, 4, 0.04, 26).map(([x, y]) => [px + (x - px) * rx, py + (y - py) * ry]), "#94adb5", 0.4, { edge: 1.5 }); - strokesH(g2, W2 * 0.9, py - ry * 0.6, py + ry * 0.7, 18, "#4f6670", 0.35, 41, u); - lotusLeaf(g2, px - rx * 0.6, py + 30 * u, 70 * u, 1); - lotusLeaf(g2, px - rx * 0.35, py + 70 * u, 55 * u, 2); - lotusLeaf(g2, px + rx * 0.1, py + 10 * u, 60 * u, 3); - blossom(g2, px - rx * 0.48, py - 30 * u, 22 * u, 4, "#e7a0b0"); - tree(g2, X(portrait ? -0.95 : -0.88), gy + 40 * u, 560 * u, 17, { - color: "#3a3128", - depth: 4, - leaves: (x, y, l) => { - blot(g2, x, y, l * 1.4, "#5f8a4c", 0.32); - blot(g2, x + l * 0.4, y - l * 0.3, l * 0.9, "#86a85e", 0.3); - } + question(g2, fx + 30 * u, fy - 330 * u * k, 80 * u, smooth(inv(C.question, C.question + 0.25, t)) * (1 - smooth(inv(C.flapHard + 0.2, C.flapHard + 0.5, t)))); + if (t > C.lift && t < C.lift + 1.2) particles(8, 99, (r, i) => { + const q2 = t - C.lift; + leaf(g2, X(-0.12) + (r() - 0.5) * 200 * u, gy + 180 * u - q2 * 80 * u * r(), 8 * u, q2 * 4 + i, "#9fbf7a", 0.8 * (1 - q2 / 1.2)); }); + fallingThings(g2, W2, H2, t, 26, 41, u, (x, y, rot2, r) => leaf(g2, x, y, (6 + r() * 5) * u, rot2, r() < 0.5 ? "#f0a8b4" : "#f6c8cf", 0.85), 45); + }, + cam(s) { + const lift = easeInOut(inv(CUE.spring.lift - 0.3, CUE.spring.lift + 3.5, s.t)); + return cam(s, lerp(1.3, 1.1, lift), lerp(s.X(-0.1), s.W / 2, lift), lerp(s.gy + 60 * s.u, s.H * 0.3, lift)); + }, + enterAt: [0.5, 0.5] + }, + reflection: { + label: "\u5012\u5F71", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#c9d8d8", 0.18, "#ffffff", 0); + mountains(g2, W2, gy - 60 * u, 120 * u, "#8fa69a", 0.28, 71, { soft: 1 }); + for (let i = 0; i < 7; i++) { + const x = X(-1 + i * 0.33) + hash(i) * 60 * u; + willow(g2, x, gy - 20 * u, (200 + hash(i * 3) * 80) * u, 0, 40 + i, "#8fb86b", 1); + } + band(g2, W2, gy - 25 * u, H2, "#a9bcc0", 0.32, 111, 3 * u); + strokesH(g2, W2, gy, H2, 30, "#5a6e78", 0.28, 29, u); + lotusLeaf(g2, X(-0.9), H2 - 90 * u, 120 * u, 7); + lotusLeaf(g2, X(-0.72), H2 - 30 * u, 90 * u, 8); + lotusLeaf(g2, X(0.92), H2 - 60 * u, 110 * u, 9); }, draw(s) { - const { g: g2, W: W2, u, t, gy, X } = s; + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; const k = ks(s); - cloud(g2, X(-0.2) + t * 14 * u, gy - 430 * u, 420 * u, 2, 0.3); - cloud(g2, X(0.5) + t * 9 * u, gy - 520 * u, 300 * u, 5, 0.25); - const nx = X(0.55), ny = gy + 95 * u; - for (let i = 0; i < 12; i++) reed(g2, nx - 220 * u + i * 42 * u + hash(i) * 20 * u, ny - 10 * u + hash(i + 3) * 30 * u, (260 + hash(i * 7) * 180) * u, t, 70 + i); - nest(g2, nx, ny + 10 * u, 270 * k, 3); - egg(g2, nx + 95 * k, ny + 4 * u, 30 * k, { seed: 2 }); - bird(g2, { x: nx, y: ny - 8 * u, s: 230 * k, t, sw: 0.34, body: "#f7f2e6", swim: true, face: -1, seed: 5, neckUp: 0.15 + 0.15 * Math.sin(t * 0.8), look: 0.15 * Math.sin(t * 0.5), fluff: 0 }); - egg(g2, nx - 100 * k, ny + 12 * u, 28 * k, { seed: 4 }); - for (let i = 0; i < 5; i++) reed(g2, nx - 160 * u + i * 85 * u, ny + 60 * u, (150 + hash(i * 11) * 90) * u, t, 90 + i, { alpha: 0.85 }); - const dx = X(0.05) + 280 * u * Math.sin(t * 0.55), dy = gy - 140 * u + 60 * u * Math.sin(t * 1.3); - g2.save(); - g2.translate(dx, dy); - g2.rotate(0.15 * Math.sin(t)); - brush(g2, [[-28 * u, 0], [0, 0], [26 * u, 1 * u]], 6 * u, { color: "#b8322a", alpha: 0.9, taperIn: 0.05, taperOut: 0.9 }); - g2.fillStyle = rgba("#8aa0a8", 0.35); - for (const sgn of [-1, 1]) for (const off of [0, 9 * u]) { - g2.beginPath(); - g2.ellipse(2 * u - off, sgn * (13 * u + 3 * u * Math.sin(t * 40)), 7 * u, 16 * u, sgn * 0.3, 0, Math.PI * 2); - g2.fill(); + water(g2, 0, W2, gy, H2 - gy, t, 7, { alpha: 0.22, lines: 10, speed: 3 }); + const px = X(-0.16), py = gy + 175 * u, ps = 200 * k; + const R = CUE.reflection; + const reveal = smooth(inv(R.reveal, R.reveal + 1.6, t)); + const bow = smooth(inv(R.bow, R.bow + 1, t)) * (1 - smooth(inv(R.proud - 0.4, R.proud + 0.8, t))); + const proud = smooth(inv(R.proud, R.proud + 1.2, t)); + const land = inv(0, R.land, t); + const body = mixHex("#aaa69e", WHITE, reveal); + const o = { x: px, y: py, s: ps, t, sw: 1, body, beak: mixHex("#b88a5c", SWAN_BEAK, reveal), seed: 7, face: 1, neckUp: -0.95 * bow + 0.25 * proud, glow: reveal * (1 - smooth(inv(R.reveal + 2, R.reveal + 4, t))), noBlink: bow > 0.5 }; + const refl = (c2) => bird(c2, { ...o, body: WHITE, beak: SWAN_BEAK, swim: true, glow: 0 }); + if (land >= 1) { + const mirror = smooth(inv(R.mirror, R.mirror + 1.2, t)); + reflection(g2, refl, { x: px - ps * 1.3, y: py - ps * 1.4, w: ps * 2.8, h: ps * 1.46 }, py + ps * 0.06, t, lerp(0.3, 0.95, mirror), "#7a90a0"); + if (mirror > 0 && mirror < 1) blot(g2, px + 60 * u, py + 140 * u, 220 * u, "#fff4d8", 0.35 * Math.sin(Math.PI * mirror)); + if (reveal > 0 && reveal < 1) blot(g2, px + 30 * u, py - 60 * u, 320 * u, "#fff4d8", 0.55 * Math.sin(Math.PI * reveal)); + for (let r = 0; r < 3; r++) { + const q2 = inv(R.reveal + r * 0.5, R.reveal + 2.4 + r * 0.5, t); + if (q2 > 0 && q2 < 1) ripplesRing(g2, px + 70 * u, py + 60 * u, 60 * u + q2 * 420 * u, 0.4 * (1 - q2), "#9a7a3a"); + } } - g2.restore(); - for (let b = 0; b < 2; b++) { - const bx = X(-0.5 + b * 0.35) + 120 * u * Math.sin(t * 0.7 + b * 2), by = gy - 60 * u + 50 * u * Math.sin(t * 1.1 + b); - const f = Math.abs(Math.sin(t * 9 + b)); - g2.fillStyle = rgba(b ? "#e0a040" : "#f1f0ea", 0.85); - for (const sgn of [-1, 1]) { + for (let i = 0; i < 3; i++) { + const q2 = easeOut(inv(1.2 + i * 0.4, 5.2 + i * 0.4, t)); + const sx = lerp(X(1.3 + i * 0.2), X(0.3 + i * 0.24), q2), sy = gy + (110 + i * 45) * u; + const greet = smooth(inv(R.proud + 0.4 + i * 0.2, R.proud + 1.2 + i * 0.2, t)); + swimBird(g2, swanBird(sx, sy, (150 - i * 12) * k, t + i, { face: -1, seed: 90 + i, neckUp: -0.55 * greet + 0.08 * Math.sin(t + i) })); + } + if (land < 1) { + const lx = lerp(X(-0.65), px, easeOut(land)), ly = lerp(H2 * 0.12, py, easeInOut(land)); + bird(g2, { ...o, x: lx, y: ly, legs: false, wing: 1 - land * 0.3, flap: t * 9, swim: true, neckUp: -0.5 }); + } else { + const settle = inv(R.land, R.land + 0.85, t); + swimBird(g2, { ...o, swim: true, wing: (1 - easeOut(settle)) * 0.7, flap: t * 6 }); + const q2 = t - R.land; + if (q2 < 1) particles(14, 5, (r) => { + const a = -Math.PI * r(), v = (100 + r() * 180) * u; + const x = px + Math.cos(a) * v * q2 * 1.6, y = py + Math.sin(a) * v * q2 * 1.4 + 300 * u * q2 * q2; + g2.fillStyle = rgba("#ffffff", 0.9 * (1 - q2)); g2.beginPath(); - g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.arc(x, y, (3 + r() * 5) * u, 0, Math.PI * 2); g2.fill(); - } - g2.strokeStyle = rgba(INK, 0.6); - g2.lineWidth = 1.5 * u; - for (const sgn of [-1, 1]) { - g2.beginPath(); - g2.ellipse(bx + sgn * 9 * u * f, by, 9 * u * f + 1, 12 * u, sgn * 0.4, 0, Math.PI * 2); + g2.strokeStyle = rgba("#6a7a86", 0.5 * (1 - q2)); + g2.lineWidth = u; g2.stroke(); - } + }); } - void W2; + particles(10, 61, (r, i) => { + const x = (r() * W2 + t * 14 * u) % W2, y = gy + 40 * u + r() * (H2 - gy - 80 * u); + leaf(g2, x, y, 6 * u, t * 0.3 + i, "#f0a8b4", 0.75); + }); }, cam(s) { - const p = easeInOut(inv(0, s.D, s.t)); - return cam(s, lerp(1, 1.4, p), lerp(s.W / 2, s.X(0.52), p), lerp(s.H / 2, s.gy + 20 * s.u, p)); + const { t, u, gy } = s; + const zin = easeInOut(inv(CUE.reflection.bow - 0.2, CUE.reflection.mirror + 1.2, t)), zout = easeInOut(inv(CUE.reflection.reveal - 0.6, CUE.reflection.reveal + 2.4, t)); + return cam(s, lerp(lerp(1.25, 1.65, zin), 1.3, zout), lerp(s.X(-0.05), s.X(-0.1), zin), gy + lerp(lerp(60, 230, zin), 110, zout) * u); }, - enterAt: [0.5, 0.4] + enterAt: [0.3, 0.3] }, - hatch: { - label: "\u7834\u58F3", - bg: bgNestSide, + accepted: { + label: "\u63A5\u7EB3", + bg(s) { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky(g2, W2, gy, "#b8d0d8", 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 50 * u, 110 * u, "#88a294", 0.28, 81, { soft: 1 }); + band(g2, W2, gy - 20 * u, H2, "#a3bfc6", 0.34, 121, 3 * u); + strokesH(g2, W2, gy, H2, 30, "#4f6670", 0.28, 33, u); + const bank = [[-50 * u, gy - 60 * u], [X(-0.55), gy - 20 * u], [X(-0.38), gy + 120 * u], [X(-0.5), gy + 260 * u], [-50 * u, gy + 300 * u]]; + wash(g2, bank, "#9fb878", 0.6); + for (let i = 0; i < 10; i++) grass(g2, X(-0.95) + i * 50 * u, gy + 100 * u + hash(i) * 120 * u, 50 * u, 0, 300 + i, "#4f6a3e", 0.6, 0.3); + }, draw(s) { - const { g: g2, u, t, X } = s; + const { g: g2, W: W2, H: H2, u, t, gy, X } = s; const k = ks(s); - const [nx, ny] = nestPos(s); - bird(g2, { x: X(-0.6), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: -0.25 + 0.1 * Math.sin(t * 0.7), fluff: 0 }); - nest(g2, nx, ny + 10 * u, 470 * k, 8); - const eggs = [[-150, -6], [-70, 8], [110, 10], [180, -6]]; - egg(g2, nx + 20 * k, ny - 2 * u, 62 * k, { color: "#e6e3d6", seed: 99, wobble: t > s.D - 2.5 ? 0.04 * Math.sin(t * 13) : 0 }); - const targets = [[X(-0.38), ny + 150 * u], [X(-0.2), ny + 175 * u], [X(0.32), ny + 165 * u], [X(0.5), ny + 140 * u]]; - eggs.forEach(([ex, ey], i) => { - const x = nx + ex * k, y = ny + ey * u; - const tc = 0.4 + i * 0.75; - const crack = inv(tc, tc + 0.35, t); - const open = easeOut(inv(tc + 0.55, tc + 1.05, t)); - egg(g2, x, y, 40 * k, { crack, open, seed: 10 + i, wobble: crack > 0 && open === 0 ? 0.08 * Math.sin(t * 30) : 0 }); - if (open > 0) { - const em = spring(inv(tc + 0.55, tc + 1.3, t)); - const hop = inv(tc + 2, tc + 3, t); - const [tx, ty] = targets[i]; - const bx = lerp(x, tx, easeInOut(hop)), by = lerp(y + 10 * u, ty, easeInOut(hop)) - Math.sin(Math.PI * hop) * 70 * u; - const peep = (t - tc) % 2.3; - bird(g2, { - x: bx, - y: by + (1 - em) * 40 * u, - s: 88 * k * lerp(0.6, 1, em), - t, - body: DUCKY, - seed: 20 + i, - face: i < 2 ? -1 : 1, - walk: hop > 0 && hop < 1 ? 3 : 0, - mouth: peep > 1 && peep < 1.25 ? 1 : 0, - squash: (1 - em) * 0.2, - neckUp: 0.15, - legs: hop > 0 - }); - if (hop >= 1 && peep > 1 && peep < 1.5) soundMarks(g2, bx + (i < 2 ? -1 : 1) * 40 * u, by - 95 * u, 40 * u, 1 - (peep - 1) / 0.5, i, i < 2 ? -1 : 1); - } + cloud(g2, X(0.3) + t * 10 * u, H2 * 0.14, 380 * u, 9, 0.22); + willow(g2, X(-0.88), gy + 60 * u, 560 * u, t, 8, "#8fb86b", 1); + const cheer = t > CUE.accepted.cheer; + [[-0.82, "#c0504a", true], [-0.66, "#4a6a9a", false], [-0.52, "#d0a040", true]].forEach(([fx, col, girl], i) => { + child(g2, X(fx), gy + 110 * u + i * 12 * u, 270 * k, t + i, { face: 1, dress: col, girl, pose: cheer ? i === 1 ? "shoo" : "clap" : "stand", seed: 10 + i }); + if (cheer) soundMarks(g2, X(fx) + 50 * u, gy - 200 * u * k, 40 * u, 0.8 * (1 - smooth(inv(10, 11, t))), i); + }); + const hx = X(0.62), hy = gy + 70 * u; + const heart = smooth(inv(CUE.accepted.heart, CUE.accepted.heart + 2, t)); + swimBird(g2, swanBird(hx - 70 * u, hy, 115 * k, t, { face: 1, seed: 95, neckUp: -0.55 * heart })); + swimBird(g2, swanBird(hx + 70 * u, hy, 115 * k, t + 1, { face: -1, seed: 96, neckUp: -0.55 * heart })); + const px = X(0.12), py = gy + 210 * u; + const nuzzle = (ph) => Math.max(0, Math.sin(t * 1.6 + ph)) * smooth(inv(0.5, 1.5, t)); + swimBird(g2, swanBird(px - 190 * u, py - 30 * u, 165 * k, t + 2, { face: 1, seed: 91, neckUp: -0.7 * nuzzle(0) })); + swimBird(g2, swanBird(px, py, 200 * k, t, { face: 1, seed: 7, neckUp: 0.2 + 0.1 * Math.sin(t), glow: 0.2 })); + swimBird(g2, swanBird(px + 220 * u, py - 20 * u, 165 * k, t + 3, { face: -1, seed: 92, neckUp: -0.7 * nuzzle(2) })); + particles(12, 71, (r, i) => { + const x = (r() * W2 + t * 18 * u) % W2, y = gy + 30 * u + r() * (H2 - gy - 60 * u); + leaf(g2, x, y, 6 * u, t * 0.4 + i, "#f0a8b4", 0.75); }); }, cam(s) { - const [nx, ny] = nestPos(s); - return cam(s, lerp(1.3, 1.42, easeInOut(s.t / s.D)), nx - 20 * s.u, ny + 30 * s.u); + return cam(s, lerp(1.32, 1.18, easeInOut(s.t / s.D)), s.X(-0.12), s.gy + 110 * s.u); }, - enterAt: [0.55, 0.65] + enterAt: [0.2, 0.5] }, - bigegg: { - label: "\u6700\u5927\u7684\u86CB", - bg: bgNestSide, + fly: { + label: "\u9AD8\u98DE", + bg(s) { + const { g: g2, W: W2, H: H2, u, X, portrait } = s; + sky(g2, W2, H2, "#a8c4d4", 0.32, "#f2ead8", 0); + sun(g2, X(0.62), H2 * (portrait ? 0.14 : 0.18), 60 * u, "#d65a40", 0.75); + mountains(g2, W2, H2 * 0.92, 300 * u, "#7d8f8a", 0.35, 91); + mountains(g2, W2, H2 * 1.02, 260 * u, "#4f5d58", 0.5, 97); + }, draw(s) { - const { g: g2, u, t, X } = s; + const { g: g2, W: W2, H: H2, u, t, X, portrait } = s; const k = ks(s); - const [nx, ny] = nestPos(s); - const C = CUE.bigegg; - const surprise = smooth(inv(C.surprise, C.surprise + 0.3, t)) * (1 - smooth(inv(C.surprise + 2.2, C.surprise + 3.2, t))); - bird(g2, { x: X(-0.62), y: ny + 60 * u, s: 270 * k, t, sw: 0.34, body: "#f7f2e6", face: 1, seed: 5, neckUp: lerp(-0.2, 0.75, surprise), fluff: 0 }); - nest(g2, nx, ny + 10 * u, 470 * k, 8); - [[-150, -6], [-70, 8], [110, 10], [180, -6]].forEach(([ex2, ey2], i) => egg(g2, nx + ex2 * k, ny + ey2 * u, 40 * k, { open: 1, seed: 10 + i })); - const amp = 0.12 * smooth(inv(0.3, C.crackFrom, t)); - const wob = Math.sin(t * 14) * amp; - const ex = nx + 20 * k, ey = ny - 2 * u; - const crack = inv(C.crackFrom, C.open - 0.05, t), open = easeOut(inv(C.open, C.open + 0.5, t)); - egg(g2, ex, ey - (open ? 0 : Math.abs(Math.sin(t * 7)) * 8 * u * amp * 6), 62 * k, { color: "#e6e3d6", crack, open, seed: 99, wobble: open ? 0 : wob }); - const back = inv(C.walk, C.walk + 2.6, t); - if (open > 0) { - const em = spring(inv(C.open + 0.1, C.open + 1, t)); - const ux = lerp(ex, X(-0.02), easeInOut(back)), uy = lerp(ey + 8 * u, ny + 175 * u, easeInOut(back)) - Math.sin(Math.PI * clamp(back * 3)) * 40 * u; - bird(g2, { - x: ux, - y: uy + (1 - em) * 60 * u, - s: 132 * k * lerp(0.7, 1, em), - t, - sw: 0.12, - body: UGLY, - beak: UGLY_BEAK, - fluff: 1.1, - seed: 7, - face: -1, - squash: (1 - em) * 0.25, - legs: back > 0, - walk: back > 0 && back < 1 ? 1.6 : 0, - tilt: t > C.question && t < C.walk ? 0.18 * Math.sin((t - C.question) * 2) : 0, - neckUp: t > C.question && t < C.walk ? 0.2 : 0 - }); - question(g2, ux + 10 * u, uy - 230 * u * k, 90 * u, smooth(inv(C.question, C.question + 0.3, t)) * (1 - smooth(inv(C.walk - 0.4, C.walk - 0.1, t)))); - } - for (let i = 0; i < 4; i++) { - const recoil = surprise * 0.3 + easeInOut(back) * 0.5; - const x = X(-0.42) + i * 70 * u - recoil * 60 * u; - const y = ny + 150 * u + i % 2 * 22 * u; - bird(g2, { x, y, s: 86 * k, t: t + i * 0.3, body: DUCKY, seed: 30 + i, face: 1, neckUp: 0.25 - surprise * 0.2, tilt: -0.2 * surprise, walk: back > 0 && back < 1 ? 1.2 : 0 }); - exclaim(g2, x + 10 * u, y - 150 * u, 46 * u, smooth(inv(C.surprise, C.surprise + 0.15, t)) * (1 - smooth(inv(C.surprise + 1.4, C.surprise + 1.8, t))), i); - } + particles(7, 21, (r, i) => { + const y = H2 * (0.15 + r() * 0.6), w = (260 + r() * 360) * u, sp2 = (90 + r() * 160) * u; + const x = ((r() * (W2 + w * 2) - t * sp2) % (W2 + w * 2) + W2 + w * 2) % (W2 + w * 2) - w; + cloud(g2, x, y, w, 30 + i, 0.25 + r() * 0.15); + }); + const enter = easeOut(inv(0, 2.2, t)); + const cx = lerp(-W2 * 0.4, X(portrait ? 0.05 : 0.1), enter) + Math.sin(t * 0.9) * 20 * u; + const cy = H2 * (portrait ? 0.42 : 0.44) + Math.sin(t * 1.2) * 18 * u; + const sp = portrait ? 0.62 : 1; + const flock2 = [[-250, 110, 0.78], [-470, 210, 0.66], [-230, -120, 0.74], [-440, -210, 0.6]]; + flock2.forEach(([dx, dy, sc], i) => bird(g2, { x: cx + dx * u * sp, y: cy + dy * u * sp, s: 230 * k * sc, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.3 + i * 0.8, neckUp: -0.85, seed: 80 + i })); + bird(g2, { x: cx, y: cy, s: 240 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.2, neckUp: -0.85, seed: 7, glow: 0.3 }); }, cam(s) { - const [nx, ny] = nestPos(s); - const zin = easeInOut(inv(0, CUE.bigegg.open + 0.6, s.t)), zout = easeInOut(inv(CUE.bigegg.walk - 0.4, CUE.bigegg.walk + 2.4, s.t)); - return cam(s, lerp(lerp(1.35, 1.9, zin), 1.35, zout), lerp(nx + 20 * s.u, s.X(-0.18), zout), ny + lerp(-30, 70, zout) * s.u); + return { z: lerp(1, 1.08, s.t / s.D), x: s.W / 2, y: s.H / 2 }; }, - enterAt: [0.55, 0.62] + enterAt: [0.1, 0.5] }, - bullied: { - label: "\u88AB\u5632\u7B11", + ending: { + label: "\u5BC4\u8BED", bg(s) { const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#c9c2b0", 0.18, "#ffffff", 0); - const rx = X(0.78), ry = gy - 360 * u; - wash(g2, [[rx - 260 * u, ry + 40 * u], [rx + 400 * u, ry - 40 * u], [rx + 420 * u, ry + 70 * u], [rx - 240 * u, ry + 120 * u]], "#4a4a4f", 0.6); - for (let i = 0; i < 12; i++) brush(g2, [[rx - 240 * u + i * 55 * u, ry + 115 * u - i * 6 * u], [rx - 250 * u + i * 55 * u, ry + 40 * u - i * 6 * u]], 4 * u, { alpha: 0.5, seed: i }); - wash(g2, [[rx - 200 * u, ry + 115 * u], [rx + 400 * u, ry + 65 * u], [rx + 400 * u, gy - 10 * u], [rx - 200 * u, gy - 10 * u]], "#b3a48a", 0.4); - const hx = X(-0.78), hy = gy - 10 * u; - wash(g2, [[hx - 150 * u, hy], [hx - 110 * u, hy - 150 * u], [hx, hy - 210 * u], [hx + 110 * u, hy - 150 * u], [hx + 150 * u, hy]], "#d6b060", 0.55); - particles(60, 12, (r) => { - const a = r() * Math.PI - Math.PI, rr = r() * 140 * u; - const x = hx + Math.cos(a) * rr, y = hy - 20 * u + Math.sin(a) * rr; - brush(g2, [[x, y], [x + 18 * u * (r() - 0.5), y - 25 * u]], 2 * u, { color: "#8a6a2a", alpha: 0.6 }); + sky(g2, W2, gy, "#e8a070", 0.3, "#f0c090", 0.35); + sun(g2, X(0.05), gy - 130 * u, 115 * u, "#c8463a", 0.9); + mountains(g2, W2, gy - 20 * u, 200 * u, "#6a5048", 0.42, 101); + mountains(g2, W2, gy + 10 * u, 120 * u, "#3a2c28", 0.6, 107); + band(g2, W2, gy + 5 * u, H2, "#d8a878", 0.3, 131, 3 * u); + particles(14, 9, (r) => { + const y = gy + 20 * u + r() * 200 * u, w = (130 - (y - gy) / u * 0.4) * u; + brush(g2, [[X(0.05) - w, y], [X(0.05), y + 2 * u], [X(0.05) + w, y]], 6 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); }); - band(g2, W2, gy - 20 * u, H2, "#c9b48a", 0.32, 51); - fence(g2, -20 * u, W2 + 20 * u, gy + 10 * u, 160 * u, 5); - strokesH(g2, W2, gy + 20 * u, H2, 34, "#8a7450", 0.3, 13, u); + for (let i = 0; i < 8; i++) reed(g2, X(-0.98) + i * 34 * u, H2 + 10 * u, (300 + hash(i) * 200) * u, 0, 800 + i, { color: "#2a2220", alpha: 0.85 }); + for (let i = 0; i < 6; i++) reed(g2, X(0.86) + i * 30 * u, H2 + 10 * u, (240 + hash(i + 9) * 160) * u, 0, 820 + i, { color: "#2a2220", alpha: 0.85 }); }, draw(s) { - const { g: g2, u, t, gy, X } = s; - const k = ks(s); - const C = CUE.bullied; - const laugh = t > C.laugh1[0] && t < C.laugh1[1] || t > C.laugh2[0] && t < C.laugh2[1]; - const ga = inv(C.girlIn, C.girlIn + 0.9, t); - if (ga > 0) child(g2, lerp(X(-1.15), X(-0.78), easeOut(ga)), gy + 200 * u, 360 * k, t, { face: 1, pose: t > C.shoo ? "shoo" : "basket", dress: "#b8483e", seed: 4 }); - for (let i = 0; i < 3; i++) { - const x = X(-0.52) + i * 85 * u, y = gy + 150 * u + i % 2 * 20 * u; - const b = laugh ? Math.abs(Math.sin(t * 9 + i)) : 0; - bird(g2, { x, y, s: 92 * k, t: t + i, body: DUCKY, seed: 40 + i, face: 1, mouth: laugh ? 0.4 + 0.6 * b : 0, squash: 0.06 * b, tilt: laugh ? -0.15 : 0, neckUp: laugh ? 0.3 : 0 }); - if (laugh) soundMarks(g2, x + 50 * u, y - 120 * u, 36 * u, 0.8, i); + const { g: g2, H: H2, u, t, gy, X, portrait } = s; + for (let i = 0; i < 5; i++) { + const q2 = inv(0, 10, t); + const x0 = X(-0.75 + i * 0.07), y0 = H2 * (0.3 + 0.03 * i); + const tx = X(0.05) + i * 12 * u, ty = gy - 160 * u - i * 8 * u; + const x = lerp(x0, tx, q2 * 0.85), y = lerp(y0, ty, q2 * 0.85); + farBird(g2, x, y, lerp(28, 9, q2) * u, t * 7 + i * 1.3, "#2a2220", 0.9); + } + const fs = (portrait ? 150 : 130) * u; + const vx = portrait ? X(0.62) : X(0.72), vy = portrait ? H2 * 0.3 : H2 * 0.36; + writeText(g2, "\u76F8\u4FE1\u81EA\u5DF1", vx, vy, fs, 1.4, 0.55, t, { vertical: true, gap: 1.02 }); + const E = CUE.ending; + const ea = smooth(inv(E.end - 0.3, E.end + 0.3, t)); + if (ea > 0) { + g2.save(); + g2.globalAlpha = ea; + g2.font = `${40 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.save(); + g2.translate(vx - fs * 0.95, vy - fs * 1.45); + [..."\u4E11\u5C0F\u9E2D \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); + g2.restore(); + g2.restore(); + } + const sp = inv(E.end + 0.1, E.end + 0.35, t); + if (sp > 0) { + g2.save(); + g2.translate(vx - fs * 0.95, vy + fs * 1.2); + g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); + seal(g2, 0, 0, 80 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + }, + cam(s) { + return { z: lerp(1, 1.05, s.t / s.D), x: s.W / 2, y: s.gy }; + }, + enterAt: [0.5, 0.4] + } + }; + + // src/film/audio.ts + var SR = 44100; + var midi = (m) => 440 * Math.pow(2, (m - 69) / 12); + var N = { E2: 40, G2: 43, A2: 45, B2: 47, D3: 50, E3: 52, G3: 55, A3: 57, B3: 59, D4: 62, E4: 64, G4: 67, A4: 69, B4: 71, D5: 74, E5: 76, G5: 79, A5: 81, B5: 83, D6: 86, E6: 88 }; + var sceneStart = (id) => TL.scenes.find((s) => s.id === id)?.start ?? 0; + var sceneDur = (id) => TL.scenes.find((s) => s.id === id)?.dur ?? 0; + var Mixer = class { + ctx; + music; + sfx; + voice; + rev; + revIn; + master; + noise; + ksCache = /* @__PURE__ */ new Map(); + r = rng(4242); + q = []; + /** 人声轨(供「山谷回声」等效果使用;在 score/effects 之前由 buildSoundtrack 设置) */ + voiceBuf = null; + /** 在 t 时刻真正创建节点(离线渲染按需建节点) */ + at(t, fn) { + this.q.push({ t, fn }); + } + /** 把人声 [t0, t0+dur) 这一段当回声再放几遍:越来越远、越来越暗 */ + echo(t0, dur, delay = 0.42, gain = 0.3, taps = 3) { + this.at(t0 + delay, () => { + if (!this.voiceBuf) return; + for (let k = 1; k <= taps; k++) { + const s = this.ctx.createBufferSource(); + s.buffer = this.voiceBuf; + const f = this.ctx.createBiquadFilter(); + f.type = "lowpass"; + f.frequency.value = 2800 / k; + const g2 = this.ctx.createGain(); + g2.gain.value = gain * Math.pow(0.5, k - 1); + s.connect(f); + f.connect(g2); + this.out(g2, this.sfx, k % 2 ? -0.45 : 0.45, 0.6); + s.start(t0 + delay * k + 1 / 30, t0, dur); + } + }); + } + constructor(ctx) { + this.ctx = ctx; + const self = this; + for (const k of ["flute", "pad", "bell", "block", "noiseHit", "tone", "wind", "quack", "swanCall"]) { + const orig = self[k].bind(this); + self[k] = (...a) => { + this.q.push({ t: a[0], fn: () => orig(...a) }); + }; + } + this.master = ctx.createGain(); + this.master.gain.value = 0.9; + const comp = ctx.createDynamicsCompressor(); + comp.threshold.value = -10; + comp.knee.value = 6; + comp.ratio.value = 6; + comp.attack.value = 4e-3; + comp.release.value = 0.2; + this.master.connect(comp); + comp.connect(ctx.destination); + this.music = ctx.createGain(); + this.music.connect(this.master); + this.sfx = ctx.createGain(); + this.sfx.gain.value = 0.9; + this.sfx.connect(this.master); + this.voice = ctx.createGain(); + this.voice.gain.value = 1; + this.voice.connect(this.master); + this.rev = ctx.createConvolver(); + this.rev.buffer = this.impulse(2.3, 2.4); + this.revIn = ctx.createGain(); + this.revIn.gain.value = 0.55; + this.revIn.connect(this.rev); + this.rev.connect(this.master); + this.noise = ctx.createBuffer(1, SR * 2, SR); + const d = this.noise.getChannelData(0), nr = rng(99); + for (let i = 0; i < d.length; i++) d[i] = nr() * 2 - 1; + } + impulse(sec, decay) { + const b = this.ctx.createBuffer(2, Math.floor(SR * sec), SR), r = rng(7); + for (let c2 = 0; c2 < 2; c2++) { + const d = b.getChannelData(c2); + for (let i = 0; i < d.length; i++) d[i] = (r() * 2 - 1) * Math.pow(1 - i / d.length, decay) * (i < 400 ? i / 400 : 1); + } + return b; + } + out(node, bus, pan = 0, send = 0.25) { + const p = this.ctx.createStereoPanner(); + p.pan.value = pan; + node.connect(p); + p.connect(bus); + if (send > 0) { + const s = this.ctx.createGain(); + s.gain.value = send; + p.connect(s); + s.connect(this.revIn); + } + } + // ---------- 乐器 ---------- + ks(freq) { + const key = Math.round(freq * 10); + let b = this.ksCache.get(key); + if (b) return b; + const len = Math.floor(SR * 3), per = Math.max(2, Math.round(SR / freq)); + b = this.ctx.createBuffer(1, len, SR); + const d = b.getChannelData(0), r = rng(key); + const buf = new Float32Array(per); + for (let i = 0; i < per; i++) buf[i] = (r() * 2 - 1) * (1 - i / per * 0.5); + let idx = 0, last = 0; + const damp = freq > 600 ? 0.994 : 0.997; + for (let i = 0; i < len; i++) { + const cur = buf[idx]; + const nxt = buf[(idx + 1) % per]; + const v = damp * 0.5 * (cur + nxt); + buf[idx] = v; + d[i] = cur * 0.8 + last * 0.2; + last = cur; + idx = (idx + 1) % per; + } + this.ksCache.set(key, b); + return b; + } + // 拨弦直接在 JS 里叠加到采样缓冲(比每个音符建节点快一个数量级) + pl = []; + pluck(t, m, vel = 0.5, pan = 0, o = {}) { + if (t < 0 || t > TOTAL + 2) return; + if (!this.pl.length) { + const n2 = this.ctx.length; + this.pl = [new Float32Array(n2), new Float32Array(n2)]; + } + const src = this.ks(midi(m)).getChannelData(0); + const dur = o.dur ?? 2.6; + const i0 = Math.floor(t * SR), n = Math.min(src.length - 2, Math.floor((dur + 0.6) * SR)); + const gl = vel * Math.cos((pan + 1) * Math.PI / 4), gr = vel * Math.sin((pan + 1) * Math.PI / 4); + const relStart = dur * 0.7 * SR, tau = dur * 0.15 * SR; + const a = Math.exp(-2 * Math.PI * (2400 + (o.bright ?? 0.5) * 4e3) / SR); + const L = this.pl[0], R = this.pl[1]; + let pos = 0, lp3 = 0; + const bendTo = o.bend ? Math.pow(2, o.bend / 12) : 1; + const b0 = 0.12 * SR, b1 = 0.32 * SR; + for (let k = 0; k < n; k++) { + const idx = i0 + k; + if (idx >= L.length) break; + const rate = o.bend ? k < b0 ? 1 : k > b1 ? bendTo : 1 + (bendTo - 1) * (k - b0) / (b1 - b0) : 1; + const p = Math.floor(pos), f = pos - p; + if (p + 1 >= src.length) break; + const x = src[p] * (1 - f) + src[p + 1] * f; + pos += rate; + lp3 = x + a * (lp3 - x); + const env = k < relStart ? 1 : Math.exp(-(k - relStart) / tau); + const v = lp3 * env; + L[idx] += v * gl; + R[idx] += v * gr; + } + } + flushPlucks() { + if (!this.pl.length) return; + const b = this.ctx.createBuffer(2, this.ctx.length, SR); + b.copyToChannel(this.pl[0], 0); + b.copyToChannel(this.pl[1], 1); + const s = this.ctx.createBufferSource(); + s.buffer = b; + const g2 = this.ctx.createGain(); + g2.gain.value = 1; + s.connect(g2); + this.out(g2, this.music, 0, 0.3); + s.start(0); + this.pl = []; + } + flute(t, m, dur, vel = 0.22, pan = 0.1) { + const c2 = this.ctx, fr = midi(m); + const o1 = c2.createOscillator(); + o1.type = "sine"; + o1.frequency.value = fr; + const o2 = c2.createOscillator(); + o2.type = "triangle"; + o2.frequency.value = fr * 2; + const lfo = c2.createOscillator(); + lfo.frequency.value = 5.3; + const lg = c2.createGain(); + lg.gain.setValueAtTime(0, t); + lg.gain.linearRampToValueAtTime(fr * 7e-3, t + Math.min(dur, 0.6)); + lfo.connect(lg); + lg.connect(o1.frequency); + lg.connect(o2.frequency); + const g2 = c2.createGain(); + g2.gain.value = 0.12; + const g3 = c2.createGain(); + g3.gain.setValueAtTime(0, t); + g3.gain.linearRampToValueAtTime(vel, t + 0.07); + g3.gain.setValueAtTime(vel * 0.85, t + Math.max(0.08, dur - 0.12)); + g3.gain.linearRampToValueAtTime(0, t + dur + 0.18); + const n = c2.createBufferSource(); + n.buffer = this.noise; + n.loop = true; + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = fr * 2.2; + bp.Q.value = 1.2; + const ng = c2.createGain(); + ng.gain.setValueAtTime(0, t); + ng.gain.linearRampToValueAtTime(vel * 0.16, t + 0.05); + ng.gain.linearRampToValueAtTime(vel * 0.04, t + 0.25); + ng.gain.linearRampToValueAtTime(0, t + dur + 0.1); + n.connect(bp); + bp.connect(ng); + this.out(ng, this.music, pan, 0.4); + o1.connect(g3); + o2.connect(g2); + g2.connect(g3); + this.out(g3, this.music, pan, 0.45); + for (const o of [o1, o2, lfo]) { + o.start(t); + o.stop(t + dur + 0.3); + } + n.start(t, t * 0.37 % 1.5); + n.stop(t + dur + 0.2); + } + pad(t0, t1, notes, vel = 0.06, cutoff = 900) { + const c2 = this.ctx; + const f = c2.createBiquadFilter(); + f.type = "lowpass"; + f.frequency.value = cutoff; + const g2 = c2.createGain(); + const att = Math.min(1.6, (t1 - t0) / 3); + g2.gain.setValueAtTime(0, t0); + g2.gain.linearRampToValueAtTime(vel, t0 + att); + g2.gain.setValueAtTime(vel, Math.max(t0 + att, t1 - att)); + g2.gain.linearRampToValueAtTime(0, t1); + f.connect(g2); + this.out(g2, this.music, 0, 0.5); + for (const m of notes) for (const det of [-6, 6]) { + const o = c2.createOscillator(); + o.type = "triangle"; + o.frequency.value = midi(m); + o.detune.value = det; + o.connect(f); + o.start(t0); + o.stop(t1 + 0.1); + } + } + bell(t, m, vel = 0.15, pan = 0, bus) { + const c2 = this.ctx, fr = midi(m); + const g2 = c2.createGain(); + g2.gain.value = 1; + [[1, 1, 2.5], [2.76, 0.4, 1.2], [5.4, 0.22, 0.6], [8.93, 0.1, 0.35]].forEach(([k, a, d]) => { + const o = c2.createOscillator(); + o.type = "sine"; + o.frequency.value = fr * k; + const og = c2.createGain(); + og.gain.setValueAtTime(0, t); + og.gain.linearRampToValueAtTime(vel * a, t + 5e-3); + og.gain.exponentialRampToValueAtTime(1e-4, t + d); + o.connect(og); + og.connect(g2); + o.start(t); + o.stop(t + d + 0.05); + }); + this.out(g2, bus ?? this.music, pan, 0.5); + } + block(t, vel = 0.2, fr = 950, pan = 0) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = "sine"; + o.frequency.setValueAtTime(fr, t); + o.frequency.exponentialRampToValueAtTime(fr * 0.6, t + 0.05); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(vel, t); + g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.09); + o.connect(g2); + this.out(g2, this.music, pan, 0.15); + o.start(t); + o.stop(t + 0.12); + } + gliss(t, notes, step = 0.06, vel = 0.3, pan = 0) { + notes.forEach((m, i) => this.pluck(t + i * step, m, vel * (0.7 + 0.3 * i / notes.length), pan + (i / notes.length - 0.5) * 0.6, { dur: 2.2 })); + } + melody(t, beat, seq, vel = 0.22, oct = 0, inst = "flute") { + let x = t; + for (const [n, d] of seq) { + if (n !== "-") { + if (inst === "flute") this.flute(x, N[n] + oct, d * beat * 0.96, vel); + else this.pluck(x, N[n] + oct, vel, 0.15, { bend: d >= 2 ? this.r() < 0.5 ? 0 : 0 : 0 }); } - const ra = inv(C.roosterIn, C.roosterIn + 0.6, t); - const pecks = C.pecks; - let peck = 0, pushed = 0; - for (const p of pecks) { - const q2 = inv(p - 0.12, p + 0.12, t); - peck = Math.max(peck, Math.sin(Math.PI * q2)); - if (t > p) pushed += 1; + x += d * beat; + } + return x; + } + // ---------- 音效 ---------- + noiseHit(t, dur, type, f0, f1, vel, pan = 0, q2 = 1, send = 0.2, bus) { + const c2 = this.ctx; + const s = c2.createBufferSource(); + s.buffer = this.noise; + s.loop = true; + const f = c2.createBiquadFilter(); + f.type = type; + f.Q.value = q2; + f.frequency.setValueAtTime(f0, t); + f.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + Math.min(0.01, dur * 0.2)); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); + s.connect(f); + f.connect(g2); + this.out(g2, bus ?? this.sfx, pan, send); + s.start(t, t * 0.731 % 1.8); + s.stop(t + dur + 0.05); + } + tone(t, f0, f1, dur, vel, type = "sine", pan = 0, send = 0.2) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = type; + o.frequency.setValueAtTime(f0, t); + o.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 8e-3); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); + o.connect(g2); + this.out(g2, this.sfx, pan, send); + o.start(t); + o.stop(t + dur + 0.05); + } + wind(t, dur, vel, pan = 0) { + const c2 = this.ctx; + const s = c2.createBufferSource(); + s.buffer = this.noise; + s.loop = true; + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.Q.value = 0.8; + const lfo = c2.createOscillator(); + lfo.frequency.value = 0.23; + const lg = c2.createGain(); + lg.gain.value = 260; + f.frequency.value = 520; + lfo.connect(lg); + lg.connect(f.frequency); + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + Math.min(1.5, dur / 3)); + g2.gain.setValueAtTime(vel, t + dur - Math.min(1.5, dur / 3)); + g2.gain.linearRampToValueAtTime(0, t + dur); + s.connect(f); + f.connect(g2); + this.out(g2, this.sfx, pan, 0.1); + s.start(t); + s.stop(t + dur + 0.1); + lfo.start(t); + lfo.stop(t + dur + 0.1); + } + chirp(t, vel = 0.06, pan = 0) { + const base = 2600 + this.r() * 1600; + for (let i = 0; i < 2 + Math.floor(this.r() * 3); i++) this.tone(t + i * 0.09, base, base * (1.25 + this.r() * 0.2), 0.06, vel, "sine", pan, 0.3); + } + peep(t, vel = 0.12, pan = 0) { + this.tone(t, 2100, 2900, 0.09, vel, "triangle", pan, 0.15); + this.tone(t + 0.13, 2200, 3e3, 0.08, vel * 0.8, "triangle", pan, 0.15); + } + quack(t, vel = 0.14, pan = 0, pitch = 1) { + const c2 = this.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(330 * pitch, t); + o.frequency.linearRampToValueAtTime(250 * pitch, t + 0.16); + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.frequency.value = 1150 * pitch; + f.Q.value = 3.2; + const f2 = c2.createBiquadFilter(); + f2.type = "peaking"; + f2.frequency.value = 2400; + f2.gain.value = 8; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.015); + g2.gain.setValueAtTime(vel, t + 0.11); + g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.2); + o.connect(f); + f.connect(f2); + f2.connect(g2); + this.out(g2, this.sfx, pan, 0.15); + o.start(t); + o.stop(t + 0.25); + } + crack(t, vel = 0.25, pan = 0, big = false) { + const n = big ? 7 : 3; + for (let i = 0; i < n; i++) this.noiseHit(t + i * (0.018 + this.r() * 0.03), 0.03 + this.r() * 0.03, "highpass", 2500 + this.r() * 2500, 1800, vel * (0.6 + this.r() * 0.5), pan, 0.7, 0.1); + if (big) this.tone(t, 160, 70, 0.25, vel * 0.9, "sine", pan, 0.2); + } + flap(t, n, rate, vel = 0.12, pan = 0) { + for (let i = 0; i < n; i++) this.noiseHit(t + i / rate, 0.12, "bandpass", 900, 300, vel * (0.8 + this.r() * 0.3), pan, 0.9, 0.1); + } + thump(t, vel = 0.4, f = 95) { + this.tone(t, f, f * 0.55, 0.22, vel, "sine", 0, 0.15); + this.noiseHit(t, 0.05, "lowpass", 1200, 300, vel * 0.5, 0, 0.7, 0.05); + } + shot(t, pan = 0.5) { + this.noiseHit(t, 0.5, "lowpass", 6e3, 200, 0.85, pan, 0.5, 0.6); + this.tone(t, 120, 40, 0.5, 0.6, "sine", pan, 0.6); + this.noiseHit(t + 0.05, 1.6, "lowpass", 900, 120, 0.18, pan, 0.6, 0.9); + } + swanCall(t, vel = 0.08, pan = -0.3) { + const c2 = this.ctx; + for (const [dt, f] of [[0, 520], [0.32, 620]]) { + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(f, t + dt); + o.frequency.linearRampToValueAtTime(f * 0.94, t + dt + 0.28); + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = 1300; + bp.Q.value = 4; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t + dt); + g2.gain.linearRampToValueAtTime(vel, t + dt + 0.04); + g2.gain.exponentialRampToValueAtTime(1e-4, t + dt + 0.3); + o.connect(bp); + bp.connect(g2); + this.out(g2, this.sfx, pan, 0.8); + o.start(t + dt); + o.stop(t + dt + 0.35); + } + } + splash(t, vel = 0.35) { + this.noiseHit(t, 0.6, "lowpass", 3e3, 400, vel, 0, 0.6, 0.3); + for (let i = 0; i < 6; i++) this.tone(t + 0.08 + this.r() * 0.4, 900 + this.r() * 900, 1500 + this.r() * 900, 0.05, vel * 0.2, "sine", this.r() - 0.5, 0.3); + } + heart(t) { + this.tone(t, 70, 50, 0.16, 0.28, "sine", 0, 0.05); + this.tone(t + 0.2, 64, 48, 0.14, 0.2, "sine", 0, 0.05); + } + clap(t, vel = 0.08, pan = -0.5) { + this.noiseHit(t, 0.06, "bandpass", 1600 + this.r() * 600, 900, vel, pan, 1.5, 0.2); + } + swish(t, vel = 0.08) { + this.noiseHit(t, 0.35, "bandpass", 2500, 900, vel, 0, 1, 0.25); + } + drop(t) { + this.tone(t, 700, 1500, 0.12, 0.3, "sine", 0, 0.5); + this.noiseHit(t, 0.08, "highpass", 3e3, 2e3, 0.1, 0, 0.7, 0.4); + } + lap(t0, t1, vel = 0.05) { + for (let t = t0; t < t1; t += 0.6 + this.r() * 0.9) this.noiseHit(t, 0.5, "bandpass", 700 + this.r() * 300, 300, vel, this.r() * 1.2 - 0.6, 1.5, 0.3); + } + }; + var cache = /* @__PURE__ */ new Map(); + function buildSoundtrack(voiceUrl, onStage) { + const film = FILM; + const hit = cache.get(film.id); + if (hit) return hit; + const cached = (async () => { + const len = Math.ceil(SR * (TOTAL + 0.3)); + const ctx = new OfflineAudioContext(2, len, SR); + onStage?.("\u52A0\u8F7D\u65C1\u767D\u2026"); + let voiceBuf = null; + try { + const ab = await (await fetch(voiceUrl)).arrayBuffer(); + voiceBuf = await ctx.decodeAudioData(ab); + } catch (e) { + console.warn("voice load failed", e); + } + onStage?.("\u5408\u6210\u914D\u4E50\u4E0E\u97F3\u6548\u2026"); + const m = new Mixer(ctx); + m.voiceBuf = voiceBuf; + const mg = m.music.gain; + mg.setValueAtTime(0.9, 0); + for (const s of TL.scenes) { + const spans = s.lines?.length ? s.lines.map((l) => [l.t0, l.t0 + l.dur]) : s.voice_start && s.voice_dur ? [[s.voice_start, s.voice_start + s.voice_dur]] : []; + for (const [a, b] of spans) { + mg.setTargetAtTime(0.42, Math.max(0, a - 0.25), 0.08); + mg.setTargetAtTime(0.9, b + 0.1, 0.25); } - const ux = X(0.1) - pushed * 30 * u; - const cower = t > C.shoo ? 0.2 : 0.08; - bird(g2, { x: ux, y: gy + 170 * u, s: 140 * k, t, sw: 0.16, body: UGLY, beak: UGLY_BEAK, fluff: 1.1, seed: 7, face: t > C.girlIn ? -1 : 1, neckUp: -0.45, shiver: t > C.pecks[0] ? 1 : 0.4, squash: cower }); - tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(1.2, 1.5, t)) * (1 - smooth(inv(3.4, 3.7, t)))); - tear(g2, ux + (t > C.girlIn ? -1 : 1) * 40 * u, gy + 60 * u + t * 30 % 30 * u, 9 * u, smooth(inv(7.6, 7.9, t)) * (1 - smooth(inv(9.6, 10, t)))); - if (ra > 0) { - const rx = lerp(X(1.3), X(0.42), easeOut(ra)) - (t > C.pecks[3] + 0.3 ? (t - C.pecks[3] - 0.3) * 20 * u : 0); - rooster(g2, rx, gy + 175 * u, 240 * k, t, { face: -1, peck, flap: t > C.roosterIn + 0.6 && t < C.pecks[3] + 0.5 ? t * 18 : 0 }); + } + film.score(m); + film.effects(m); + m.flushPlucks(); + m.q.sort((a, b) => a.t - b.t); + let qi = 0; + const until = (tt) => { + while (qi < m.q.length && m.q[qi].t < tt) { + m.q[qi].fn(); + qi++; } - pecks.forEach((p, j) => { - const q2 = t - p; - if (q2 < 0 || q2 > 2) return; - for (let m = 0; m < 3; m++) { - const x = ux + 30 * u + (m - 1) * 30 * u + Math.sin(q2 * 4 + m) * 20 * u; - const y = gy + 40 * u - 80 * u * easeOut(q2 * 2) + q2 * q2 * 40 * u; - leaf(g2, x, y, 10 * u, q2 * 3 + m + j, "#9a968e", 0.8 * (1 - q2 / 2)); - } + }; + until(1.5); + for (let k = 1; k < TOTAL + 0.3; k++) { + const tt = k; + ctx.suspend(tt).then(() => { + until(tt + 1.5); + ctx.resume(); }); - }, - cam(s) { - return cam(s, lerp(1.28, 1.36, easeInOut(s.t / s.D)), s.X(-0.08), s.gy + 110 * s.u); - }, - enterAt: [0.3, 0.6] - }, - leave: { - label: "\u79BB\u5BB6", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#b9a48a", 0.2, "#ffffff", 0); - mountains(g2, W2, gy - 70 * u, 140 * u, "#8a8478", 0.3, 23); - mountains(g2, W2, gy - 20 * u, 90 * u, "#6a665e", 0.35, 29); - band(g2, W2, gy - 20 * u, H2, "#b8ad8a", 0.28, 61); - const path = [[X(-0.75), H2 + 20 * u], [X(-0.42), gy + 170 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 30 * u], [X(0.68), gy - 25 * u]]; - const pts = spline(path, 10); - for (let i = 0; i < pts.length - 1; i++) { - const f = i / pts.length; - brush(g2, [pts[i], pts[i + 1]], lerp(150, 16, f) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); - } - tree(g2, X(0.8), gy - 10 * u, 380 * u, 33, { depth: 4, leaves: (x, y, l) => blot(g2, x, y, l * 0.8, "#8a7a50", 0.25) }); - fence(g2, -20 * u, X(-0.12), gy + 120 * u, 170 * u, 8, X(-0.42), 120 * u); - strokesH(g2, W2, gy + 30 * u, H2, 30, "#7a6a48", 0.28, 15, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X, D } = s; - const p = Math.pow(inv(CUE.leave.walk, D + 1.2, t), 1.15); - const path = spline([[X(-0.62), gy + 200 * u], [X(-0.42), gy + 175 * u], [X(-0.05), gy + 100 * u], [X(0.35), gy + 32 * u], [X(0.62), gy - 20 * u]], 12); - const idx = Math.min(path.length - 1, Math.floor(p * (path.length - 1))); - const [bx, by] = path[idx]; - const sc = lerp(150, 52, p) * ks(s); - bird(g2, { x: bx, y: by, s: sc, t, sw: 0.18, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: 1, walk: t > CUE.leave.walk ? 1.7 : 0, neckUp: -0.35, shiver: t < CUE.leave.walk ? 0.4 : 0 }); - tear(g2, bx + 45 * u * (sc / 150), by - 95 * u * (sc / 150) + t * 25 % 25 * u, 9 * u, smooth(inv(0.3, 0.6, t)) * (1 - smooth(inv(2.2, 2.6, t)))); - for (let i = 0; i < 14; i++) grass(g2, i / 14 * W2 + hash(i) * 50 * u, H2 - hash(i * 3) * 120 * u, 70 * u, t, 200 + i, "#5d6a44", 0.6, 2); - fallingThings(g2, W2, H2, t, 14, 8, u, (x, y, rot, r) => leaf(g2, x, y * 0.8, (8 + r() * 6) * u, rot, r() < 0.5 ? "#b08a4a" : "#8a7a50", 0.75), 50); - }, - cam(s) { - return cam(s, lerp(1.08, 1.16, s.t / s.D), s.W / 2, s.gy + 60 * s.u); - }, - enterAt: [0.25, 0.7] + } + if (voiceBuf) { + const v = ctx.createBufferSource(); + v.buffer = voiceBuf; + const vg = ctx.createGain(); + vg.gain.value = 1.15; + const hp = ctx.createBiquadFilter(); + hp.type = "highpass"; + hp.frequency.value = 80; + v.connect(hp); + hp.connect(vg); + vg.connect(m.voice); + v.start(1 / 30); + } + const out = await ctx.startRendering(); + onStage?.("\u54CD\u5EA6\u6807\u51C6\u5316\uFF08\u221214 LUFS\uFF09\u2026"); + normalize(out, -14, 0.84); + onStage?.("\u5B8C\u6210"); + return out; + })(); + cache.set(film.id, cached); + cached.catch(() => cache.delete(film.id)); + return cached; + } + function toWav(b) { + const ch = b.numberOfChannels, len = b.length, sr = b.sampleRate; + const buf = new ArrayBuffer(44 + len * ch * 2), v = new DataView(buf); + const w = (o2, s) => { + for (let i = 0; i < s.length; i++) v.setUint8(o2 + i, s.charCodeAt(i)); + }; + w(0, "RIFF"); + v.setUint32(4, 36 + len * ch * 2, true); + w(8, "WAVE"); + w(12, "fmt "); + v.setUint32(16, 16, true); + v.setUint16(20, 1, true); + v.setUint16(22, ch, true); + v.setUint32(24, sr, true); + v.setUint32(28, sr * ch * 2, true); + v.setUint16(32, ch * 2, true); + v.setUint16(34, 16, true); + w(36, "data"); + v.setUint32(40, len * ch * 2, true); + const data = Array.from({ length: ch }, (_, c2) => b.getChannelData(c2)); + let o = 44; + for (let i = 0; i < len; i++) for (let c2 = 0; c2 < ch; c2++) { + const s = Math.max(-1, Math.min(1, data[c2][i])); + v.setInt16(o, s < 0 ? s * 32768 : s * 32767, true); + o += 2; + } + return buf; + } + function loudness(b) { + const fs = b.sampleRate; + const biquad = (x, b0, b1, b2, a1, a2) => { + const y = new Float32Array(x.length); + let x1 = 0, x2 = 0, y1 = 0, y2 = 0; + for (let i = 0; i < x.length; i++) { + const v = b0 * x[i] + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + x2 = x1; + x1 = x[i]; + y2 = y1; + y1 = v; + y[i] = v; + } + return y; + }; + let K = Math.tan(Math.PI * 1681.974450955533 / fs), Q = 0.7071752369554196; + const Vh = Math.pow(10, 3.999843853973347 / 20), Vb = Math.pow(Vh, 0.4996667741545416); + let a0 = 1 + K / Q + K * K; + const s1 = [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; + K = Math.tan(Math.PI * 38.13547087602444 / fs); + Q = 0.5003270373238773; + a0 = 1 + K / Q + K * K; + const s2 = [1, -2, 1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; + const chans = Array.from({ length: b.numberOfChannels }, (_, c2) => { + const y = biquad(b.getChannelData(c2), s1[0], s1[1], s1[2], s1[3], s1[4]); + return biquad(y, s2[0], s2[1], s2[2], s2[3], s2[4]); + }); + const blk = Math.round(0.4 * fs), hop = Math.round(0.1 * fs), z = []; + for (let st = 0; st + blk <= b.length; st += hop) { + let sum = 0; + for (const y of chans) { + let e = 0; + for (let i = st; i < st + blk; i++) e += y[i] * y[i]; + sum += e / blk; + } + z.push(sum); + } + const L = (e) => -0.691 + 10 * Math.log10(e + 1e-12); + const abs = z.filter((e) => L(e) > -70); + if (!abs.length) return -70; + const rel = L(abs.reduce((a, e) => a + e, 0) / abs.length) - 10; + const gated = abs.filter((e) => L(e) > rel); + return L(gated.reduce((a, e) => a + e, 0) / Math.max(1, gated.length)); + } + function normalize(b, target = -14, ceiling = 0.84) { + const L = loudness(b); + const g2 = Math.pow(10, (target - L) / 20); + const n = b.length, ch = Array.from({ length: b.numberOfChannels }, (_, c2) => b.getChannelData(c2)); + const want = new Float32Array(n); + for (let i = 0; i < n; i++) { + let m = 0; + for (const d of ch) { + d[i] *= g2; + const a = Math.abs(d[i]); + if (a > m) m = a; + } + want[i] = m > ceiling ? ceiling / m : 1; + } + const att = 1 / (4e-3 * b.sampleRate), rel = 1 / (0.12 * b.sampleRate); + for (let i = n - 2; i >= 0; i--) want[i] = Math.min(want[i], want[i + 1] + att); + let cur = 1; + for (let i = 0; i < n; i++) { + cur = Math.min(want[i], cur + rel); + for (const d of ch) d[i] *= cur; + } + return { before: L, gain: g2 }; + } + + // src/films/duckling/music.ts + function score(m) { + const B2 = 60 / 72; + const S = sceneStart, D2 = sceneDur; + const r = rng(808); + const arp2 = (t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) => { + let i = 0; + for (let t2 = t0; t2 < t1 - 0.05; t2 += step, i++) { + const ch = chords[Math.floor((t2 - t0) / (step * 8)) % chords.length]; + m.pluck(t2, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.8 }); + } + }; + const G2 = [N.G3, N.D4, N.B4], Em2 = [N.E3, N.B3, N.G4], Dm = [N.D3, N.A3, N.D4], Am2 = [N.A2, N.E3, N.A3], Gh2 = [N.G4, N.B4, N.D5]; + let t = S("title"); + m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5], 0.07, 0.28); + m.pad(t + 0.5, S("summer") + 1, [N.G2, N.D3], 0.05); + m.melody(t + 2.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1.5]], 0.17); + t = S("summer"); + m.pad(t, S("hatch") + 0.5, [N.G2, N.D3, N.B3], 0.045); + arp2(t + 0.3, S("hatch"), [G2, Em2, G2, Dm], B2 / 2, 0.2); + m.melody(t + 1.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1], ["B4", 1], ["D5", 0.5], ["B4", 0.5], ["A4", 2], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.13); + t = S("hatch"); + for (let i = 0; i < Math.floor(D2("hatch") / B2); i++) { + if (i % 2 === 0) m.block(t + i * B2, 0.07, 1100, -0.3); + m.pluck(t + i * B2 + B2 / 2, [N.G4, N.B4, N.D5, N.E5][i % 4], 0.13, 0.3, { dur: 0.5, bright: 0.9 }); + } + m.pad(t, S("bigegg"), [N.G2, N.D3], 0.035); + t = S("bigegg"); + const BE = CUE.bigegg; + m.pad(t, t + BE.open, [N.E2, N.B2], 0.05, 500); + for (let x = t + 0.6; x < t + BE.open - 0.05; x += 0.09) m.pluck(x, N.E3, 0.06 + 0.1 * (x - t) / BE.open, 0, { dur: 0.3, send: 0.1 }); + m.pluck(t + BE.surprise, N.D4, 0.3, 0, { bend: -2 }); + m.pluck(t + BE.surprise + 0.25, N.A3, 0.3, 0, { bend: -2 }); + m.pluck(t + BE.question + 0.05, N.B4, 0.22, 0.2, { bend: 2 }); + arp2(t + BE.walk, S("bullied"), [Em2, G2], B2 / 2, 0.12); + t = S("bullied"); + m.pad(t, S("leave") + 0.5, [N.E2, N.B2, N.G3], 0.05, 700); + for (let i = 0; i < 12; i++) m.pluck(t + 0.3 + i * B2, [N.E4, N.D4, N.B3, N.A3, N.G3, N.A3, N.B3, N.E3][i % 8], 0.16, -0.2, { dur: 2, bend: i % 4 === 3 ? -1 : 0 }); + t = S("leave"); + m.pad(t, S("marsh") + 0.3, [N.E2, N.B2], 0.045, 600); + m.melody(t + 0.4, B2 * 1.1, [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["E4", 2]], 0.13); + t = S("marsh"); + for (let x = t + 0.2; x < t + CUE.marsh.shots[0] - 0.02; x += 0.125) m.pluck(x, Math.floor((x - t) / 0.5) % 2 ? N.B2 : N.E2, 0.17, 0, { dur: 0.25, send: 0.05 }); + m.pad(t + CUE.marsh.hide, S("autumn") + 0.5, [N.E2], 0.04, 400); + for (let x = t + CUE.marsh.hide + 0.6; x < t + 13; x += 0.95) m.heart(x); + t = S("autumn"); + m.pad(t, S("winter") + 0.5, [N.E2, N.B2, N.G3], 0.04, 800); + arp2(t + 0.3, S("winter"), [Em2, Am2, G2, Em2], B2 * 0.75, 0.13); + m.bell(t + CUE.autumn.swans + 0.6, N.B5, 0.07, 0.4); + m.bell(t + CUE.autumn.swans + 3, N.E6, 0.06, -0.3); + m.melody(t + CUE.autumn.swans + 1.4, B2, [["B4", 1.5], ["D5", 0.5], ["E5", 1], ["G5", 1], ["E5", 2], ["D5", 1], ["B4", 1], ["A4", 2]], 0.11, 0); + t = S("winter"); + m.pad(t, S("spring") + 0.5, [N.E2, N.B2], 0.035, 350); + for (let i = 0; i < 6; i++) m.bell(t + 1 + i * 2.1, [N.E5, N.B4, N.D5, N.A4, N.B4, N.E5][i], 0.05, i % 2 ? 0.4 : -0.4); + t = S("spring"); + m.bell(t + 0.15, N.G5, 0.1, 0); + m.bell(t + 0.4, N.D6, 0.07, 0.3); + m.pad(t, S("reflection") + 0.5, [N.G2, N.D3, N.B3], 0.045); + const SP = CUE.spring; + arp2(t + 0.5, t + SP.flapHard, [G2, Em2], B2 / 2, 0.15); + arp2(t + SP.flapHard, S("reflection"), [G2, Dm, Em2, G2], B2 / 4, 0.14, [0, 1, 2, 1, 2, 1]); + m.gliss(t + SP.lift - 0.05, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5, N.E5, N.G5], 0.05, 0.25); + m.melody(t + SP.lift + 0.4, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["E5", 0.5], ["D5", 2]], 0.13); + t = S("reflection"); + const RF = CUE.reflection; + m.pad(t, t + RF.reveal, [N.E2, N.B2, N.E3], 0.04, 600); + for (let i = 0; i < 10; i++) m.pluck(t + 0.8 + i * B2 * 1.05, [N.B3, N.D4, N.E4, N.D4, N.B3, N.A3, N.B3, N.D4, N.E4, N.G4][i], 0.12, 0, { dur: 2 }); + m.bell(t + RF.mirror, N.E5, 0.06, 0); + m.bell(t + RF.mirror + 0.3, N.B5, 0.04, 0.3); + m.gliss(t + RF.reveal - 0.05, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5, N.B5, N.D6], 0.06, 0.3); + [N.G5, N.B5, N.D6, N.G5].forEach((x, i) => m.bell(t + RF.reveal + i * 0.18, x, 0.08, i % 2 ? 0.3 : -0.3)); + m.pad(t + RF.reveal, S("accepted") + 0.5, [N.G2, N.D3, N.B3, N.D4], 0.05, 1200); + m.melody(t + RF.reveal + 1.2, B2, [["G5", 1.5], ["E5", 0.5], ["D5", 1], ["B4", 1], ["D5", 2]], 0.14); + t = S("accepted"); + arp2(t + 0.2, S("fly"), [G2, Em2, Dm, G2], B2 / 2, 0.15); + m.pad(t, S("fly") + 0.3, [N.G2, N.D3], 0.04); + m.melody(t + 0.8, B2, [["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.12); + t = S("fly"); + m.pad(t, S("ending") + 1, [N.G2, N.D3, N.G3, N.B3], 0.06, 1600); + arp2(t, S("ending"), [Gh2, G2, Em2, Gh2], B2 / 4, 0.16, [0, 1, 2, 1, 2, 1, 0, 2]); + for (let i = 0; i < Math.floor(D2("fly") / B2); i++) m.block(t + i * B2, 0.08, 800, 0); + m.melody(t + 0.2, B2, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["A5", 0.5], ["B5", 2]], 0.15, 0); + t = S("ending"); + m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); + const EN = CUE.ending.end; + arp2(t + 0.2, t + EN - 0.2, [G2, Em2, G2, Dm], B2 * 0.75, 0.12); + m.gliss(t + EN, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.22); + m.bell(t + EN + 0.1, N.G5, 0.08); + m.bell(t + EN + 0.35, N.D6, 0.05, 0.3); + m.pluck(t + EN + 0.6, N.G3, 0.2, 0, { dur: 2 }); + m.pluck(t + EN + 0.6, N.D4, 0.15, 0, { dur: 2 }); + m.pluck(t + EN + 0.6, N.G4, 0.12, 0, { dur: 2 }); + void r; + } + function effects(m) { + const S = sceneStart, D2 = sceneDur; + const r = m.r; + let t = S("title"); + const TT = CUE.title; + m.drop(t + TT.drop); + m.swish(t + TT.write); + m.swish(t + TT.write + 0.45); + m.swish(t + TT.write + 0.9); + m.thump(t + TT.seal + 0.01, 0.45); + for (let i = 0; i < 6; i++) m.peep(t + 2.6 + i * 0.65, 0.04, 0.3); + t = S("summer"); + m.wind(t, D2("summer"), 0.025); + for (let x = t + 0.6; x < t + D2("summer"); x += 1.1 + r() * 1.6) m.chirp(x, 0.035, r() * 1.4 - 0.7); + t = S("hatch"); + for (let k = 0; k < 4; k++) { + const tc = t + 0.4 + k * 0.75; + m.crack(tc + 0.05, 0.22, -0.3 + k * 0.2); + m.crack(tc + 0.25, 0.18, -0.3 + k * 0.2); + m.noiseHit(tc + 0.55, 0.08, "bandpass", 1800, 900, 0.18, -0.3 + k * 0.2, 2, 0.1); + m.peep(tc + 0.75, 0.1, -0.3 + k * 0.2); + for (let p = 1; p < 5; p++) { + const pt = tc + 1 + 2.3 * (p - 1); + if (pt - t < D2("hatch")) m.peep(pt + 0.05, 0.07, -0.3 + k * 0.2); + } + m.noiseHit(tc + 3, 0.05, "lowpass", 600, 200, 0.12, -0.3 + k * 0.2, 0.7, 0.05); + } + t = S("bigegg"); + const BE = CUE.bigegg; + for (let x = t + 0.5; x < t + BE.crackFrom; x += Math.max(0.12, 0.5 - (x - t) * 0.12)) m.block(x, 0.05 + (x - t) * 0.02, 420, 0); + m.crack(t + BE.crackFrom + 0.05, 0.2); + m.crack(t + BE.crackFrom + 0.4, 0.22); + m.crack(t + BE.open, 0.35, 0, true); + m.noiseHit(t + BE.surprise, 0.25, "bandpass", 2400, 1200, 0.06, -0.4, 1.2, 0.2); + m.peep(t + BE.surprise + 0.1, 0.08, -0.4); + m.peep(t + BE.surprise + 0.2, 0.08, -0.5); + for (let x = t + BE.walk + 0.1; x < t + BE.walk + 2.6; x += 0.6) m.noiseHit(x, 0.05, "lowpass", 600, 200, 0.1, 0, 0.7, 0.05); + t = S("bullied"); + const BU = CUE.bullied; + for (let x = t + BU.laugh1[0] + 0.1; x < t + BU.laugh1[1] - 0.2; x += 0.35 + r() * 0.3) m.quack(x, 0.09, -0.5 + r() * 0.3, 1.25 + r() * 0.2); + for (let x = t + BU.laugh2[0] + 0.1; x < t + BU.laugh2[1] - 0.2; x += 0.4 + r() * 0.35) m.quack(x, 0.07, -0.5 + r() * 0.3, 1.25 + r() * 0.2); + m.flap(t + BU.roosterIn, 6, 9, 0.12, 0.5); + m.tone(t + BU.roosterIn + 0.2, 600, 900, 0.25, 0.08, "sawtooth", 0.5, 0.3); + m.tone(t + BU.roosterIn + 0.45, 900, 500, 0.5, 0.07, "sawtooth", 0.5, 0.4); + for (const p of BU.pecks) { + m.block(t + p, 0.25, 1500, 0.2); + m.flap(t + p - 0.15, 2, 10, 0.08, 0.3); + } + m.swish(t + BU.shoo, 0.07); + t = S("leave"); + m.wind(t, D2("leave") + 1, 0.07, -0.2); + for (let x = t + CUE.leave.walk + 0.2; x < t + D2("leave"); x += 0.62) m.noiseHit(x, 0.04, "lowpass", 500, 200, 0.05, 0, 0.7, 0.05); + t = S("marsh"); + m.lap(t, t + D2("marsh"), 0.035); + const MA = CUE.marsh; + for (let x = t + MA.laugh[0] + 0.1; x < t + MA.laugh[1] - 0.2; x += 0.3 + r() * 0.3) m.quack(x, 0.1, 0.4 + r() * 0.3, 0.95 + r() * 0.15); + m.shot(t + MA.shots[0], 0.6); + m.shot(t + MA.shots[1], 0.45); + m.flap(t + MA.scatter, 14, 12, 0.13, 0.4); + for (let i = 0; i < 6; i++) m.quack(t + MA.scatter + 0.05 + i * 0.12, 0.08, 0.5, 1.1); + m.splash(t + MA.hide + 0.2, 0.12); + t = S("autumn"); + m.wind(t, D2("autumn"), 0.03, 0.2); + const AU = CUE.autumn.swans; + m.swanCall(t + AU + 0.8, 0.06, 0.5); + m.swanCall(t + AU + 3, 0.07, 0.1); + m.swanCall(t + AU + 5.6, 0.05, -0.5); + m.flap(t + AU + 1.5, 12, 1.4, 0.04, 0.2); + t = S("winter"); + m.wind(t, D2("winter") + 0.8, 0.12, 0); + m.wind(t + 2, D2("winter") - 2, 0.06, 0.6); + m.crack(t + CUE.winter.ice, 0.2, 0, true); + m.noiseHit(t + CUE.winter.ice + 0.05, 0.9, "bandpass", 300, 120, 0.12, 0, 4, 0.5); + t = S("spring"); + for (let x = t + 0.4; x < t + D2("spring"); x += 0.7 + r() * 1.1) m.chirp(x, 0.04, r() * 1.4 - 0.7); + const SP = CUE.spring; + m.flap(t + SP.spread, 2, 2, 0.1, 0); + m.flap(t + SP.spread + 0.9, Math.floor((SP.flapHard - SP.spread - 0.9) * 0.83), 0.83, 0.08, 0); + m.flap(t + SP.flapHard, Math.floor((SP.lift - SP.flapHard) * 1.6), 1.6, 0.13, 0); + m.flap(t + SP.lift, 6, 1.46, 0.1, 0.2); + m.noiseHit(t + SP.lift - 0.1, 1.2, "bandpass", 400, 2400, 0.12, 0, 1, 0.4); + m.pluck(t + SP.question, N.B4, 0.2, 0, { bend: 2 }); + t = S("reflection"); + const RF = CUE.reflection; + m.flap(t, 5, 7, 0.1, -0.2); + m.splash(t + RF.land, 0.3); + m.lap(t + 1.2, t + D2("reflection"), 0.03); + m.bell(t + RF.reveal, 91, 0.06, 0, m.sfx); + t = S("accepted"); + m.lap(t, t + D2("accepted"), 0.03); + const CH = CUE.accepted.cheer; + for (let x = t + CH + 0.1; x < t + CH + 4.5; x += 0.11 + r() * 0.08) m.clap(x, 0.06 * (1 - (x - t - CH) / 5), -0.55 + r() * 0.2); + for (let i = 0; i < 4; i++) m.chirp(t + CH + 0.4 + i * 1.3, 0.03, 0.4); + t = S("fly"); + m.wind(t, D2("fly") + 1, 0.07, 0); + m.flap(t + 0.3, Math.floor(D2("fly") * 3.2 / 2 / Math.PI * 2), 3.2 / Math.PI, 0.09, 0); + m.noiseHit(t + 0.9, 1.6, "bandpass", 300, 2e3, 0.1, 0, 1, 0.5); + t = S("ending"); + m.lap(t, t + D2("ending"), 0.025); + m.thump(t + CUE.ending.end + 0.12, 0.4); + } + + // src/films/duckling/content.ts + var PLATFORMS = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66", + titleMax: 20, + tagMax: 10, + color: "#ff2442", + titles: ["\u6C34\u58A8\u7248\u4E11\u5C0F\u9E2D\uFF5C\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", "\u4E00\u7B14\u4E00\u753B\u753B\u51FA\u7684\u6C34\u58A8\u4E11\u5C0F\u9E2D", "\u7761\u524D\u6545\u4E8B\uFF5C\u4E11\u5C0F\u9E2D\u53D8\u767D\u5929\u9E45"], + desc: `\u8FD8\u8BB0\u5F97\u5C0F\u5B66\u8BED\u6587\u4E66\u91CC\u7684\u300A\u4E11\u5C0F\u9E2D\u300B\u5417\uFF1F +\u8FD9\u6B21\u628A\u5B83\u753B\u6210\u4E86\u4E00\u90E8\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF1A\u5BA3\u7EB8\u5E95\u3001\u6BDB\u7B14\u52FE\u7EBF\u3001\u6DE1\u5F69\u6655\u67D3\uFF0C\u8FDE\u5B57\u5E55\u90FD\u662F\u6977\u4E66\u3002 + +\u{1F95A} \u6700\u5927\u7684\u90A3\u9897\u86CB\uFF0C\u94BB\u51FA\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19 +\u{1F413} \u88AB\u7B11\u8BDD\u3001\u88AB\u6B3A\u8D1F\uFF0C\u4ED6\u6084\u6084\u79BB\u5F00\u4E86\u5BB6 +\u2744\uFE0F \u71AC\u8FC7\u6700\u51B7\u7684\u51AC\u5929 +\u{1F9A2} \u6625\u5929\u6765\u4E86\uFF0C\u6C34\u91CC\u7684\u5012\u5F71\u662F\u4E00\u53EA\u767D\u5929\u9E45 + +\u9001\u7ED9\u6BCF\u4E00\u4E2A\u89C9\u5F97\u81EA\u5DF1\u300C\u4E0D\u4E00\u6837\u300D\u7684\u5C0F\u670B\u53CB\uFF0C\u4E5F\u9001\u7ED9\u5C0F\u65F6\u5019\u7684\u81EA\u5DF1\uFF1A +\u4E0D\u6015\u98CE\u96EA\uFF0C\u4E5F\u4E0D\u6015\u5632\u7B11\uFF0C\u603B\u6709\u4E00\u5929\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002 + +\u9002\u5408\u5F53\u7761\u524D\u6545\u4E8B\uFF0C\u548C\u5B69\u5B50\u4E00\u8D77\u770B \u{1F440}`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u513F\u7AE5\u6545\u4E8B", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8\u52A8\u753B", "\u4EB2\u5B50\u9605\u8BFB", "\u5C0F\u5B66\u8BED\u6587", "\u7AE5\u8BDD\u6545\u4E8B"], + tip: "\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\uFF1B\u8BDD\u9898\u7C98\u8D34\u540E\u5728 App \u91CC\u70B9\u9009\u4E00\u4E0B\u5373\u53EF\u53D8\u6210\u84DD\u8272\u8BDD\u9898\u3002" }, - marsh: { - label: "\u6CBC\u6CFD", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy } = s; - sky(g2, W2, gy, "#7e878a", 0.25, "#ffffff", 0.02); - mountains(g2, W2, gy - 50 * u, 100 * u, "#6e7672", 0.3, 37, { soft: 1 }); - for (let i = 0; i < 46; i++) reed(g2, i / 46 * W2 + hash(i) * 30 * u, gy - 10 * u, (90 + hash(i * 3) * 90) * u, 0, 500 + i, { color: "#5d6658", alpha: 0.4, wind: 0.2 }); - band(g2, W2, gy - 15 * u, H2, "#8fa3a6", 0.32, 71, 6 * u); - particles(8, 19, (r) => blot(g2, r() * W2, gy + r() * (H2 - gy), (80 + r() * 160) * u, "#5d6a62", 0.12, 2.2, 0.4)); - strokesH(g2, W2, gy + 20 * u, H2, 36, "#3f4c52", 0.35, 17, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const M = CUE.marsh; - water(g2, 0, W2, gy + 10 * u, H2 - gy, t, 3, { alpha: 0.3, lines: 12 }); - for (let i = 0; i < 12; i++) reed(g2, X(0.62) + i * 38 * u, gy + 40 * u + hash(i) * 30 * u, (280 + hash(i * 5) * 160) * u, t, 120 + i, { wind: t > M.shots[0] ? 1 + 2 * Math.exp(-(t - M.shots[0]) * 2) : 1 }); - for (const [ts, fx, fy] of [[M.shots[0], 0.86, -130], [M.shots[1], 0.72, -170]]) { - const q2 = t - ts; - if (q2 > 0 && q2 < 0.09) { - g2.fillStyle = rgba("#e0a040", 0.9); - g2.beginPath(); - g2.arc(X(fx), gy + fy * u, 26 * u, 0, Math.PI * 2); - g2.fill(); - } - if (q2 > 0) smoke(g2, X(fx) - q2 * 30 * u, gy + fy * u - q2 * 25 * u, (30 + 110 * easeOut(q2 / 2)) * u, 0.6 * Math.max(0, 1 - q2 / 4), Math.floor(ts * 10)); - } - for (let i = 0; i < 3; i++) { - const takeoff = easeIn(inv(M.scatter + i * 0.12, M.scatter + 2.1 + i * 0.12, t)); - const bx = X(0.2 + i * 0.16) + takeoff * 380 * u, by = gy + (110 + i * 25) * u - takeoff * (gy + 300 * u); - const laugh = t > M.laugh[0] && t < M.laugh[1]; - const o = { x: bx, y: by, s: 140 * k, t: t + i, sw: 0.32, body: "#9b7b55", head: "#3d6b4a", beak: "#d8b03a", seed: 60 + i, face: takeoff > 0 ? 1 : -1, mouth: laugh ? 0.5 + 0.5 * Math.abs(Math.sin(t * 9 + i)) : 0, fluff: 0 }; - if (takeoff > 0) bird(g2, { ...o, legs: false, wing: 1, flap: t * 16 + i, neckUp: -0.7 }); - else { - swimBird(g2, o); - if (laugh) soundMarks(g2, bx - 70 * u, by - 150 * u, 34 * u, 0.8, i, -1); - } - } - particles(7, 33, (r, i) => { - const q2 = t - M.scatter + 0.1 - r() * 0.4; - if (q2 <= 0) return; - farBird(g2, X(0.8) - q2 * (120 + r() * 120) * u, gy - 120 * u - q2 * (150 + r() * 120) * u, (18 + r() * 12) * u, t * 14 + i); - }); - const hide = easeInOut(inv(M.hide, M.hide + 1.4, t)); - const startle = Math.exp(-Math.max(0, t - M.shots[0]) * 5) * (t > M.shots[0] ? 1 : 0); - const ux = lerp(X(-0.08), X(-0.55), hide), uy = gy + 175 * u; - swimBird(g2, { x: ux, y: uy, s: 150 * k, t, sw: 0.24, body: UGLY, beak: UGLY_BEAK, fluff: 1, seed: 7, face: hide > 0 && hide < 1 ? -1 : 1, neckUp: lerp(0, -0.6, hide), tuck: 0.3 * hide, shiver: t > M.shots[0] ? 1 : 0, squash: startle * 0.2 }); - exclaim(g2, ux + 20 * u, uy - 220 * u, 50 * u, startle > 0.05 ? 1 : 0, 3); - for (let i = 0; i < 9; i++) reed(g2, X(-0.78) + i * 40 * u + hash(i * 9) * 20 * u, H2 + 20 * u, (420 + hash(i * 13) * 220) * u, t, 150 + i, { color: "#2f3b2c", alpha: 0.85, wind: t > M.shots[0] ? 1 + 1.5 * Math.exp(-(t - M.shots[0])) : 1 }); - }, - cam(s) { - const { t, u } = s; - let dx = 0; - for (const ts of CUE.marsh.shots) { - const q2 = t - ts; - if (q2 > 0) dx += Math.sin(q2 * 90) * 10 * u * Math.exp(-q2 * 9); - } - const p = easeInOut(inv(9.2, s.D, t)); - return cam(s, lerp(1.15, 1.55, p), lerp(s.X(0.1), s.X(-0.52), p), s.gy + lerp(90, 120, p) * u, { dx, dy: dx * 0.5 }); - }, - enterAt: [0.5, 0.6] + { + id: "dy", + name: "\u6296\u97F3", + titleMax: 30, + tagMax: 5, + color: "#111111", + titles: ["\u7528\u4EE3\u7801\u4E00\u7B14\u4E00\u753B\uFF0C\u753B\u4E86\u4E00\u90E8\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u300B", "\u50CF\u5C0F\u65F6\u5019\u8BED\u6587\u4E66\u63D2\u56FE\u7684\u4E11\u5C0F\u9E2D\uFF0C\u4F60\u8FD8\u8BB0\u5F97\u5417", "\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\uFF0C\u8FD9\u6B21\u662F\u6C34\u58A8\u624B\u7ED8\u7248"], + desc: `\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u2014\u2014\u628A\u5B89\u5F92\u751F\u7684\u300A\u4E11\u5C0F\u9E2D\u300B\u753B\u6210\u4E86\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\u3002 +\u4ECE\u7834\u58F3\u3001\u88AB\u5632\u7B11\u3001\u71AC\u8FC7\u5BD2\u51AC\uFF0C\u5230\u5728\u6C34\u91CC\u770B\u89C1\u81EA\u5DF1\u7684\u5012\u5F71\uFF1A\u539F\u6765\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002 +\u76F8\u4FE1\u81EA\u5DF1\uFF0C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u6545\u4E8B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE\u52A8\u753B"], + tip: "\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" }, - autumn: { - label: "\u79CB\u5929\u7684\u5927\u9E1F", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#e3a868", 0.12, "#e8b878", 0.38); - sun(g2, X(0.3), gy - 150 * u, 72 * u, "#c8463a", 0.85); - mountains(g2, W2, gy - 40 * u, 170 * u, "#8a7a6a", 0.32, 41); - mountains(g2, W2, gy, 100 * u, "#5e5248", 0.45, 47); - band(g2, W2, gy, gy + 190 * u, "#b8a990", 0.32, 81, 4 * u); - particles(9, 3, (r) => { - const y = gy + 20 * u + r() * 120 * u, w = (80 - (y - gy) / u * 0.3) * u; - brush(g2, [[X(0.3) - w, y], [X(0.3), y + 2 * u], [X(0.3) + w, y]], 5 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); - }); - band(g2, W2, gy + 185 * u, H2, "#b8925a", 0.32, 83); - strokesH(g2, W2, gy + 200 * u, H2, 30, "#7a5a30", 0.3, 19, u); - tree(g2, X(-0.88), gy + 260 * u, 640 * u, 9, { - depth: 5, - color: "#3a2a20", - leaves: (x, y, l) => { - blot(g2, x, y, l * 1.3, "#c0582e", 0.35); - blot(g2, x + l * 0.5, y - l * 0.2, l * 0.9, "#e0a040", 0.3); - } - }); - for (let i = 0; i < 10; i++) reed(g2, X(0.7) + i * 30 * u, gy + 200 * u, (180 + hash(i) * 120) * u, 0, 600 + i, { color: "#8a6a3a", alpha: 0.7 }); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X, portrait } = s; - const k = ks(s); - fallingThings(g2, W2, H2, t, 22, 12, u, (x, y, rot, r) => leaf(g2, x * 0.55, y, (9 + r() * 7) * u, rot, ["#c0582e", "#d98a3a", "#b8402a", "#e0a040"][Math.floor(r() * 4)], 0.85), 55); - const sp = (W2 + 1400 * u) / 11.5; - let lead = 0; - for (let i = 0; i < 5; i++) { - const x = W2 + 160 * u + i * 170 * u - (t - CUE.autumn.swans) * sp; - const y = H2 * (portrait ? 0.2 : 0.17) + i * (portrait ? 70 : 52) * u + 10 * u * Math.sin(t * 1.3 + i); - if (i === 0) lead = x; - if (x < -300 * u || x > W2 + 300 * u) continue; - bird(g2, { x, y, s: 105 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 4.2 + i * 0.9, face: -1, neckUp: -0.85, seed: 80 + i }); - } - const lookUp = smooth(inv(CUE.autumn.lookUp, CUE.autumn.lookUp + 1, t)); - const turn = lead < X(0.42); - const walkP = inv(CUE.autumn.walk, CUE.autumn.walk + 2.2, t); - const ux = X(0.42) - easeInOut(walkP) * 110 * u; - bird(g2, { x: ux, y: gy + 230 * u, s: 168 * k, t, sw: 0.42, body: UGLY, beak: UGLY_BEAK, fluff: 0.7, seed: 7, face: turn ? -1 : 1, neckUp: lookUp * 0.95, walk: walkP > 0 && walkP < 1 ? 1.4 : 0 }); - }, - cam(s) { - return cam(s, lerp(1.04, 1.12, easeInOut(s.t / s.D)), lerp(s.X(0.15), s.X(0.05), easeInOut(s.t / s.D)), s.H * 0.48); - }, - enterAt: [0.8, 0.3] + { + id: "sph", + name: "\u5FAE\u4FE1\u89C6\u9891\u53F7", + titleMax: 16, + tagMax: null, + color: "#07c160", + titles: ["\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u300B", "\u4E11\u5C0F\u9E2D\u53D8\u767D\u5929\u9E45\xB7\u6C34\u58A8\u7248", "\u76F8\u4FE1\u81EA\u5DF1\uFF0C\u4F60\u4E5F\u4F1A\u98DE"], + desc: `\u4E00\u90E8\u50CF\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u7684\u6C34\u58A8\u52A8\u753B\u3002 +\u6700\u5927\u7684\u90A3\u9897\u86CB\u91CC\uFF0C\u94BB\u51FA\u4E86\u53C8\u5927\u53C8\u7070\u7684\u4E11\u5C0F\u9E2D\uFF1B\u4ED6\u88AB\u5632\u7B11\u3001\u79BB\u5F00\u5BB6\u3001\u71AC\u8FC7\u5BD2\u51AC\uFF0C\u7EC8\u4E8E\u5728\u6625\u5929\u7684\u6E56\u6C34\u91CC\u770B\u89C1\u2014\u2014\u81EA\u5DF1\u662F\u4E00\u53EA\u767D\u5929\u9E45\u3002 +\u8F6C\u7ED9\u5BB6\u91CC\u7684\u5C0F\u670B\u53CB\uFF0C\u5F53\u4F5C\u4ECA\u665A\u7684\u7761\u524D\u6545\u4E8B\u5427\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u7AE5\u8BDD\u6545\u4E8B", "\u56FD\u98CE", "\u624B\u7ED8", "\u4EB2\u5B50", "\u8BED\u6587\u8BFE\u672C", "\u6210\u957F", "\u76F8\u4FE1\u81EA\u5DF1"], + tip: "\u6807\u9898 \u226416 \u5B57\uFF0C\u8BDD\u9898\u6570\u91CF\u4E0D\u9650\u3002" }, - winter: { - label: "\u5BD2\u51AC", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#5f6a74", 0.35, "#c9d0d4", 0.1); - mountains(g2, W2, gy - 30 * u, 160 * u, "#5a6268", 0.3, 53); - particles(10, 5, (r) => blot(g2, r() * W2, gy - 120 * u - r() * 60 * u, (50 + r() * 60) * u, "#ffffff", 0.45, 2, 0.5)); - band(g2, W2, gy - 10 * u, gy + 300 * u, "#c9d4d8", 0.5, 91, 5 * u); - particles(10, 77, (r, i) => { - const x = r() * W2, y = gy + 20 * u + r() * 250 * u; - brush(g2, [[x, y], [x + (60 + r() * 120) * u, y + (r() - 0.5) * 20 * u]], 1.6 * u, { color: "#6a7a86", alpha: 0.35, seed: i }); - }); - band(g2, W2, gy + 290 * u, H2, "#ffffff", 0.6, 93, 14 * u); - for (const [fx, h, sd] of [[-0.86, 520, 3], [0.88, 460, 7]]) { - tree(g2, X(fx), gy + 40 * u, h * u, sd, { depth: 5, color: "#2a2622", leaves: (x, y, l) => blot(g2, x, y - 3 * u, l * 0.35, "#ffffff", 0.85, 1.6, 0.6) }); - } - for (let i = 0; i < 12; i++) reed(g2, X(-0.5) + i * 26 * u, gy + 290 * u, (140 + hash(i) * 90) * u, 0, 700 + i, { color: "#6a6458", alpha: 0.65, wind: 0.6 }); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const night = 0.34 * smooth(inv(4, 6.5, t)) - 0.24 * smooth(inv(8.2, 9, t)) + 0.24 * smooth(inv(9.6, 10.6, t)) - 0.3 * smooth(inv(11.8, 13.2, t)); - if (night > 0) { - g2.fillStyle = rgba("#26303c", night); - g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); - blot(g2, X(0.6), H2 * 0.16, 70 * u, "#fffbe8", night * 2.2); - g2.fillStyle = rgba("#fffbe8", night * 2.4); - g2.beginPath(); - g2.arc(X(0.6), H2 * 0.16, 30 * u, 0, Math.PI * 2); - g2.fill(); - } - const ux = X(-0.04), uy = gy + 150 * u; - for (let i = 0; i < 6; i++) { - const a = i / 6 * Math.PI * 2 + 0.4, L = (150 + hash(i) * 160) * u; - const pts = [[ux + Math.cos(a) * 60 * u, uy + 10 * u + Math.sin(a) * 15 * u]]; - for (let j = 1; j < 5; j++) pts.push([pts[0][0] + Math.cos(a + nz(j, i) * 0.4) * L * j / 4, pts[0][1] + Math.sin(a + nz(j, i) * 0.4) * L * j / 4 * 0.25]); - brush(g2, pts, 2.2 * u, { color: "#3e4c58", alpha: 0.75, grow: easeOut(inv(CUE.winter.ice + i * 0.05, CUE.winter.ice + 0.7 + i * 0.05, t)), taperIn: 0.05, seed: i }); - } - bird(g2, { x: ux, y: uy, s: 175 * k, t, sw: 0.5, body: UGLY, beak: UGLY_BEAK, fluff: 0.8, seed: 7, face: 1, swim: true, tuck: 1, shiver: 1, snow: smooth(inv(0, s.D, t)), noBlink: true }); - for (let i = 0; i < 4; i++) { - const q2 = (t * 0.45 + i * 0.27) % 1; - const y = H2 * (0.2 + 0.18 * i); - brush(g2, [[lerp(-300, W2 + 300, q2) * 1, y], [lerp(-300, W2 + 300, q2) + 180 * u, y + 6 * u], [lerp(-300, W2 + 300, q2) + 360 * u, y]], 3 * u, { color: "#d8dde0", alpha: 0.5, dry: 0.9, seed: i }); - } - fallingThings(g2, W2, H2, t, 170, 31, u, (x, y, _r, r) => { - const rr = (2 + r() * 5) * u; - g2.fillStyle = rgba("#ffffff", 0.92); - g2.beginPath(); - g2.arc(x, y, rr, 0, Math.PI * 2); - g2.fill(); - g2.strokeStyle = rgba("#8a96a0", 0.35); - g2.lineWidth = 0.8 * u; - g2.stroke(); - }, 95); - }, - cam(s) { - return cam(s, lerp(1.2, 1.5, easeInOut(s.t / s.D)), s.X(-0.04), s.gy + 70 * s.u); - }, - enterAt: [0.5, 0.2] + { + id: "bili", + name: "\u54D4\u54E9\u54D4\u54E9", + titleMax: 80, + tagMax: 10, + color: "#fb7299", + titles: ["\u3010\u6C34\u58A8\u624B\u7ED8\u3011\u5B89\u5F92\u751F\u7AE5\u8BDD\u300A\u4E11\u5C0F\u9E2D\u300B\uFF5C\u5C0F\u5B66\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u5168\u7A0B\u4EE3\u7801\u9010\u5E27\u7ED8\u5236\uFF0B\u539F\u521B\u56FD\u98CE\u914D\u4E50", "\u3010\u513F\u7AE5\u52A8\u753B\u3011\u6C34\u58A8\u7248\u300A\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\u300B\uFF1A\u4E00\u90E8\u50CF\u8BFE\u672C\u63D2\u56FE\u7684\u56FD\u98CE\u7AE5\u8BDD\u77ED\u7247"], + desc: `\u4E00\u90E8\u7EA6 2 \u5206\u534A\u7684\u6C34\u58A8\u624B\u7ED8\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u6539\u7F16\u81EA\u5B89\u5F92\u751F\u7AE5\u8BDD\u300A\u4E11\u5C0F\u9E2D\u300B\uFF081843\uFF09\u3002 + +\u753B\u9762\uFF1A\u5168\u90E8\u7531\u4EE3\u7801\u5728\u6D4F\u89C8\u5668\u91CC\u9010\u5E27\u7ED8\u5236\u2014\u2014\u5BA3\u7EB8\u7EB9\u7406\u3001\u53D8\u5BBD\u6BDB\u7B14\u7B14\u89E6\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u6655\u67D3\u3001\u6731\u7802\u5370\u7AE0\uFF0C14 \u4E2A\u573A\u666F\u6A2A\u7AD6\u5C4F\u5404\u81EA\u91CD\u65B0\u6784\u56FE\u3002 +\u58F0\u97F3\uFF1A\u4E2D\u6587\u65C1\u767D\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF08\u53E4\u7B5D\u62E8\u5F26\uFF0B\u7AF9\u7B1B\uFF0B\u94FA\u5E95\uFF0C\u968F\u56DB\u5B63\u6362\u60C5\u7EEA\uFF09\uFF1B\u7834\u58F3\u3001\u9E2D\u53EB\u3001\u67AA\u58F0\u3001\u51B0\u88C2\u3001\u632F\u7FC5\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\u3002 + +\u7AE0\u8282\uFF1A +00:00 \u7247\u5934 +00:06 \u590F\u65E5\u4E61\u6751 +00:18 \u7834\u58F3 +00:29 \u6700\u5927\u7684\u86CB +00:41 \u88AB\u5632\u7B11 +00:51 \u79BB\u5BB6 +00:59 \u6CBC\u6CFD +01:13 \u79CB\u5929\u7684\u5927\u9E1F +01:28 \u5BD2\u51AC +01:42 \u6625\u56DE +01:54 \u5012\u5F71 +02:07 \u63A5\u7EB3 +02:19 \u9AD8\u98DE +02:26 \u5BC4\u8BED`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u56FD\u98CE", "\u624B\u7ED8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u539F\u521B\u914D\u4E50", "\u4EE3\u7801\u827A\u672F", "\u7AE5\u8BDD"], + tip: "\u6807\u9898 \u226480 \u5B57\uFF0C\u6807\u7B7E \u226410 \u4E2A\uFF1B\u7AE0\u8282\u65F6\u95F4\u53EF\u76F4\u63A5\u7C98\u8FDB\u7B80\u4ECB\u3002" + } + ]; + var PHOTO_POSTS = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 20, + tagMax: 10, + titles: ["\u4E11\u5C0F\u9E2D\u7684\u516D\u4E2A\u77AC\u95F4\uFF5C\u6C34\u58A8\u5B9E\u51B5\u56FE", "\u4F1A\u52A8\u7684\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\uFF5C\u4E11\u5C0F\u9E2D"], + body: `\u628A\u300A\u4E11\u5C0F\u9E2D\u300B\u753B\u6210\u4E86 6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u957F\u6309\u5C31\u80FD\u770B\u5B83\u52A8\u8D77\u6765 \u2728 + +\u5176\u4E00 \xB7 \u6700\u5927\u7684\u86CB\uFF1A\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19 +\u5176\u4E8C \xB7 \u4E11\u5C0F\u9E2D\uFF1A\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6 +\u5176\u4E09 \xB7 \u4EF0\u671B\uFF1A\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u4ECE\u5929\u4E0A\u98DE\u8FC7 +\u5176\u56DB \xB7 \u5BD2\u51AC\uFF1A\u6E56\u9762\u7ED3\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\u71AC\u8FC7\u4E00\u4E2A\u4E2A\u5BD2\u591C +\u5176\u4E94 \xB7 \u5012\u5F71\uFF1A\u4F4E\u5934\u4E00\u770B\uFF0C\u6C34\u91CC\u662F\u4E00\u53EA\u767D\u5929\u9E45 +\u5176\u516D \xB7 \u5C55\u7FC5\uFF1A\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45 + +\u6BCF\u4E00\u7B14\u90FD\u662F\u4EE3\u7801\u753B\u51FA\u6765\u7684\uFF1A\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u3002 +\u4F60\u5C0F\u65F6\u5019\u6700\u559C\u6B22\u8BFE\u672C\u91CC\u7684\u54EA\u4E00\u7BC7\uFF1F`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8", "\u5C0F\u5B66\u8BED\u6587", "\u7AE5\u8BDD\u6545\u4E8B", "\u6CBB\u6108\u7CFB", "\u52A8\u6001\u58C1\u7EB8"], + tip: "\u53D1\u5E03\u65F6\u9009\u62E9\u300C\u5B9E\u51B5\u56FE\u300D\u4E0A\u4F20 6 \u7EC4 JPG+MOV\uFF1B\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\u3002" }, - spring: { - label: "\u6625\u56DE", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#bcd3c8", 0.2, "#ffffff", 0); - sun(g2, X(0.5), H2 * 0.15, 50 * u, "#d65a40", 0.7); - mountains(g2, W2, gy - 40 * u, 150 * u, "#86a87a", 0.35, 61); - mountains(g2, W2, gy, 90 * u, "#5e8a50", 0.4, 67); - band(g2, W2, gy - 5 * u, H2, "#a9c48a", 0.35, 101); - const pts = spline([[X(-1.1), gy + 120 * u], [X(-0.3), gy + 60 * u], [X(0.3), gy + 160 * u], [X(1.1), gy + 90 * u]], 12); - brush(g2, pts, 70 * u, { color: "#9fc0c8", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.2 }); - brush(g2, pts.map(([x, y]) => [x, y - 10 * u]), 6 * u, { color: "#5e7a86", alpha: 0.3, dry: 0.7, taperIn: 0, taperOut: 0 }); - strokesH(g2, W2, gy + 200 * u, H2, 26, "#5e8a50", 0.3, 23, u); - }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - const C = CUE.spring; - willow(g2, X(-0.9), gy + 220 * u, 620 * u, t, 3, "#8fb86b", 0.15 + 0.85 * smooth(inv(C.buds, C.buds + 1.4, t))); - const bloom = smooth(inv(C.bloom, C.bloom + 1.3, t)); - tree(g2, X(0.86), gy + 220 * u, 520 * u, 21, { depth: 4, color: "#3a2c26", t, leaves: (x, y, l) => { - if (bloom > 0) { - blossom(g2, x, y, l * 0.28 * bloom, Math.floor(x), "#ec9aa8", 0.75); - blossom(g2, x + l * 0.3, y - l * 0.2, l * 0.2 * bloom, Math.floor(y), "#f4b8c0", 0.7); - } else blot(g2, x, y, l * 0.12, "#b86a74", 0.5); - } }); - for (let i = 0; i < 2; i++) farBird(g2, X(-0.2 + i * 0.4) + Math.sin(t * 0.5 + i) * 100 * u, H2 * 0.25 + i * 40 * u, 20 * u, t * 9 + i, "#3a3a3a"); - const spread = easeOut(inv(C.spread, C.spread + 0.9, t)); - const lift = easeIn(inv(C.lift, C.lift + 4.2, t)); - const fx = X(-0.12) + lift * W2 * 0.55, fy = gy + 170 * u - lift * (gy + 260 * u); - const flapRate = t < C.flapHard ? 2.6 : t < C.lift ? lerp(3, 6, inv(C.flapHard, C.lift, t)) : 4.6; - bird(g2, { - x: fx, - y: fy, - s: 205 * k, - t, - sw: 0.8, - body: "#b5b1a9", - beak: "#b88a5c", - fluff: 0.2, - seed: 7, - face: 1, - wing: spread, - flap: t * flapRate * 1 + (t > 3.4 ? Math.sin(t) : 0), - legs: lift < 0.05, - neckUp: lift > 0 ? -0.6 * smooth(lift * 4) : lerp(0, 0.3, spread), - look: t < C.spread ? 0.2 * Math.sin(t * 1.5) : 0 - }); - question(g2, fx + 30 * u, fy - 330 * u * k, 80 * u, smooth(inv(C.question, C.question + 0.25, t)) * (1 - smooth(inv(C.flapHard + 0.2, C.flapHard + 0.5, t)))); - if (t > C.lift && t < C.lift + 1.2) particles(8, 99, (r, i) => { - const q2 = t - C.lift; - leaf(g2, X(-0.12) + (r() - 0.5) * 200 * u, gy + 180 * u - q2 * 80 * u * r(), 8 * u, q2 * 4 + i, "#9fbf7a", 0.8 * (1 - q2 / 1.2)); - }); - fallingThings(g2, W2, H2, t, 26, 41, u, (x, y, rot, r) => leaf(g2, x, y, (6 + r() * 5) * u, rot, r() < 0.5 ? "#f0a8b4" : "#f6c8cf", 0.85), 45); - }, - cam(s) { - const lift = easeInOut(inv(CUE.spring.lift - 0.3, CUE.spring.lift + 3.5, s.t)); - return cam(s, lerp(1.3, 1.1, lift), lerp(s.X(-0.1), s.W / 2, lift), lerp(s.gy + 60 * s.u, s.H * 0.3, lift)); + { + id: "dy", + name: "\u6296\u97F3\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 30, + tagMax: 5, + titles: ["\u6C34\u58A8\u5B9E\u51B5\u56FE\uFF1A\u4E11\u5C0F\u9E2D\u53D8\u6210\u767D\u5929\u9E45\u7684\u516D\u4E2A\u77AC\u95F4", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF0C\u4E11\u5C0F\u9E2D\u957F\u5927\u4E86"], + body: `6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u8BB2\u5B8C\u4E00\u4E2A\u300A\u4E11\u5C0F\u9E2D\u300B\u3002 +\u7834\u58F3 \u2192 \u88AB\u5632\u7B11 \u2192 \u4EF0\u671B\u5929\u9E45 \u2192 \u71AC\u8FC7\u5BD2\u51AC \u2192 \u6C34\u4E2D\u5012\u5F71 \u2192 \u5C55\u7FC5\u9AD8\u98DE\u3002 +\u4E0D\u6015\u98CE\u96EA\uFF0C\u4E5F\u4E0D\u6015\u5632\u7B11\uFF0C\u603B\u6709\u4E00\u5929\uFF0C\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180\u3002`, + tags: ["\u4E11\u5C0F\u9E2D", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u5B89\u5F92\u751F\u7AE5\u8BDD", "\u56FD\u98CE"], + tip: "\u56FE\u6587\u6A21\u5F0F\u4E0A\u4F20\u5B9E\u51B5\u56FE\uFF1B\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + } + ]; + + // src/films/duckling/index.ts + var tl = timeline_default; + var at = (id, dt) => sceneAt(tl, id, dt); + var duckling = { + id: "duckling", + title: "\u4E11\u5C0F\u9E2D", + tagline: "\u53D8\u6210\u767D\u5929\u9E45", + intro: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u513F\u7AE5\u52A8\u753B\u3002\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\uFF0C\u50CF\u5C0F\u5B66\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE\u90A3\u6837\uFF0C\u4E00\u7B14\u4E00\u753B\u5728\u4F60\u7684\u6D4F\u89C8\u5668\u91CC\u5B9E\u65F6\u753B\u51FA\u6765\uFF1B\u65C1\u767D\u3001\u539F\u521B\u56FD\u98CE\u914D\u4E50\u4E0E\u97F3\u6548\u540C\u6B65\u64AD\u653E\u3002", + credit: "\u6545\u4E8B\u6539\u7F16\u81EA\u5B89\u5F92\u751F\u300A\u4E11\u5C0F\u9E2D\u300B\uFF081843\uFF0C\u516C\u6709\u9886\u57DF\uFF09", + assets: "film/", + slug: "ugly-duckling", + timeline: tl, + scenes: SCENES2, + seal: ["\u7AE5\u8BDD"], + series: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u4E11\u5C0F\u9E2D", + score, + effects, + covers: [ + { id: "reflect", name: "\u5012\u5F71\u63ED\u6653", t: at("reflection", 7.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u539F\u6765\uFF0C\u4ED6\u662F\u4E00\u53EA\u767D\u5929\u9E45", side: "left", vertical: true }, + { id: "hatch", name: "\u7834\u58F3\u800C\u51FA", t: at("bigegg", 4.4), title: "\u4E11\u5C0F\u9E2D", sub: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, + { id: "fly", name: "\u5C55\u7FC5\u9AD8\u98DE", t: at("fly", 4.2), title: "\u76F8\u4FE1\u81EA\u5DF1", sub: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180", side: "right", vertical: true }, + { id: "autumn", name: "\u79CB\u65E5\u4EF0\u671B", t: at("autumn", 6.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", side: "left", vertical: true } + ], + live: live_default, + platforms: PLATFORMS, + photoPosts: PHOTO_POSTS, + qa: qa_default, + making: [ + ["\u753B\u9762", "\u5168\u90E8\u7531\u4EE3\u7801\u9010\u5E27\u7ED8\u5236\uFF1A\u7A0B\u5E8F\u751F\u6210\u5BA3\u7EB8\u7EB9\u7406\uFF0C\u53D8\u5BBD\u6BDB\u7B14\u7B14\u89E6\u5E26\u8D77\u7B14\u3001\u6536\u7B14\u548C\u98DE\u767D\uFF0C\u6DE1\u5F69\u6655\u67D3\u6709\u6C34\u5F69\u79EF\u8FB9\uFF0C\u518D\u76D6\u6731\u7802\u5370\u7AE0\u3002\u540C\u4E00\u53EA\u300C\u9E1F\u300D\u53EF\u4EE5\u4ECE\u5C0F\u9EC4\u9E2D\u4E00\u8DEF\u957F\u6210\u5929\u9E45\u3002\u6BCF\u5E27\u53EA\u7531\u65F6\u95F4\u51B3\u5B9A\uFF0C\u53EF\u9010\u5E27\u590D\u73B0\u3002"], + ["\u58F0\u97F3", "\u4E2D\u6587\u795E\u7ECF\u7F51\u7EDC\u8BED\u97F3\u65C1\u767D\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF08\u53E4\u7B5D\u62E8\u5F26\u3001\u7AF9\u7B1B\u3001\u94FA\u5E95\u3001\u94C3\uFF09\uFF0C\u968F\u56DB\u5B63\u6362\u60C5\u7EEA\uFF1B\u7834\u58F3\u3001\u9E2D\u53EB\u3001\u67AA\u58F0\u3001\u51B0\u88C2\u3001\u632F\u7FC5\u3001\u62CD\u624B\u7B49\u97F3\u6548\u90FD\u662F\u7A0B\u5E8F\u5408\u6210\u3002\u4EBA\u58F0\u51FA\u73B0\u65F6\u914D\u4E50\u81EA\u52A8\u538B\u4F4E\uFF0C\u6574\u4F53\u54CD\u5EA6 \u221214 LUFS\u3002"], + ["\u6D41\u7A0B", "\u6309 vibe-video \u6D41\u7A0B\uFF1A\u5206\u955C storyboard \u2192 \u914D\u97F3\u751F\u6210\u65F6\u95F4\u8F74 \u2192 \u63A5\u89E6\u8868 / \u9759\u5E27\u68C0\u67E5 \u2192 \u786E\u5B9A\u6027\u81EA\u68C0 \u2192 \u5168\u7247\u6E32\u67D3 \u2192 \u6DF7\u97F3 \u2192 vibe qa \u8D28\u68C0 \u2192 \u5C01\u9762\u4E0E\u53D1\u5E03\u5305\uFF1B\u5B9E\u51B5\u56FE\u6309 livecanvas \u6D41\u7A0B\u914D\u5BF9\u5C01\u88C5\u3002"] + ] + }; + + // src/films/wolf/timeline.json + var timeline_default2 = { + title: "\u72FC\u6765\u4E86", + style: "ink-wash textbook illustration", + w: 1920, + h: 1080, + fps: 30, + total: 137.86666666666667, + frames: 4136, + voice: { + n: "zh-CN-XiaoxiaoNeural", + boy: "zh-CN-YunxiaNeural", + farmer: "zh-CN-YunjianNeural" + }, + scenes: [ + { + id: "title", + start: 0, + dur: 6.5, + voice_start: 0.5, + voice_dur: 2.568, + lines: [ + { + who: "n", + text: "\u4F0A\u7D22\u5BD3\u8A00\uFF0C\u72FC\u6765\u4E86\u3002", + t0: 0.5, + dur: 2.568, + subs: [ + { + t0: 0.5, + t1: 1.967, + text: "\u4F0A\u7D22\u5BD3\u8A00", + who: "n" + }, + { + t0: 1.967, + t1: 3.068, + text: "\u72FC\u6765\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 0.5, + t1: 1.967, + text: "\u4F0A\u7D22\u5BD3\u8A00", + who: "n" + }, + { + t0: 1.967, + t1: 3.068, + text: "\u72FC\u6765\u4E86", + who: "n" + } + ] }, - enterAt: [0.5, 0.5] - }, - reflection: { - label: "\u5012\u5F71", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#c9d8d8", 0.18, "#ffffff", 0); - mountains(g2, W2, gy - 60 * u, 120 * u, "#8fa69a", 0.28, 71, { soft: 1 }); - for (let i = 0; i < 7; i++) { - const x = X(-1 + i * 0.33) + hash(i) * 60 * u; - willow(g2, x, gy - 20 * u, (200 + hash(i * 3) * 80) * u, 0, 40 + i, "#8fb86b", 1); - } - band(g2, W2, gy - 25 * u, H2, "#a9bcc0", 0.32, 111, 3 * u); - strokesH(g2, W2, gy, H2, 30, "#5a6e78", 0.28, 29, u); - lotusLeaf(g2, X(-0.9), H2 - 90 * u, 120 * u, 7); - lotusLeaf(g2, X(-0.72), H2 - 30 * u, 90 * u, 8); - lotusLeaf(g2, X(0.92), H2 - 60 * u, 110 * u, 9); + { + id: "village", + start: 6.5, + dur: 10.566666666666666, + voice_start: 7, + voice_dur: 9.168, + lines: [ + { + who: "n", + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50\u3002\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B\uFF0C\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50\uFF0C\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349\u3002", + t0: 7, + dur: 9.168, + subs: [ + { + t0: 7, + t1: 9.096, + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50", + who: "n" + }, + { + t0: 9.096, + t1: 11.715, + text: "\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B", + who: "n" + }, + { + t0: 11.715, + t1: 13.549, + text: "\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50", + who: "n" + }, + { + t0: 13.549, + t1: 16.168, + text: "\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 7, + t1: 9.096, + text: "\u5C71\u811A\u4E0B\u6709\u4E2A\u5C0F\u6751\u5B50", + who: "n" + }, + { + t0: 9.096, + t1: 11.715, + text: "\u5927\u4EBA\u4EEC\u6BCF\u5929\u5728\u7530\u91CC\u5E72\u6D3B", + who: "n" + }, + { + t0: 11.715, + t1: 13.549, + text: "\u6709\u4E2A\u653E\u7F8A\u7684\u5B69\u5B50", + who: "n" + }, + { + t0: 13.549, + t1: 16.168, + text: "\u5929\u5929\u8D76\u7740\u7F8A\u7FA4\u4E0A\u5C71\u5403\u8349", + who: "n" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - water(g2, 0, W2, gy, H2 - gy, t, 7, { alpha: 0.22, lines: 10, speed: 3 }); - const px = X(-0.16), py = gy + 175 * u, ps = 200 * k; - const R = CUE.reflection; - const reveal = smooth(inv(R.reveal, R.reveal + 1.6, t)); - const bow = smooth(inv(R.bow, R.bow + 1, t)) * (1 - smooth(inv(R.proud - 0.4, R.proud + 0.8, t))); - const proud = smooth(inv(R.proud, R.proud + 1.2, t)); - const land = inv(0, R.land, t); - const body = mixHex("#aaa69e", WHITE, reveal); - const o = { x: px, y: py, s: ps, t, sw: 1, body, beak: mixHex("#b88a5c", SWAN_BEAK, reveal), seed: 7, face: 1, neckUp: -0.95 * bow + 0.25 * proud, glow: reveal * (1 - smooth(inv(R.reveal + 2, R.reveal + 4, t))), noBlink: bow > 0.5 }; - const refl = (c2) => bird(c2, { ...o, body: WHITE, beak: SWAN_BEAK, swim: true, glow: 0 }); - if (land >= 1) { - const mirror = smooth(inv(R.mirror, R.mirror + 1.2, t)); - reflection(g2, refl, { x: px - ps * 1.3, y: py - ps * 1.4, w: ps * 2.8, h: ps * 1.46 }, py + ps * 0.06, t, lerp(0.3, 0.95, mirror), "#7a90a0"); - if (mirror > 0 && mirror < 1) blot(g2, px + 60 * u, py + 140 * u, 220 * u, "#fff4d8", 0.35 * Math.sin(Math.PI * mirror)); - if (reveal > 0 && reveal < 1) blot(g2, px + 30 * u, py - 60 * u, 320 * u, "#fff4d8", 0.55 * Math.sin(Math.PI * reveal)); - for (let r = 0; r < 3; r++) { - const q2 = inv(R.reveal + r * 0.5, R.reveal + 2.4 + r * 0.5, t); - if (q2 > 0 && q2 < 1) ripplesRing(g2, px + 70 * u, py + 60 * u, 60 * u + q2 * 420 * u, 0.4 * (1 - q2), "#9a7a3a"); + { + id: "bored", + start: 17.0667, + dur: 12.833333333333334, + voice_start: 17.567, + voice_dur: 11.424, + lines: [ + { + who: "n", + text: "\u5C71\u5761\u4E0A\uFF0C\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349\u3002\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B\uFF0C\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349\uFF0C\u89C9\u5F97\u65E0\u804A\u6781\u4E86\u3002\u5FFD\u7136\uFF0C\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C\u3002", + t0: 17.567, + dur: 11.424, + subs: [ + { + t0: 17.567, + t1: 18.403, + text: "\u5C71\u5761\u4E0A", + who: "n" + }, + { + t0: 18.403, + t1: 20.91, + text: "\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349", + who: "n" + }, + { + t0: 20.91, + t1: 23.139, + text: "\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B", + who: "n" + }, + { + t0: 23.139, + t1: 25.09, + text: "\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349", + who: "n" + }, + { + t0: 25.09, + t1: 26.762, + text: "\u89C9\u5F97\u65E0\u804A\u6781\u4E86", + who: "n" + }, + { + t0: 26.762, + t1: 27.319, + text: "\u5FFD\u7136", + who: "n" + }, + { + t0: 27.319, + t1: 28.991, + text: "\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C", + who: "n" + } + ] } - } - for (let i = 0; i < 3; i++) { - const q2 = easeOut(inv(1.2 + i * 0.4, 5.2 + i * 0.4, t)); - const sx = lerp(X(1.3 + i * 0.2), X(0.3 + i * 0.24), q2), sy = gy + (110 + i * 45) * u; - const greet = smooth(inv(R.proud + 0.4 + i * 0.2, R.proud + 1.2 + i * 0.2, t)); - swimBird(g2, swanBird(sx, sy, (150 - i * 12) * k, t + i, { face: -1, seed: 90 + i, neckUp: -0.55 * greet + 0.08 * Math.sin(t + i) })); - } - if (land < 1) { - const lx = lerp(X(-0.65), px, easeOut(land)), ly = lerp(H2 * 0.12, py, easeInOut(land)); - bird(g2, { ...o, x: lx, y: ly, legs: false, wing: 1 - land * 0.3, flap: t * 9, swim: true, neckUp: -0.5 }); - } else { - const settle = inv(R.land, R.land + 0.85, t); - swimBird(g2, { ...o, swim: true, wing: (1 - easeOut(settle)) * 0.7, flap: t * 6 }); - const q2 = t - R.land; - if (q2 < 1) particles(14, 5, (r) => { - const a = -Math.PI * r(), v = (100 + r() * 180) * u; - const x = px + Math.cos(a) * v * q2 * 1.6, y = py + Math.sin(a) * v * q2 * 1.4 + 300 * u * q2 * q2; - g2.fillStyle = rgba("#ffffff", 0.9 * (1 - q2)); - g2.beginPath(); - g2.arc(x, y, (3 + r() * 5) * u, 0, Math.PI * 2); - g2.fill(); - g2.strokeStyle = rgba("#6a7a86", 0.5 * (1 - q2)); - g2.lineWidth = u; - g2.stroke(); - }); - } - particles(10, 61, (r, i) => { - const x = (r() * W2 + t * 14 * u) % W2, y = gy + 40 * u + r() * (H2 - gy - 80 * u); - leaf(g2, x, y, 6 * u, t * 0.3 + i, "#f0a8b4", 0.75); - }); + ], + subs: [ + { + t0: 17.567, + t1: 18.403, + text: "\u5C71\u5761\u4E0A", + who: "n" + }, + { + t0: 18.403, + t1: 20.91, + text: "\u7F8A\u513F\u6162\u60A0\u60A0\u5730\u5403\u7740\u8349", + who: "n" + }, + { + t0: 20.91, + t1: 23.139, + text: "\u653E\u7F8A\u5A03\u8EBA\u5728\u5927\u6811\u4E0B", + who: "n" + }, + { + t0: 23.139, + t1: 25.09, + text: "\u5634\u91CC\u53FC\u7740\u4E00\u6839\u8349", + who: "n" + }, + { + t0: 25.09, + t1: 26.762, + text: "\u89C9\u5F97\u65E0\u804A\u6781\u4E86", + who: "n" + }, + { + t0: 26.762, + t1: 27.319, + text: "\u5FFD\u7136", + who: "n" + }, + { + t0: 27.319, + t1: 28.991, + text: "\u4ED6\u7684\u773C\u73E0\u4E00\u8F6C", + who: "n" + } + ] }, - cam(s) { - const { t, u, gy } = s; - const zin = easeInOut(inv(CUE.reflection.bow - 0.2, CUE.reflection.mirror + 1.2, t)), zout = easeInOut(inv(CUE.reflection.reveal - 0.6, CUE.reflection.reveal + 2.4, t)); - return cam(s, lerp(lerp(1.25, 1.65, zin), 1.3, zout), lerp(s.X(-0.05), s.X(-0.1), zin), gy + lerp(lerp(60, 230, zin), 110, zout) * u); + { + id: "shout1", + start: 29.9, + dur: 9.3, + voice_start: 30.4, + voice_dur: 7.892, + lines: [ + { + who: "n", + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934\uFF0C\u62E2\u7740\u5634\u5927\u558A\uFF1A", + t0: 30.4, + dur: 3.36, + subs: [ + { + t0: 30.4, + t1: 32.233, + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934", + who: "n" + }, + { + t0: 32.233, + t1: 33.76, + text: "\u62E2\u7740\u5634\u5927\u558A", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u6765\u4E86\uFF01\u5FEB\u6765\u5440\uFF01", + t0: 34.14, + dur: 4.152, + subs: [ + { + t0: 34.14, + t1: 35.524, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 35.524, + t1: 36.908, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 36.908, + t1: 38.292, + text: "\u5FEB\u6765\u5440\uFF01", + who: "boy" + } + ] + } + ], + subs: [ + { + t0: 30.4, + t1: 32.233, + text: "\u4ED6\u8DF3\u4E0A\u5927\u77F3\u5934", + who: "n" + }, + { + t0: 32.233, + t1: 33.76, + text: "\u62E2\u7740\u5634\u5927\u558A", + who: "n" + }, + { + t0: 34.14, + t1: 35.524, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 35.524, + t1: 36.908, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 36.908, + t1: 38.292, + text: "\u5FEB\u6765\u5440\uFF01", + who: "boy" + } + ] }, - enterAt: [0.3, 0.3] - }, - accepted: { - label: "\u63A5\u7EB3", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#b8d0d8", 0.2, "#ffffff", 0); - mountains(g2, W2, gy - 50 * u, 110 * u, "#88a294", 0.28, 81, { soft: 1 }); - band(g2, W2, gy - 20 * u, H2, "#a3bfc6", 0.34, 121, 3 * u); - strokesH(g2, W2, gy, H2, 30, "#4f6670", 0.28, 33, u); - const bank = [[-50 * u, gy - 60 * u], [X(-0.55), gy - 20 * u], [X(-0.38), gy + 120 * u], [X(-0.5), gy + 260 * u], [-50 * u, gy + 300 * u]]; - wash(g2, bank, "#9fb878", 0.6); - for (let i = 0; i < 10; i++) grass(g2, X(-0.95) + i * 50 * u, gy + 100 * u + hash(i) * 120 * u, 50 * u, 0, 300 + i, "#4f6a3e", 0.6, 0.3); + { + id: "run1", + start: 39.2, + dur: 11.8, + voice_start: 39.7, + voice_dur: 10.412, + lines: [ + { + who: "n", + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86\uFF0C\u625B\u8D77\u9504\u5934\uFF0C\u62FF\u8D77\u6241\u62C5\uFF0C\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1\u3002", + t0: 39.7, + dur: 6.768, + subs: [ + { + t0: 39.7, + t1: 41.956, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 41.956, + t1: 43.084, + text: "\u625B\u8D77\u9504\u5934", + who: "n" + }, + { + t0: 43.084, + t1: 44.212, + text: "\u62FF\u8D77\u6241\u62C5", + who: "n" + }, + { + t0: 44.212, + t1: 46.468, + text: "\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1", + who: "n" + } + ] + }, + { + who: "farmer", + text: "\u72FC\u5728\u54EA\u513F\uFF1F\u72FC\u5728\u54EA\u513F\uFF1F", + t0: 46.848, + dur: 3.264, + subs: [ + { + t0: 46.848, + t1: 48.48, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + }, + { + t0: 48.48, + t1: 50.112, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + } + ] + } + ], + subs: [ + { + t0: 39.7, + t1: 41.956, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 41.956, + t1: 43.084, + text: "\u625B\u8D77\u9504\u5934", + who: "n" + }, + { + t0: 43.084, + t1: 44.212, + text: "\u62FF\u8D77\u6241\u62C5", + who: "n" + }, + { + t0: 44.212, + t1: 46.468, + text: "\u6025\u6025\u5FD9\u5FD9\u5F80\u5C71\u4E0A\u8DD1", + who: "n" + }, + { + t0: 46.848, + t1: 48.48, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + }, + { + t0: 48.48, + t1: 50.112, + text: "\u72FC\u5728\u54EA\u513F\uFF1F", + who: "farmer" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, gy, X } = s; - const k = ks(s); - cloud(g2, X(0.3) + t * 10 * u, H2 * 0.14, 380 * u, 9, 0.22); - willow(g2, X(-0.88), gy + 60 * u, 560 * u, t, 8, "#8fb86b", 1); - const cheer = t > CUE.accepted.cheer; - [[-0.82, "#c0504a", true], [-0.66, "#4a6a9a", false], [-0.52, "#d0a040", true]].forEach(([fx, col, girl], i) => { - child(g2, X(fx), gy + 110 * u + i * 12 * u, 270 * k, t + i, { face: 1, dress: col, girl, pose: cheer ? i === 1 ? "shoo" : "clap" : "stand", seed: 10 + i }); - if (cheer) soundMarks(g2, X(fx) + 50 * u, gy - 200 * u * k, 40 * u, 0.8 * (1 - smooth(inv(10, 11, t))), i); - }); - const hx = X(0.62), hy = gy + 70 * u; - const heart = smooth(inv(CUE.accepted.heart, CUE.accepted.heart + 2, t)); - swimBird(g2, swanBird(hx - 70 * u, hy, 115 * k, t, { face: 1, seed: 95, neckUp: -0.55 * heart })); - swimBird(g2, swanBird(hx + 70 * u, hy, 115 * k, t + 1, { face: -1, seed: 96, neckUp: -0.55 * heart })); - const px = X(0.12), py = gy + 210 * u; - const nuzzle = (ph) => Math.max(0, Math.sin(t * 1.6 + ph)) * smooth(inv(0.5, 1.5, t)); - swimBird(g2, swanBird(px - 190 * u, py - 30 * u, 165 * k, t + 2, { face: 1, seed: 91, neckUp: -0.7 * nuzzle(0) })); - swimBird(g2, swanBird(px, py, 200 * k, t, { face: 1, seed: 7, neckUp: 0.2 + 0.1 * Math.sin(t), glow: 0.2 })); - swimBird(g2, swanBird(px + 220 * u, py - 20 * u, 165 * k, t + 3, { face: -1, seed: 92, neckUp: -0.7 * nuzzle(2) })); - particles(12, 71, (r, i) => { - const x = (r() * W2 + t * 18 * u) % W2, y = gy + 30 * u + r() * (H2 - gy - 60 * u); - leaf(g2, x, y, 6 * u, t * 0.4 + i, "#f0a8b4", 0.75); - }); + { + id: "laugh1", + start: 51, + dur: 9.4, + voice_start: 51.5, + voice_dur: 7.988, + lines: [ + { + who: "boy", + text: "\u54C8\u54C8\u54C8\uFF0C\u54EA\u6709\u4EC0\u4E48\u72FC\uFF0C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + t0: 51.5, + dur: 3.816, + subs: [ + { + t0: 51.5, + t1: 52.215, + text: "\u54C8\u54C8\u54C8", + who: "boy" + }, + { + t0: 52.215, + t1: 53.408, + text: "\u54EA\u6709\u4EC0\u4E48\u72FC", + who: "boy" + }, + { + t0: 53.408, + t1: 55.316, + text: "\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + who: "boy" + } + ] + }, + { + who: "n", + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934\uFF0C\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86\u3002", + t0: 55.696, + dur: 3.792, + subs: [ + { + t0: 55.696, + t1: 57.863, + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934", + who: "n" + }, + { + t0: 57.863, + t1: 59.488, + text: "\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 51.5, + t1: 52.215, + text: "\u54C8\u54C8\u54C8", + who: "boy" + }, + { + t0: 52.215, + t1: 53.408, + text: "\u54EA\u6709\u4EC0\u4E48\u72FC", + who: "boy" + }, + { + t0: 53.408, + t1: 55.316, + text: "\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01", + who: "boy" + }, + { + t0: 55.696, + t1: 57.863, + text: "\u519C\u6C11\u4EEC\u6C14\u5F97\u76F4\u6447\u5934", + who: "n" + }, + { + t0: 57.863, + t1: 59.488, + text: "\u53EA\u597D\u4E0B\u5C71\u53BB\u4E86", + who: "n" + } + ] }, - cam(s) { - return cam(s, lerp(1.32, 1.18, easeInOut(s.t / s.D)), s.X(-0.12), s.gy + 110 * s.u); + { + id: "shout2", + start: 60.4, + dur: 10.9, + voice_start: 60.9, + voice_dur: 9.496, + lines: [ + { + who: "n", + text: "\u8FC7\u4E86\u51E0\u5929\uFF0C\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765\uFF1A", + t0: 60.9, + dur: 3.216, + subs: [ + { + t0: 60.9, + t1: 62.069, + text: "\u8FC7\u4E86\u51E0\u5929", + who: "n" + }, + { + t0: 62.069, + t1: 64.116, + text: "\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u6765\u4E86\uFF01", + t0: 64.496, + dur: 2.904, + subs: [ + { + t0: 64.496, + t1: 65.948, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 65.948, + t1: 67.4, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + } + ] + }, + { + who: "n", + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71\u3002", + t0: 67.78, + dur: 2.616, + subs: [ + { + t0: 67.78, + t1: 70.396, + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 60.9, + t1: 62.069, + text: "\u8FC7\u4E86\u51E0\u5929", + who: "n" + }, + { + t0: 62.069, + t1: 64.116, + text: "\u653E\u7F8A\u5A03\u53C8\u558A\u8D77\u6765", + who: "n" + }, + { + t0: 64.496, + t1: 65.948, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 65.948, + t1: 67.4, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 67.78, + t1: 70.396, + text: "\u519C\u6C11\u4EEC\u53C8\u4E00\u6B21\u8DD1\u4E0A\u4E86\u5C71", + who: "n" + } + ] }, - enterAt: [0.2, 0.5] - }, - fly: { - label: "\u9AD8\u98DE", - bg(s) { - const { g: g2, W: W2, H: H2, u, X, portrait } = s; - sky(g2, W2, H2, "#a8c4d4", 0.32, "#f2ead8", 0); - sun(g2, X(0.62), H2 * (portrait ? 0.14 : 0.18), 60 * u, "#d65a40", 0.75); - mountains(g2, W2, H2 * 0.92, 300 * u, "#7d8f8a", 0.35, 91); - mountains(g2, W2, H2 * 1.02, 260 * u, "#4f5d58", 0.5, 97); + { + id: "warn", + start: 71.3, + dur: 8, + voice_start: 71.8, + voice_dur: 6.596, + lines: [ + { + who: "boy", + text: "\u54C8\u54C8\uFF0C\u53C8\u4E0A\u5F53\u5566\uFF01", + t0: 71.8, + dur: 2.352, + subs: [ + { + t0: 71.8, + t1: 72.472, + text: "\u54C8\u54C8", + who: "boy" + }, + { + t0: 72.472, + t1: 74.152, + text: "\u53C8\u4E0A\u5F53\u5566\uFF01", + who: "boy" + } + ] + }, + { + who: "farmer", + text: "\u4F60\u518D\u8BF4\u8C0E\uFF0C\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + t0: 74.532, + dur: 3.864, + subs: [ + { + t0: 74.532, + t1: 75.636, + text: "\u4F60\u518D\u8BF4\u8C0E", + who: "farmer" + }, + { + t0: 75.636, + t1: 78.396, + text: "\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + who: "farmer" + } + ] + } + ], + subs: [ + { + t0: 71.8, + t1: 72.472, + text: "\u54C8\u54C8", + who: "boy" + }, + { + t0: 72.472, + t1: 74.152, + text: "\u53C8\u4E0A\u5F53\u5566\uFF01", + who: "boy" + }, + { + t0: 74.532, + t1: 75.636, + text: "\u4F60\u518D\u8BF4\u8C0E", + who: "farmer" + }, + { + t0: 75.636, + t1: 78.396, + text: "\u4E0B\u6B21\u6211\u4EEC\u5C31\u4E0D\u4FE1\u4F60\u4E86\uFF01", + who: "farmer" + } + ] }, - draw(s) { - const { g: g2, W: W2, H: H2, u, t, X, portrait } = s; - const k = ks(s); - particles(7, 21, (r, i) => { - const y = H2 * (0.15 + r() * 0.6), w = (260 + r() * 360) * u, sp2 = (90 + r() * 160) * u; - const x = ((r() * (W2 + w * 2) - t * sp2) % (W2 + w * 2) + W2 + w * 2) % (W2 + w * 2) - w; - cloud(g2, x, y, w, 30 + i, 0.25 + r() * 0.15); - }); - const enter = easeOut(inv(0, 2.2, t)); - const cx = lerp(-W2 * 0.4, X(portrait ? 0.05 : 0.1), enter) + Math.sin(t * 0.9) * 20 * u; - const cy = H2 * (portrait ? 0.42 : 0.44) + Math.sin(t * 1.2) * 18 * u; - const sp = portrait ? 0.62 : 1; - const flock = [[-250, 110, 0.78], [-470, 210, 0.66], [-230, -120, 0.74], [-440, -210, 0.6]]; - flock.forEach(([dx, dy, sc], i) => bird(g2, { x: cx + dx * u * sp, y: cy + dy * u * sp, s: 230 * k * sc, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.3 + i * 0.8, neckUp: -0.85, seed: 80 + i })); - bird(g2, { x: cx, y: cy, s: 240 * k, t, sw: 1, body: WHITE, beak: SWAN_BEAK, legs: false, wing: 1, flap: t * 3.2, neckUp: -0.85, seed: 7, glow: 0.3 }); + { + id: "wolf", + start: 79.3, + dur: 12, + voice_start: 79.8, + voice_dur: 10.584, + lines: [ + { + who: "n", + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50\uFF0C\u4E4C\u4E91\u538B\u7740\u5C71\u5934\u3002\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B\uFF0C\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4\u3002\u72FC\uFF0C\u771F\u7684\u6765\u4E86\uFF01", + t0: 79.8, + dur: 10.584, + subs: [ + { + t0: 79.8, + t1: 81.564, + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50", + who: "n" + }, + { + t0: 81.564, + t1: 83.328, + text: "\u4E4C\u4E91\u538B\u7740\u5C71\u5934", + who: "n" + }, + { + t0: 83.328, + t1: 84.21, + text: "\u6811\u4E1B\u91CC", + who: "n" + }, + { + t0: 84.21, + t1: 86.562, + text: "\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B", + who: "n" + }, + { + t0: 86.562, + t1: 88.62, + text: "\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4", + who: "n" + }, + { + t0: 88.62, + t1: 88.914, + text: "\u72FC", + who: "n" + }, + { + t0: 88.914, + t1: 90.384, + text: "\u771F\u7684\u6765\u4E86\uFF01", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 79.8, + t1: 81.564, + text: "\u53C8\u8FC7\u4E86\u4E9B\u65E5\u5B50", + who: "n" + }, + { + t0: 81.564, + t1: 83.328, + text: "\u4E4C\u4E91\u538B\u7740\u5C71\u5934", + who: "n" + }, + { + t0: 83.328, + t1: 84.21, + text: "\u6811\u4E1B\u91CC", + who: "n" + }, + { + t0: 84.21, + t1: 86.562, + text: "\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B", + who: "n" + }, + { + t0: 86.562, + t1: 88.62, + text: "\u6162\u6162\u9760\u8FD1\u4E86\u7F8A\u7FA4", + who: "n" + }, + { + t0: 88.62, + t1: 88.914, + text: "\u72FC", + who: "n" + }, + { + t0: 88.914, + t1: 90.384, + text: "\u771F\u7684\u6765\u4E86\uFF01", + who: "n" + } + ] }, - cam(s) { - return { z: lerp(1, 1.08, s.t / s.D), x: s.W / 2, y: s.H / 2 }; + { + id: "cry", + start: 91.3, + dur: 9.4, + voice_start: 91.8, + voice_dur: 7.988, + lines: [ + { + who: "n", + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86\uFF0C\u62FC\u547D\u5730\u558A\uFF1A", + t0: 91.8, + dur: 3.192, + subs: [ + { + t0: 91.8, + t1: 93.715, + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86", + who: "n" + }, + { + t0: 93.715, + t1: 94.992, + text: "\u62FC\u547D\u5730\u558A", + who: "n" + } + ] + }, + { + who: "boy", + text: "\u72FC\u6765\u4E86\uFF01\u72FC\u771F\u7684\u6765\u4E86\uFF01\u6551\u547D\u554A\uFF01", + t0: 95.372, + dur: 4.416, + subs: [ + { + t0: 95.372, + t1: 96.634, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 96.634, + t1: 98.526, + text: "\u72FC\u771F\u7684\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 98.526, + t1: 99.788, + text: "\u6551\u547D\u554A\uFF01", + who: "boy" + } + ] + } + ], + subs: [ + { + t0: 91.8, + t1: 93.715, + text: "\u653E\u7F8A\u5A03\u5413\u574F\u4E86", + who: "n" + }, + { + t0: 93.715, + t1: 94.992, + text: "\u62FC\u547D\u5730\u558A", + who: "n" + }, + { + t0: 95.372, + t1: 96.634, + text: "\u72FC\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 96.634, + t1: 98.526, + text: "\u72FC\u771F\u7684\u6765\u4E86\uFF01", + who: "boy" + }, + { + t0: 98.526, + t1: 99.788, + text: "\u6551\u547D\u554A\uFF01", + who: "boy" + } + ] }, - enterAt: [0.1, 0.5] - }, - ending: { - label: "\u5BC4\u8BED", - bg(s) { - const { g: g2, W: W2, H: H2, u, gy, X } = s; - sky(g2, W2, gy, "#e8a070", 0.3, "#f0c090", 0.35); - sun(g2, X(0.05), gy - 130 * u, 115 * u, "#c8463a", 0.9); - mountains(g2, W2, gy - 20 * u, 200 * u, "#6a5048", 0.42, 101); - mountains(g2, W2, gy + 10 * u, 120 * u, "#3a2c28", 0.6, 107); - band(g2, W2, gy + 5 * u, H2, "#d8a878", 0.3, 131, 3 * u); - particles(14, 9, (r) => { - const y = gy + 20 * u + r() * 200 * u, w = (130 - (y - gy) / u * 0.4) * u; - brush(g2, [[X(0.05) - w, y], [X(0.05), y + 2 * u], [X(0.05) + w, y]], 6 * u, { color: "#c8463a", alpha: 0.35, dry: 0.6, seed: Math.floor(y) }); - }); - for (let i = 0; i < 8; i++) reed(g2, X(-0.98) + i * 34 * u, H2 + 10 * u, (300 + hash(i) * 200) * u, 0, 800 + i, { color: "#2a2220", alpha: 0.85 }); - for (let i = 0; i < 6; i++) reed(g2, X(0.86) + i * 30 * u, H2 + 10 * u, (240 + hash(i + 9) * 160) * u, 0, 820 + i, { color: "#2a2220", alpha: 0.85 }); + { + id: "noone", + start: 100.7, + dur: 11.166666666666666, + voice_start: 101.2, + voice_dur: 9.76, + lines: [ + { + who: "n", + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86\uFF0C\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E\u3002", + t0: 101.2, + dur: 4.296, + subs: [ + { + t0: 101.2, + t1: 103.348, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 103.348, + t1: 105.496, + text: "\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E", + who: "n" + } + ] + }, + { + who: "farmer", + text: "\u54FC\uFF0C\u53C8\u6765\u9A97\u4EBA\u3002", + t0: 105.876, + dur: 2.664, + subs: [ + { + t0: 105.876, + t1: 106.409, + text: "\u54FC", + who: "farmer" + }, + { + t0: 106.409, + t1: 108.54, + text: "\u53C8\u6765\u9A97\u4EBA", + who: "farmer" + } + ] + }, + { + who: "n", + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71\u3002", + t0: 108.92, + dur: 2.04, + subs: [ + { + t0: 108.92, + t1: 110.96, + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 101.2, + t1: 103.348, + text: "\u5C71\u4E0B\u7684\u519C\u6C11\u542C\u89C1\u4E86", + who: "n" + }, + { + t0: 103.348, + t1: 105.496, + text: "\u90FD\u4EE5\u4E3A\u4ED6\u53C8\u5728\u8BF4\u8C0E", + who: "n" + }, + { + t0: 105.876, + t1: 106.409, + text: "\u54FC", + who: "farmer" + }, + { + t0: 106.409, + t1: 108.54, + text: "\u53C8\u6765\u9A97\u4EBA", + who: "farmer" + }, + { + t0: 108.92, + t1: 110.96, + text: "\u8C01\u4E5F\u6CA1\u6709\u4E0A\u5C71", + who: "n" + } + ] }, - draw(s) { - const { g: g2, H: H2, u, t, gy, X, portrait } = s; - for (let i = 0; i < 5; i++) { - const q2 = inv(0, 10, t); - const x0 = X(-0.75 + i * 0.07), y0 = H2 * (0.3 + 0.03 * i); - const tx = X(0.05) + i * 12 * u, ty = gy - 160 * u - i * 8 * u; - const x = lerp(x0, tx, q2 * 0.85), y = lerp(y0, ty, q2 * 0.85); - farBird(g2, x, y, lerp(28, 9, q2) * u, t * 7 + i * 1.3, "#2a2220", 0.9); - } - const fs = (portrait ? 150 : 130) * u; - const vx = portrait ? X(0.62) : X(0.72), vy = portrait ? H2 * 0.3 : H2 * 0.36; - writeText(g2, "\u76F8\u4FE1\u81EA\u5DF1", vx, vy, fs, 1.4, 0.55, t, { vertical: true, gap: 1.02 }); - const E = CUE.ending; - const ea = smooth(inv(E.end - 0.3, E.end + 0.3, t)); - if (ea > 0) { - g2.save(); - g2.globalAlpha = ea; - g2.font = `${40 * u}px ${FONT_KAI}`; - g2.fillStyle = rgba(INK, 0.85); - g2.textAlign = "center"; - g2.save(); - g2.translate(vx - fs * 0.95, vy - fs * 1.45); - [..."\u4E11\u5C0F\u9E2D \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); - g2.restore(); - g2.restore(); - } - const sp = inv(E.end + 0.1, E.end + 0.35, t); - if (sp > 0) { - g2.save(); - g2.translate(vx - fs * 0.95, vy + fs * 1.2); - g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); - seal(g2, 0, 0, 80 * u, ["\u7AE5\u8BDD"], { alpha: Math.min(1, sp * 2) }); - g2.restore(); - } + { + id: "chaos", + start: 111.8667, + dur: 8, + voice_start: 112.367, + voice_dur: 5.976, + lines: [ + { + who: "n", + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4\uFF0C\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1\uFF0C\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86\u3002", + t0: 112.367, + dur: 5.976, + subs: [ + { + t0: 112.367, + t1: 113.725, + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4", + who: "n" + }, + { + t0: 113.725, + t1: 115.898, + text: "\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1", + who: "n" + }, + { + t0: 115.898, + t1: 118.343, + text: "\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 112.367, + t1: 113.725, + text: "\u72FC\u51B2\u8FDB\u7F8A\u7FA4", + who: "n" + }, + { + t0: 113.725, + t1: 115.898, + text: "\u7F8A\u513F\u5413\u5F97\u56DB\u5904\u4E71\u8DD1", + who: "n" + }, + { + t0: 115.898, + t1: 118.343, + text: "\u597D\u51E0\u53EA\u7F8A\u88AB\u72FC\u53FC\u8D70\u4E86", + who: "n" + } + ] }, - cam(s) { - return { z: lerp(1, 1.05, s.t / s.D), x: s.W / 2, y: s.gy }; + { + id: "regret", + start: 119.8667, + dur: 8, + voice_start: 120.367, + voice_dur: 5.328, + lines: [ + { + who: "n", + text: "\u592A\u9633\u843D\u5C71\u4E86\uFF0C\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A\uFF0C\u4F24\u5FC3\u5730\u54ED\u4E86\u3002", + t0: 120.367, + dur: 5.328, + subs: [ + { + t0: 120.367, + t1: 121.699, + text: "\u592A\u9633\u843D\u5C71\u4E86", + who: "n" + }, + { + t0: 121.699, + t1: 124.363, + text: "\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A", + who: "n" + }, + { + t0: 124.363, + t1: 125.695, + text: "\u4F24\u5FC3\u5730\u54ED\u4E86", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 120.367, + t1: 121.699, + text: "\u592A\u9633\u843D\u5C71\u4E86", + who: "n" + }, + { + t0: 121.699, + t1: 124.363, + text: "\u653E\u7F8A\u5A03\u62B1\u7740\u5269\u4E0B\u7684\u5C0F\u7F8A", + who: "n" + }, + { + t0: 124.363, + t1: 125.695, + text: "\u4F24\u5FC3\u5730\u54ED\u4E86", + who: "n" + } + ] }, - enterAt: [0.5, 0.4] - } - }; - - // src/film/film.ts - var TL = timeline_default; - var TOTAL = TL.total; - var bgCache = /* @__PURE__ */ new Map(); - function cachedBg(id, W2, H2, draw) { - const k = `${id}:${W2}x${H2}`; - let c2 = bgCache.get(k); - if (c2) { - bgCache.delete(k); - bgCache.set(k, c2); - } - if (!c2) { - c2 = makeCanvas(W2, H2); - const g2 = ctx2d(c2); - draw(g2, W2, H2); - bgCache.set(k, c2); - if (bgCache.size > 8) bgCache.delete(bgCache.keys().next().value); - } - return c2; - } - function sceneIndexAt(T) { - const s = TL.scenes; - for (let i = s.length - 1; i >= 0; i--) if (T >= s[i].start) return i; - return 0; - } - function drawScene(g2, W2, H2, i, T) { - const tl = TL.scenes[i]; - const def = SCENES[tl.id]; - const u = Math.min(W2, H2) / 1080; - const portrait = H2 > W2 * 1.05; - const t = T - tl.start; - const sc = { g: g2, W: W2, H: H2, u, t, D: tl.dur, T, portrait, gy: H2 * (portrait ? 0.62 : 0.6), X: (f) => W2 / 2 + f * Math.min(W2 / 2, H2 * 0.85) }; - g2.drawImage(paper(W2, H2), 0, 0); - g2.save(); - const cam2 = def.cam ? def.cam(sc) : { z: 1, x: W2 / 2, y: H2 / 2, dx: 0, dy: 0 }; - const z = Math.max(1, cam2.z); - const cx = Math.min(Math.max(cam2.x, W2 / (2 * z)), W2 - W2 / (2 * z)); - const cy = Math.min(Math.max(cam2.y, H2 / (2 * z)), H2 - H2 / (2 * z)); - g2.translate(W2 / 2 + (cam2.dx ?? 0), H2 / 2 + (cam2.dy ?? 0)); - g2.scale(z, z); - g2.translate(-cx, -cy); - if (def.bg) g2.drawImage(cachedBg(tl.id, W2, H2, (bg, w, h) => def.bg({ ...sc, g: bg, W: w, H: h })), 0, 0); - def.draw(sc); - g2.restore(); - if (def.overlay) def.overlay(sc); - } - var TR = 1.1; - function renderFrame(g2, W2, H2, T, opts = {}) { - T = clamp(T, 0, TOTAL - 1e-4); - const i = sceneIndexAt(T); - const tl = TL.scenes[i]; - const lt = T - tl.start; - g2.save(); - g2.setTransform(1, 0, 0, 1, 0, 0); - g2.globalAlpha = 1; - g2.globalCompositeOperation = "source-over"; - if (i > 0 && lt < TR) { - drawScene(g2, W2, H2, i - 1, T); - const p = easeInOut(lt / TR); - const R = Math.hypot(W2, H2) * 0.62 * p / 0.8 + 1; - const def = SCENES[tl.id]; - const fx = (def.enterAt?.[0] ?? 0.5) * W2, fy = (def.enterAt?.[1] ?? 0.5) * H2; - const loop = spline(blobLoop(fx, fy, R, i * 17 + 3, 0.16, 26), 4, true); - g2.save(); - tracePath(g2, loop, true); - g2.clip(); - drawScene(g2, W2, H2, i, T); - g2.restore(); - g2.save(); - tracePath(g2, loop, true); - g2.strokeStyle = rgba(INK, 0.16 * (1 - p)); - g2.lineWidth = Math.min(W2, H2) * 0.02 * (1 - p) + 1; - g2.stroke(); - g2.strokeStyle = rgba("#7a6a50", 0.12 * (1 - p)); - g2.lineWidth = Math.min(W2, H2) * 0.05 * (1 - p) + 1; - g2.stroke(); - g2.restore(); - } else { - drawScene(g2, W2, H2, i, T); - } - if (opts.subs !== false) drawSubs(g2, W2, H2, T); - if (opts.fadeEnd !== false) { - const f = smooth(inv(TOTAL - 1.2, TOTAL - 0.1, T)); - if (f > 0) { - g2.globalAlpha = f; - g2.drawImage(paper(W2, H2), 0, 0); - g2.globalAlpha = 1; - } - } - g2.restore(); - } - function currentSub(T) { - const i = sceneIndexAt(T); - const subs = TL.scenes[i].subs ?? []; - for (let k = 0; k < subs.length; k++) { - const s = subs[k]; - const end = k + 1 < subs.length ? subs[k + 1].t0 : s.t1 + 0.35; - if (T >= s.t0 - 0.05 && T < end) { - const a = Math.min(smooth((T - s.t0 + 0.05) / 0.18), smooth((end - T) / 0.15)); - return { text: s.text, a }; + { + id: "moral", + start: 127.8667, + dur: 10, + voice_start: 128.367, + voice_dur: 7.224, + lines: [ + { + who: "n", + text: "\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002", + t0: 128.367, + dur: 7.224, + subs: [ + { + t0: 128.367, + t1: 129.437, + text: "\u8BF4\u8C0E\u7684\u4EBA", + who: "n" + }, + { + t0: 129.437, + t1: 131.042, + text: "\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD", + who: "n" + }, + { + t0: 131.042, + t1: 133.183, + text: "\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6", + who: "n" + }, + { + t0: 133.183, + t1: 135.591, + text: "\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427", + who: "n" + } + ] + } + ], + subs: [ + { + t0: 128.367, + t1: 129.437, + text: "\u8BF4\u8C0E\u7684\u4EBA", + who: "n" + }, + { + t0: 129.437, + t1: 131.042, + text: "\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD", + who: "n" + }, + { + t0: 131.042, + t1: 133.183, + text: "\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6", + who: "n" + }, + { + t0: 133.183, + t1: 135.591, + text: "\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427", + who: "n" + } + ] } + ] + }; + + // src/films/wolf/live.json + var live_default2 = [ + { id: "01-idea", scene: "bored", from: 9.4, dur: 3, cover: 2, caption: "\u574F\u4E3B\u610F", index: "\u5176\u4E00", xhs: "\u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u653E\u7F8A\u5A03\u773C\u73E0\u4E00\u8F6C\u3002" }, + { id: "02-shout", scene: "shout1", from: 4, dur: 3, cover: 1.2, caption: "\u72FC\u6765\u4E86", index: "\u5176\u4E8C", xhs: "\u8DF3\u4E0A\u77F3\u5934\u5927\u558A\uFF0C\u6731\u7EA2\u5927\u5B57\u98DE\u5411\u5C71\u4E0B\u3002" }, + { id: "03-run", scene: "run1", from: 5, dur: 3, cover: 1.5, caption: "\u6025\u6025\u5FD9\u5FD9", index: "\u5176\u4E09", xhs: "\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71\u3002" }, + { id: "04-laugh", scene: "laugh1", from: 0.6, dur: 3, cover: 1, caption: "\u54C8\u54C8\u5927\u7B11", index: "\u5176\u56DB", xhs: "\u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D" }, + { id: "05-eyes", scene: "wolf", from: 6.4, dur: 3, cover: 2.2, caption: "\u771F\u7684\u6765\u4E86", index: "\u5176\u4E94", xhs: "\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B\u3002" }, + { id: "06-regret", scene: "regret", from: 4, dur: 3, cover: 1.5, caption: "\u540E\u6094", index: "\u5176\u516D", xhs: "\u5915\u9633\u4E0B\uFF0C\u4ED6\u62B1\u7740\u5C0F\u7F8A\u54ED\u4E86\u3002" } + ]; + + // src/films/wolf/qa.json + var qa_default2 = { status: "pending", results: [] }; + + // src/films/wolf/actors.ts + var actors_exports2 = {}; + __export(actors_exports2, { + POSE: () => POSE, + bigTree: () => bigTree, + bush: () => bush, + chimneySmoke: () => chimneySmoke, + cottage: () => cottage, + eyesInDark: () => eyesInDark, + flowers: () => flowers, + person: () => person, + rock: () => rock, + sheep: () => sheep, + shoutText: () => shoutText, + shoutWaves: () => shoutWaves, + stormCloud: () => stormCloud, + terraces: () => terraces, + wolf: () => wolf + }); + var lp2 = (a, b, t) => [lerp(a[0], b[0], t), lerp(a[1], b[1], t)]; + var add2 = (a, b) => [a[0] + b[0], a[1] + b[1]]; + var rot = (p, c2, a) => { + const dx = p[0] - c2[0], dy = p[1] - c2[1], co = Math.cos(a), si = Math.sin(a); + return [c2[0] + dx * co - dy * si, c2[1] + dx * si + dy * co]; + }; + function ik(a, c2, l1, l2, bend) { + let dx = c2[0] - a[0], dy = c2[1] - a[1]; + let d = Math.hypot(dx, dy); + if (d > l1 + l2 - 1e-4) { + const k = (l1 + l2 - 1e-4) / d; + dx *= k; + dy *= k; + d = l1 + l2 - 1e-4; + c2 = [a[0] + dx, a[1] + dy]; } - return null; + if (d < 1e-4) return [[a[0] + l1, a[1]], c2]; + const cosA = clamp((l1 * l1 + d * d - l2 * l2) / (2 * l1 * d), -1, 1); + const ang = Math.atan2(dy, dx) + bend * Math.acos(cosA); + return [[a[0] + Math.cos(ang) * l1, a[1] + Math.sin(ang) * l1], c2]; } - function drawSubs(g2, W2, H2, T) { - const s = currentSub(T); - if (!s || s.a <= 0) return; - const portrait = H2 > W2 * 1.05; - const u = Math.min(W2, H2) / 1080; - const fs = (portrait ? 58 : 50) * u; - const y = portrait ? H2 * 0.785 : H2 - 86 * u; - g2.save(); - g2.globalAlpha = s.a; - g2.font = `${fs}px ${FONT_KAI}`; - g2.textAlign = "center"; - g2.textBaseline = "middle"; - const w = g2.measureText(s.text).width; - const rx = w / 2 + fs * 2.2, ry = fs * 0.95; - g2.save(); - g2.translate(W2 / 2, y); - g2.scale(rx / ry, 1); - const gr = g2.createRadialGradient(0, 0, 0, 0, 0, ry); - gr.addColorStop(0, rgba(PAPER, 0.95)); - gr.addColorStop(0.72, rgba(PAPER, 0.9)); - gr.addColorStop(1, rgba(PAPER, 0)); - g2.fillStyle = gr; - g2.fillRect(-ry, -ry, ry * 2, ry * 2); - g2.restore(); + function limb(g2, pts, w, col, iw, seed, endCol, split = 0) { + brush(g2, pts, w + iw * 2.2, { color: INK, alpha: 0.82, taperIn: 0.02, taperOut: 0.04, jitter: 0.12, seed }); + g2.lineCap = "round"; g2.lineJoin = "round"; - g2.strokeStyle = rgba(PAPER, 0.96); - g2.lineWidth = fs * 0.2; - g2.strokeText(s.text, W2 / 2, y); - g2.fillStyle = rgba(INK, 0.97); - g2.fillText(s.text, W2 / 2, y); - g2.restore(); - } - - // src/film/audio.ts - var SR = 44100; - var midi = (m) => 440 * Math.pow(2, (m - 69) / 12); - var N = { E2: 40, G2: 43, A2: 45, B2: 47, D3: 50, E3: 52, G3: 55, A3: 57, B3: 59, D4: 62, E4: 64, G4: 67, A4: 69, B4: 71, D5: 74, E5: 76, G5: 79, A5: 81, B5: 83, D6: 86, E6: 88 }; - var sceneStart = (id) => TL.scenes.find((s) => s.id === id)?.start ?? 0; - var sceneDur = (id) => TL.scenes.find((s) => s.id === id)?.dur ?? 0; - var Mixer = class { - ctx; - music; - sfx; - voice; - rev; - revIn; - master; - noise; - ksCache = /* @__PURE__ */ new Map(); - r = rng(4242); - q = []; - constructor(ctx) { - this.ctx = ctx; - const self = this; - for (const k of ["flute", "pad", "bell", "block", "noiseHit", "tone", "wind", "quack", "swanCall"]) { - const orig = self[k].bind(this); - self[k] = (...a) => { - this.q.push({ t: a[0], fn: () => orig(...a) }); - }; - } - this.master = ctx.createGain(); - this.master.gain.value = 0.9; - const comp = ctx.createDynamicsCompressor(); - comp.threshold.value = -10; - comp.knee.value = 6; - comp.ratio.value = 6; - comp.attack.value = 4e-3; - comp.release.value = 0.2; - this.master.connect(comp); - comp.connect(ctx.destination); - this.music = ctx.createGain(); - this.music.connect(this.master); - this.sfx = ctx.createGain(); - this.sfx.gain.value = 0.9; - this.sfx.connect(this.master); - this.voice = ctx.createGain(); - this.voice.gain.value = 1; - this.voice.connect(this.master); - this.rev = ctx.createConvolver(); - this.rev.buffer = this.impulse(2.3, 2.4); - this.revIn = ctx.createGain(); - this.revIn.gain.value = 0.55; - this.revIn.connect(this.rev); - this.rev.connect(this.master); - this.noise = ctx.createBuffer(1, SR * 2, SR); - const d = this.noise.getChannelData(0), nr = rng(99); - for (let i = 0; i < d.length; i++) d[i] = nr() * 2 - 1; - } - impulse(sec, decay) { - const b = this.ctx.createBuffer(2, Math.floor(SR * sec), SR), r = rng(7); - for (let c2 = 0; c2 < 2; c2++) { - const d = b.getChannelData(c2); - for (let i = 0; i < d.length; i++) d[i] = (r() * 2 - 1) * Math.pow(1 - i / d.length, decay) * (i < 400 ? i / 400 : 1); - } - return b; - } - out(node, bus, pan = 0, send = 0.25) { - const p = this.ctx.createStereoPanner(); - p.pan.value = pan; - node.connect(p); - p.connect(bus); - if (send > 0) { - const s = this.ctx.createGain(); - s.gain.value = send; - p.connect(s); - s.connect(this.revIn); - } - } - // ---------- 乐器 ---------- - ks(freq) { - const key = Math.round(freq * 10); - let b = this.ksCache.get(key); - if (b) return b; - const len = Math.floor(SR * 3), per = Math.max(2, Math.round(SR / freq)); - b = this.ctx.createBuffer(1, len, SR); - const d = b.getChannelData(0), r = rng(key); - const buf = new Float32Array(per); - for (let i = 0; i < per; i++) buf[i] = (r() * 2 - 1) * (1 - i / per * 0.5); - let idx = 0, last = 0; - const damp = freq > 600 ? 0.994 : 0.997; - for (let i = 0; i < len; i++) { - const cur = buf[idx]; - const nxt = buf[(idx + 1) % per]; - const v = damp * 0.5 * (cur + nxt); - buf[idx] = v; - d[i] = cur * 0.8 + last * 0.2; - last = cur; - idx = (idx + 1) % per; - } - this.ksCache.set(key, b); - return b; + g2.strokeStyle = col; + g2.lineWidth = w; + g2.beginPath(); + g2.moveTo(pts[0][0], pts[0][1]); + for (const p of pts.slice(1)) g2.lineTo(p[0], p[1]); + g2.stroke(); + if (endCol && split > 0) { + const a = pts[pts.length - 2], b = pts[pts.length - 1], m = lp2(a, b, split); + g2.strokeStyle = endCol; + g2.lineWidth = w * 0.9; + g2.beginPath(); + g2.moveTo(m[0], m[1]); + g2.lineTo(b[0], b[1]); + g2.stroke(); } - // 拨弦直接在 JS 里叠加到采样缓冲(比每个音符建节点快一个数量级) - pl = []; - pluck(t, m, vel = 0.5, pan = 0, o = {}) { - if (t < 0 || t > TOTAL + 2) return; - if (!this.pl.length) { - const n2 = this.ctx.length; - this.pl = [new Float32Array(n2), new Float32Array(n2)]; + } + var SKIN = "#efcfa8"; + var PROP = { + boy: { hip: -0.4, sh: -0.68, head: -0.85, hr: 0.13, ua: 0.15, fa: 0.15, th: 0.2, sn: 0.2, wTop: 0.095, wBot: 0.11, limb: 0.055 }, + man: { hip: -0.48, sh: -0.79, head: -0.91, hr: 0.088, ua: 0.17, fa: 0.165, th: 0.245, sn: 0.235, wTop: 0.11, wBot: 0.105, limb: 0.05 }, + woman: { hip: -0.47, sh: -0.77, head: -0.89, hr: 0.085, ua: 0.16, fa: 0.16, th: 0.24, sn: 0.23, wTop: 0.095, wBot: 0.115, limb: 0.045 } + }; + function person(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 1, t = o.t; + const P0 = PROP[o.kind]; + const iw = Math.max(1.1, s * 0.012) / s; + const alpha = o.alpha ?? 1; + if (alpha <= 0) return; + g2.save(); + g2.globalAlpha = alpha; + g2.translate(o.x + (o.shiver ?? 0) * s * 6e-3 * Math.sin(t * 53 + seed), o.y); + g2.scale(face * s, s); + const bob = o.bob ?? 0; + const hip = [0, (o.hipY ?? P0.hip) + bob]; + const lean = o.lean ?? 0; + const sh = rot([0, P0.sh + bob], hip, lean); + const neck = rot([8e-3, P0.sh - 0.035 + bob], hip, lean); + const head = rot([0.022, P0.head + bob], hip, lean); + const jacket = o.jacket ?? (o.kind === "boy" ? "#c0473a" : "#5d7385"); + const pants = o.pants ?? (o.kind === "boy" ? "#3e4a5c" : "#3a4048"); + const L = P0.limb; + const drawTool = () => { + const tl3 = o.tool; + if (!tl3) return; + if (tl3.kind === "hoe" || tl3.kind === "pole" || tl3.kind === "stick") { + brush(g2, [tl3.a, lp2(tl3.a, tl3.b, 0.5), tl3.b], 0.022, { color: "#5a4026", alpha: 0.95, taperIn: 0, taperOut: 0, jitter: 0.08, seed: seed + 70 }); + g2.strokeStyle = rgba("#a07a48", 0.9); + g2.lineWidth = 0.012; + g2.beginPath(); + g2.moveTo(tl3.a[0], tl3.a[1]); + g2.lineTo(tl3.b[0], tl3.b[1]); + g2.stroke(); + if (tl3.kind === "hoe") { + const dx = tl3.b[0] - tl3.a[0], dy = tl3.b[1] - tl3.a[1], d = Math.hypot(dx, dy) || 1; + const ux = dx / d, uy = dy / d, nx = -uy, ny = ux; + const b = tl3.b; + const q2 = [[b[0] - nx * 0.01, b[1] - ny * 0.01], [b[0] + nx * 0.13 - ux * 0.02, b[1] + ny * 0.13 - uy * 0.02], [b[0] + nx * 0.14 + ux * 0.05, b[1] + ny * 0.14 + uy * 0.05], [b[0] + ux * 0.03, b[1] + uy * 0.03]]; + g2.fillStyle = rgba("#4a4c50", 0.95); + g2.beginPath(); + g2.moveTo(q2[0][0], q2[0][1]); + for (const p of q2.slice(1)) g2.lineTo(p[0], p[1]); + g2.closePath(); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.8); + g2.lineWidth = iw; + g2.stroke(); + } + if (tl3.kind === "pole") { + for (const e of [tl3.a, tl3.b]) { + g2.strokeStyle = rgba("#5a4026", 0.8); + g2.lineWidth = 6e-3; + g2.beginPath(); + g2.moveTo(e[0], e[1]); + g2.lineTo(e[0], e[1] + 0.06); + g2.stroke(); + } + } } - const src = this.ks(midi(m)).getChannelData(0); - const dur = o.dur ?? 2.6; - const i0 = Math.floor(t * SR), n = Math.min(src.length - 2, Math.floor((dur + 0.6) * SR)); - const gl = vel * Math.cos((pan + 1) * Math.PI / 4), gr = vel * Math.sin((pan + 1) * Math.PI / 4); - const relStart = dur * 0.7 * SR, tau = dur * 0.15 * SR; - const a = Math.exp(-2 * Math.PI * (2400 + (o.bright ?? 0.5) * 4e3) / SR); - const L = this.pl[0], R = this.pl[1]; - let pos = 0, lp2 = 0; - const bendTo = o.bend ? Math.pow(2, o.bend / 12) : 1; - const b0 = 0.12 * SR, b1 = 0.32 * SR; - for (let k = 0; k < n; k++) { - const idx = i0 + k; - if (idx >= L.length) break; - const rate = o.bend ? k < b0 ? 1 : k > b1 ? bendTo : 1 + (bendTo - 1) * (k - b0) / (b1 - b0) : 1; - const p = Math.floor(pos), f = pos - p; - if (p + 1 >= src.length) break; - const x = src[p] * (1 - f) + src[p + 1] * f; - pos += rate; - lp2 = x + a * (lp2 - x); - const env = k < relStart ? 1 : Math.exp(-(k - relStart) / tau); - const v = lp2 * env; - L[idx] += v * gl; - R[idx] += v * gr; + if (tl3.kind === "whip") { + brush(g2, [tl3.a, lp2(tl3.a, tl3.b, 0.5), tl3.b], 0.014, { color: "#5a4026", alpha: 0.95, taperIn: 0, taperOut: 0.3, seed: seed + 71 }); + const sw = tl3.swing ?? Math.sin(t * 3); + const end = [tl3.b[0] + 0.12 + 0.1 * sw, tl3.b[1] + 0.2 - 0.08 * sw]; + brush(g2, spline([tl3.b, [tl3.b[0] + 0.14, tl3.b[1] - 0.02 + 0.06 * sw], end], 8), 6e-3, { color: INK, alpha: 0.8, taperIn: 0, taperOut: 0.6, seed: seed + 72 }); } + }; + const hipL = add2(hip, [-0.03, 0.01]), hipR = add2(hip, [0.03, 0.01]); + const shL = add2(sh, [-0.05, 0.02]), shR = add2(sh, [0.05, 0.02]); + const legBend = 1; + const [kneeL] = ik(hipL, o.fl, P0.th, P0.sn, -legBend); + const [kneeR] = ik(hipR, o.fr, P0.th, P0.sn, -legBend); + const [elL] = ik(shL, o.hl, P0.ua, P0.fa, 1); + const [elR] = ik(shR, o.hr, P0.ua, P0.fa, 1); + const backCol = (c2) => mixHex(c2, "#3a3530", 0.18); + const legCol = o.kind === "boy" ? SKIN : pants; + limb(g2, [hipL, kneeL, o.fl], L * 1.05, backCol(o.kind === "boy" ? pants : pants), iw, seed + 1, o.kind === "boy" ? backCol(SKIN) : void 0, o.kind === "boy" ? 0 : 0); + if (o.kind === "boy") limb(g2, [kneeL, o.fl], L * 0.95, backCol(SKIN), iw, seed + 2); + foot(g2, o.fl, iw, o.kind); + limb(g2, [shL, elL, o.hl], L * 0.95, backCol(jacket), iw, seed + 3, backCol(SKIN), o.kind === "boy" ? 0.05 : 0.55); + hand(g2, o.hl, L, iw); + drawTool(); + const torso = [ + rot([-P0.wTop, P0.sh + 0.02 + bob], hip, lean), + rot([P0.wTop * 0.9, P0.sh + 0.02 + bob], hip, lean), + rot([P0.wBot, hip[1] + 0.03], hip, lean), + rot([-P0.wBot * 0.95, hip[1] + 0.03], hip, lean) + ]; + const pantsTop = [rot([-P0.wBot * 0.95, hip[1] - 0.01], hip, lean), rot([P0.wBot, hip[1] - 0.01], hip, lean), add2(hipR, [0.03, o.kind === "boy" ? 0.09 : 0.06]), add2(hipL, [-0.04, o.kind === "boy" ? 0.09 : 0.06])]; + wash(g2, pantsTop, pants, 0.95, { lw: iw }); + if (o.kind === "boy") { + limb(g2, [hipR, kneeR], L * 1.1, pants, iw, seed + 4); + limb(g2, [kneeR, o.fr], L, SKIN, iw, seed + 5); + } else limb(g2, [hipR, kneeR, o.fr], L * 1.08, legCol, iw, seed + 4); + foot(g2, o.fr, iw, o.kind); + wash(g2, torso, jacket, 0.96, { lw: iw }); + outline(g2, torso, iw * 1.2, { seed: seed + 9, alpha: 0.8, breaks: [0.05, 0.45, 0.95] }); + if (o.kind === "boy") { + const c0 = rot([-0.04, P0.sh + 0.02 + bob], hip, lean), c1 = rot([0.05, P0.sh + 0.02 + bob], hip, lean), c2 = rot([5e-3, P0.sh + 0.09 + bob], hip, lean); + g2.fillStyle = rgba("#f4ead4", 0.95); + g2.beginPath(); + g2.moveTo(c0[0], c0[1]); + g2.lineTo(c1[0], c1[1]); + g2.lineTo(c2[0], c2[1]); + g2.closePath(); + g2.fill(); + } else { + const b0 = rot([-P0.wBot, hip[1] + 0], hip, lean), b1 = rot([P0.wBot, hip[1] + 0], hip, lean); + brush(g2, [b0, lp2(b0, b1, 0.5), b1], 0.025, { color: "#2a2622", alpha: 0.8, taperIn: 0, taperOut: 0, seed: seed + 10 }); + const k0 = rot([0.02, P0.sh + 0.03 + bob], hip, lean), k1 = rot([0.035, hip[1] + 0.02], hip, lean); + brush(g2, [k0, lp2(k0, k1, 0.5), k1], iw * 0.9, { alpha: 0.6, seed: seed + 11 }); } - flushPlucks() { - if (!this.pl.length) return; - const b = this.ctx.createBuffer(2, this.ctx.length, SR); - b.copyToChannel(this.pl[0], 0); - b.copyToChannel(this.pl[1], 1); - const s = this.ctx.createBufferSource(); - s.buffer = b; - const g2 = this.ctx.createGain(); - g2.gain.value = 1; - s.connect(g2); - this.out(g2, this.music, 0, 0.3); - s.start(0); - this.pl = []; - } - flute(t, m, dur, vel = 0.22, pan = 0.1) { - const c2 = this.ctx, fr = midi(m); - const o1 = c2.createOscillator(); - o1.type = "sine"; - o1.frequency.value = fr; - const o2 = c2.createOscillator(); - o2.type = "triangle"; - o2.frequency.value = fr * 2; - const lfo = c2.createOscillator(); - lfo.frequency.value = 5.3; - const lg = c2.createGain(); - lg.gain.setValueAtTime(0, t); - lg.gain.linearRampToValueAtTime(fr * 7e-3, t + Math.min(dur, 0.6)); - lfo.connect(lg); - lg.connect(o1.frequency); - lg.connect(o2.frequency); - const g2 = c2.createGain(); - g2.gain.value = 0.12; - const g3 = c2.createGain(); - g3.gain.setValueAtTime(0, t); - g3.gain.linearRampToValueAtTime(vel, t + 0.07); - g3.gain.setValueAtTime(vel * 0.85, t + Math.max(0.08, dur - 0.12)); - g3.gain.linearRampToValueAtTime(0, t + dur + 0.18); - const n = c2.createBufferSource(); - n.buffer = this.noise; - n.loop = true; - const bp = c2.createBiquadFilter(); - bp.type = "bandpass"; - bp.frequency.value = fr * 2.2; - bp.Q.value = 1.2; - const ng = c2.createGain(); - ng.gain.setValueAtTime(0, t); - ng.gain.linearRampToValueAtTime(vel * 0.16, t + 0.05); - ng.gain.linearRampToValueAtTime(vel * 0.04, t + 0.25); - ng.gain.linearRampToValueAtTime(0, t + dur + 0.1); - n.connect(bp); - bp.connect(ng); - this.out(ng, this.music, pan, 0.4); - o1.connect(g3); - o2.connect(g2); - g2.connect(g3); - this.out(g3, this.music, pan, 0.45); - for (const o of [o1, o2, lfo]) { - o.start(t); - o.stop(t + dur + 0.3); + g2.strokeStyle = SKIN; + g2.lineWidth = o.kind === "boy" ? 0.05 : 0.045; + g2.lineCap = "round"; + g2.beginPath(); + g2.moveTo(neck[0], neck[1]); + g2.lineTo(lp2(neck, head, 0.6)[0], lp2(neck, head, 0.6)[1]); + g2.stroke(); + drawHead(g2, head, P0.hr, o, iw, seed); + limb(g2, [shR, elR, o.hr], L, jacket, iw, seed + 12, SKIN, o.kind === "boy" ? 0.05 : 0.55); + hand(g2, o.hr, L, iw); + g2.restore(); + } + function hand(g2, p, L, iw) { + g2.fillStyle = SKIN; + g2.beginPath(); + g2.arc(p[0], p[1], L * 0.62, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.75); + g2.lineWidth = iw; + g2.stroke(); + } + function foot(g2, p, iw, kind) { + const w = kind === "boy" ? 0.05 : 0.055; + g2.fillStyle = rgba(kind === "boy" ? "#8a6a3a" : "#2e2a26", 0.95); + g2.beginPath(); + g2.ellipse(p[0] + w * 0.4, p[1] - 8e-3, w, 0.018, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw; + g2.stroke(); + } + function drawHead(g2, c2, r, o, iw, seed) { + const t = o.t; + g2.save(); + g2.translate(c2[0], c2[1]); + g2.rotate(o.tilt ?? 0); + const kind = o.kind; + g2.fillStyle = SKIN; + g2.beginPath(); + g2.ellipse(0, 0, r, r * 1.05, 0, 0, Math.PI * 2); + g2.fill(); + const fl = []; + for (let i = 0; i < 14; i++) { + const a = i / 14 * Math.PI * 2; + fl.push([Math.cos(a) * r, Math.sin(a) * r * 1.05]); + } + outline(g2, fl, iw * 1.1, { seed: seed + 20, alpha: 0.8, breaks: [0.08, 0.5, 0.95] }); + g2.fillStyle = rgba("#1f1c1a", 0.95); + g2.beginPath(); + if (kind === "boy") { + g2.ellipse(-r * 0.12, -r * 0.3, r * 0.98, r * 0.78, -0.15, Math.PI * 0.95, Math.PI * 2.08); + g2.fill(); + brush(g2, spline([[-r * 0.2, -r * 0.95], [-r * 0.1, -r * 1.45], [r * 0.25, -r * 1.7]], 6), r * 0.32, { color: "#1f1c1a", alpha: 0.95, taperIn: 0.1, taperOut: 0.8, seed: seed + 21 }); + g2.fillStyle = rgba("#c0473a", 0.95); + g2.beginPath(); + g2.arc(-r * 0.16, -r * 1.02, r * 0.14, 0, Math.PI * 2); + g2.fill(); + } else { + g2.ellipse(-r * 0.15, -r * 0.35, r * 0.95, r * 0.72, -0.1, Math.PI * 0.92, Math.PI * 2.05); + g2.fill(); + if (kind === "woman" || o.hat === "bun") { + g2.beginPath(); + g2.arc(-r * 0.85, -r * 0.45, r * 0.38, 0, Math.PI * 2); + g2.fill(); } - n.start(t, t * 0.37 % 1.5); - n.stop(t + dur + 0.2); } - pad(t0, t1, notes, vel = 0.06, cutoff = 900) { - const c2 = this.ctx; - const f = c2.createBiquadFilter(); - f.type = "lowpass"; - f.frequency.value = cutoff; - const g2 = c2.createGain(); - const att = Math.min(1.6, (t1 - t0) / 3); - g2.gain.setValueAtTime(0, t0); - g2.gain.linearRampToValueAtTime(vel, t0 + att); - g2.gain.setValueAtTime(vel, Math.max(t0 + att, t1 - att)); - g2.gain.linearRampToValueAtTime(0, t1); - f.connect(g2); - this.out(g2, this.music, 0, 0.5); - for (const m of notes) for (const det of [-6, 6]) { - const o = c2.createOscillator(); - o.type = "triangle"; - o.frequency.value = midi(m); - o.detune.value = det; - o.connect(f); - o.start(t0); - o.stop(t1 + 0.1); + g2.fillStyle = SKIN; + g2.beginPath(); + g2.ellipse(-r * 0.2, r * 0.05, r * 0.16, r * 0.22, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw * 0.8; + g2.beginPath(); + g2.arc(-r * 0.2, r * 0.05, r * 0.14, -1.2, 1.6); + g2.stroke(); + const e = o.expr ?? {}; + const ex = r * 0.5, ey = -r * 0.08; + const blink = (t + seed * 0.71) % 3.7 < 0.12; + const look = (o.look ?? 0) * r * 0.08; + g2.fillStyle = INK; + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.2; + g2.lineCap = "round"; + const eye = e.eye ?? "open"; + if (blink && eye !== "closed" && eye !== "happy") { + g2.beginPath(); + g2.moveTo(ex - r * 0.1, ey); + g2.lineTo(ex + r * 0.1, ey); + g2.stroke(); + } else if (eye === "closed" || eye === "happy") { + g2.beginPath(); + g2.arc(ex, ey + r * 0.05, r * 0.11, Math.PI * 1.1, Math.PI * 1.9); + g2.stroke(); + } else if (eye === "sad") { + g2.beginPath(); + g2.arc(ex, ey - r * 0.02, r * 0.1, 0.2, Math.PI - 0.2); + g2.stroke(); + } else if (eye === "wide") { + g2.fillStyle = "#fbf7ee"; + g2.beginPath(); + g2.ellipse(ex, ey, r * 0.14, r * 0.17, 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(ex + r * 0.03 + look, ey, r * 0.065, 0, Math.PI * 2); + g2.fill(); + } else { + g2.fillStyle = INK; + g2.beginPath(); + g2.ellipse(ex + (eye === "sly" ? r * 0.06 : 0) + look, ey, r * 0.065, r * 0.085, 0, 0, Math.PI * 2); + g2.fill(); + if (eye === "sly") { + g2.beginPath(); + g2.moveTo(ex - r * 0.13, ey - r * 0.07); + g2.lineTo(ex + r * 0.12, ey - r * 0.1); + g2.stroke(); } } - bell(t, m, vel = 0.15, pan = 0, bus) { - const c2 = this.ctx, fr = midi(m); - const g2 = c2.createGain(); - g2.gain.value = 1; - [[1, 1, 2.5], [2.76, 0.4, 1.2], [5.4, 0.22, 0.6], [8.93, 0.1, 0.35]].forEach(([k, a, d]) => { - const o = c2.createOscillator(); - o.type = "sine"; - o.frequency.value = fr * k; - const og = c2.createGain(); - og.gain.setValueAtTime(0, t); - og.gain.linearRampToValueAtTime(vel * a, t + 5e-3); - og.gain.exponentialRampToValueAtTime(1e-4, t + d); - o.connect(og); - og.connect(g2); - o.start(t); - o.stop(t + d + 0.05); - }); - this.out(g2, bus ?? this.music, pan, 0.5); + const brow = e.brow ?? "none"; + if (brow !== "none") { + g2.lineWidth = iw * 1.4; + g2.beginPath(); + if (brow === "angry") { + g2.moveTo(ex - r * 0.14, ey - r * 0.32); + g2.lineTo(ex + r * 0.16, ey - r * 0.2); + } else if (brow === "worried") { + g2.moveTo(ex - r * 0.14, ey - r * 0.2); + g2.lineTo(ex + r * 0.15, ey - r * 0.32); + } else { + g2.moveTo(ex - r * 0.14, ey - r * 0.36); + g2.quadraticCurveTo(ex, ey - r * 0.44, ex + r * 0.15, ey - r * 0.36); + } + g2.stroke(); } - block(t, vel = 0.2, fr = 950, pan = 0) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = "sine"; - o.frequency.setValueAtTime(fr, t); - o.frequency.exponentialRampToValueAtTime(fr * 0.6, t + 0.05); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(vel, t); - g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.09); - o.connect(g2); - this.out(g2, this.music, pan, 0.15); - o.start(t); - o.stop(t + 0.12); + g2.lineWidth = iw; + g2.beginPath(); + g2.moveTo(r * 0.92, -r * 0.02); + g2.quadraticCurveTo(r * 1.06, r * 0.12, r * 0.9, r * 0.17); + g2.stroke(); + if (kind !== "man") { + g2.fillStyle = rgba("#e07a6a", 0.35); + g2.beginPath(); + g2.arc(r * 0.45, r * 0.32, r * 0.16, 0, Math.PI * 2); + g2.fill(); } - gliss(t, notes, step = 0.06, vel = 0.3, pan = 0) { - notes.forEach((m, i) => this.pluck(t + i * step, m, vel * (0.7 + 0.3 * i / notes.length), pan + (i / notes.length - 0.5) * 0.6, { dur: 2.2 })); + const m = e.mouth ?? "smile"; + const mx = r * 0.68, my = r * 0.5; + g2.fillStyle = rgba("#7a2a22", 0.95); + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.1; + g2.beginPath(); + if (m === "smile") { + g2.arc(mx - r * 0.05, my - r * 0.12, r * 0.16, 0.35, Math.PI - 0.6); + g2.stroke(); + } else if (m === "line") { + g2.moveTo(mx - r * 0.15, my); + g2.lineTo(mx + r * 0.08, my - r * 0.02); + g2.stroke(); + } else if (m === "frown") { + g2.arc(mx - r * 0.05, my + r * 0.1, r * 0.14, Math.PI + 0.5, Math.PI * 2 - 0.4); + g2.stroke(); + } else if (m === "chew") { + const k = Math.abs(Math.sin(t * 6)); + g2.ellipse(mx, my, r * 0.1, r * (0.03 + 0.05 * k), 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + } else if (m === "open" || m === "shout") { + const k = m === "shout" ? 0.22 : 0.12; + g2.ellipse(mx + r * 0.02, my, r * 0.12, r * k, 0, 0, Math.PI * 2); + g2.fill(); + g2.stroke(); + } else if (m === "laugh") { + const k = 0.6 + 0.4 * Math.abs(Math.sin(t * 9)); + g2.moveTo(mx - r * 0.2, my - r * 0.06); + g2.quadraticCurveTo(mx, my + r * 0.36 * k, mx + r * 0.2, my - r * 0.08); + g2.closePath(); + g2.fill(); + g2.stroke(); + } else if (m === "cry") { + const k = 0.8 + 0.2 * Math.sin(t * 7); + g2.moveTo(mx - r * 0.18, my + r * 0.1); + g2.quadraticCurveTo(mx, my - r * 0.14 * k, mx + r * 0.16, my + r * 0.12); + g2.quadraticCurveTo(mx, my + r * 0.02, mx - r * 0.18, my + r * 0.1); + g2.fill(); + g2.stroke(); } - melody(t, beat, seq, vel = 0.22, oct = 0, inst = "flute") { - let x = t; - for (const [n, d] of seq) { - if (n !== "-") { - if (inst === "flute") this.flute(x, N[n] + oct, d * beat * 0.96, vel); - else this.pluck(x, N[n] + oct, vel, 0.15, { bend: d >= 2 ? this.r() < 0.5 ? 0 : 0 : 0 }); - } - x += d * beat; + if (o.hat === "straw") { + g2.fillStyle = rgba("#d8b45a", 0.95); + g2.beginPath(); + g2.moveTo(-r * 1.6, -r * 0.45); + g2.quadraticCurveTo(0, -r * 1.85, r * 1.6, -r * 0.45); + g2.quadraticCurveTo(0, -r * 0.7, -r * 1.6, -r * 0.45); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.7); + g2.lineWidth = iw; + g2.stroke(); + for (let i = -3; i <= 3; i++) { + g2.beginPath(); + g2.moveTo(i * r * 0.18, -r * 1.4); + g2.lineTo(i * r * 0.5, -r * 0.62); + g2.strokeStyle = rgba("#8a6a2a", 0.5); + g2.stroke(); } - return x; + } else if (o.hat === "scarf") { + g2.fillStyle = rgba("#f2ece0", 0.97); + g2.beginPath(); + g2.ellipse(-r * 0.05, -r * 0.55, r * 1.04, r * 0.5, 0, Math.PI * 0.98, Math.PI * 2.02); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.lineWidth = iw; + g2.stroke(); + g2.beginPath(); + g2.moveTo(-r * 0.95, -r * 0.5); + g2.quadraticCurveTo(-r * 1.3, -r * 0.3, -r * 1.25, r * 0.1); + g2.lineWidth = iw * 2.5; + g2.strokeStyle = rgba("#e8e0d0", 0.95); + g2.stroke(); } - // ---------- 音效 ---------- - noiseHit(t, dur, type, f0, f1, vel, pan = 0, q2 = 1, send = 0.2, bus) { - const c2 = this.ctx; - const s = c2.createBufferSource(); - s.buffer = this.noise; - s.loop = true; - const f = c2.createBiquadFilter(); - f.type = type; - f.Q.value = q2; - f.frequency.setValueAtTime(f0, t); - f.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + Math.min(0.01, dur * 0.2)); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); - s.connect(f); - f.connect(g2); - this.out(g2, bus ?? this.sfx, pan, send); - s.start(t, t * 0.731 % 1.8); - s.stop(t + dur + 0.05); + if (o.tears && o.tears > 0) { + for (let i = 0; i < 2; i++) { + const q2 = (t * 1.4 + i * 0.5) % 1; + g2.fillStyle = rgba("#7fa4c4", 0.85 * o.tears); + g2.beginPath(); + g2.ellipse(ex + r * 0.05, ey + r * 0.2 + q2 * r * 1.3, r * 0.06, r * 0.1, 0, 0, Math.PI * 2); + g2.fill(); + } } - tone(t, f0, f1, dur, vel, type = "sine", pan = 0, send = 0.2) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = type; - o.frequency.setValueAtTime(f0, t); - o.frequency.exponentialRampToValueAtTime(Math.max(20, f1), t + dur); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + 8e-3); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dur); - o.connect(g2); - this.out(g2, this.sfx, pan, send); - o.start(t); - o.stop(t + dur + 0.05); + if (o.sweat && o.sweat > 0) { + g2.fillStyle = rgba("#9cc0d8", 0.9 * o.sweat); + g2.beginPath(); + g2.ellipse(-r * 0.6, -r * 0.7 + t % 0.8 * r * 0.5, r * 0.08, r * 0.13, 0.3, 0, Math.PI * 2); + g2.fill(); } - wind(t, dur, vel, pan = 0) { - const c2 = this.ctx; - const s = c2.createBufferSource(); - s.buffer = this.noise; - s.loop = true; - const f = c2.createBiquadFilter(); - f.type = "bandpass"; - f.Q.value = 0.8; - const lfo = c2.createOscillator(); - lfo.frequency.value = 0.23; - const lg = c2.createGain(); - lg.gain.value = 260; - f.frequency.value = 520; - lfo.connect(lg); - lg.connect(f.frequency); - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + Math.min(1.5, dur / 3)); - g2.gain.setValueAtTime(vel, t + dur - Math.min(1.5, dur / 3)); - g2.gain.linearRampToValueAtTime(0, t + dur); - s.connect(f); - f.connect(g2); - this.out(g2, this.sfx, pan, 0.1); - s.start(t); - s.stop(t + dur + 0.1); - lfo.start(t); - lfo.stop(t + dur + 0.1); + g2.restore(); + } + var POSE = { + stand: (k = "boy") => k === "boy" ? { hl: [-0.05, -0.39], hr: [0.07, -0.4], fl: [-0.04, 0], fr: [0.05, 0] } : { hl: [-0.06, -0.46], hr: [0.07, -0.47], fl: [-0.05, 0], fr: [0.06, 0] }, + walk: (ph, k = "boy", run = 0) => { + const st = (0.09 + 0.07 * run) * (k === "boy" ? 1 : 1.15), lift = 0.04 + 0.06 * run; + const s1 = Math.sin(ph), s2 = Math.sin(ph + Math.PI); + const armY = k === "boy" ? -0.42 : -0.5; + return { + fl: [st * s1, -Math.max(0, Math.cos(ph)) * lift], + fr: [st * s2, -Math.max(0, Math.cos(ph + Math.PI)) * lift], + hl: [-st * 0.8 * s1 + 0.02, armY + run * 0.08], + hr: [-st * 0.8 * s2 + 0.04 + run * 0.06, armY + run * 0.1], + bob: -Math.abs(Math.sin(ph)) * 0.015 * (1 + run), + lean: 0.06 + 0.16 * run + }; } - chirp(t, vel = 0.06, pan = 0) { - const base = 2600 + this.r() * 1600; - for (let i = 0; i < 2 + Math.floor(this.r() * 3); i++) this.tone(t + i * 0.09, base, base * (1.25 + this.r() * 0.2), 0.06, vel, "sine", pan, 0.3); + }; + function sheep(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 1, t = o.t; + const run = o.run ?? 0, walk = o.walk ?? 0; + const ph = t * (run > 0 ? 11 : walk > 0 ? 5 : 0) + seed; + const bounce = run > 0 ? Math.abs(Math.sin(ph)) * 0.05 * run : walk > 0 ? Math.abs(Math.sin(ph)) * 0.012 : 4e-3 * Math.sin(t * 2 + seed); + const iw = Math.max(1, s * 0.016) / s; + g2.save(); + g2.globalAlpha = o.alpha ?? 1; + g2.translate(o.x, o.y); + g2.scale(face * s, s); + const by = -0.36 - bounce; + const legs = [[-0.2, 0], [-0.12, Math.PI], [0.14, Math.PI * 0.5], [0.22, Math.PI * 1.5]]; + legs.forEach(([lx, off], i) => { + const sw = run > 0 ? Math.sin(ph + off) * 0.1 * run : walk > 0 ? Math.sin(ph + off) * 0.04 : 0; + const top = [lx, by + 0.1], foot2 = [lx + sw, -0 - (run > 0 ? Math.max(0, Math.cos(ph + off)) * 0.04 : 0)]; + brush(g2, [top, lp2(top, foot2, 0.5), foot2], 0.045, { color: i < 2 ? "#4a4440" : "#2e2a27", alpha: 0.92, taperIn: 0, taperOut: 0.1, seed: seed + i }); + }); + const rx = 0.33, ry = o.lamb ? 0.21 : 0.2; + const bumps = []; + const nB = 13; + for (let i = 0; i < nB; i++) { + const a = i / nB * Math.PI * 2; + bumps.push([Math.cos(a) * rx, by + Math.sin(a) * ry]); } - peep(t, vel = 0.12, pan = 0) { - this.tone(t, 2100, 2900, 0.09, vel, "triangle", pan, 0.15); - this.tone(t + 0.13, 2200, 3e3, 0.08, vel * 0.8, "triangle", pan, 0.15); + g2.fillStyle = rgba("#fbf7ee", 0.98); + g2.beginPath(); + g2.ellipse(0, by, rx * 1.02, ry * 1.04, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.7); + g2.lineWidth = iw * 1.1; + for (let i = 0; i < nB; i++) { + const a = i / nB * Math.PI * 2, b = bumps[i]; + const rr = 0.075 + 0.015 * hash(i + seed); + g2.fillStyle = rgba("#fbf7ee", 0.98); + g2.beginPath(); + g2.arc(b[0], b[1], rr, a - 1.3, a + 1.3); + g2.fill(); + g2.stroke(); } - quack(t, vel = 0.14, pan = 0, pitch = 1) { - const c2 = this.ctx; - const o = c2.createOscillator(); - o.type = "sawtooth"; - o.frequency.setValueAtTime(330 * pitch, t); - o.frequency.linearRampToValueAtTime(250 * pitch, t + 0.16); - const f = c2.createBiquadFilter(); - f.type = "bandpass"; - f.frequency.value = 1150 * pitch; - f.Q.value = 3.2; - const f2 = c2.createBiquadFilter(); - f2.type = "peaking"; - f2.frequency.value = 2400; - f2.gain.value = 8; - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t); - g2.gain.linearRampToValueAtTime(vel, t + 0.015); - g2.gain.setValueAtTime(vel, t + 0.11); - g2.gain.exponentialRampToValueAtTime(1e-4, t + 0.2); - o.connect(f); - f.connect(f2); - f2.connect(g2); - this.out(g2, this.sfx, pan, 0.15); - o.start(t); - o.stop(t + 0.25); + blot(g2, -0.02, by + 0.1, 0.3, "#8a8578", 0.22, 1.2, 0.5); + for (let i = 0; i < 5; i++) { + const cx = -0.18 + i * 0.09, cy = by - 0.04 + i % 2 * 0.06; + g2.strokeStyle = rgba("#9a958a", 0.6); + g2.lineWidth = iw; + g2.beginPath(); + g2.arc(cx, cy, 0.025, 0.5, 4.5); + g2.stroke(); + } + g2.fillStyle = rgba("#fbf7ee", 1); + g2.beginPath(); + g2.arc(-rx - 0.02, by - 0.04, 0.05, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.6); + g2.stroke(); + const graze = clamp(o.graze ?? 0), look = o.look ?? 0; + const chew = graze > 0.5 ? Math.sin(t * 7 + seed) * 0.01 : 0; + const hx = lerp(0.36, 0.38, graze) + run * 0.05, hy = lerp(by - 0.1 - look * 0.06, -0.08 + chew, graze); + const ang = lerp(0.35, 1.25, graze) - run * 0.2 - look * 0.35; + g2.save(); + g2.translate(hx, hy); + g2.rotate(ang); + g2.fillStyle = rgba("#fbf7ee", 1); + g2.beginPath(); + g2.arc(-0.03, -0.07, 0.06, 0, Math.PI * 2); + g2.fill(); + const fc = o.lamb ? "#5a4c44" : "#2e2a27"; + g2.fillStyle = rgba(fc, 0.96); + g2.beginPath(); + g2.ellipse(0.04, 0, 0.11, 0.07, 0.15, 0, Math.PI * 2); + g2.fill(); + g2.beginPath(); + g2.ellipse(-0.05, 0.02, 0.06, 0.022, -0.6 - 0.3 * (o.scared ?? 0), 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = "#f6efdf"; + g2.beginPath(); + g2.arc(0.04, -0.02, o.scared ? 0.022 : 0.016, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(0.045, -0.02, 8e-3, 0, Math.PI * 2); + g2.fill(); + if ((o.baa ?? 0) > 0) { + g2.fillStyle = rgba("#8a3a32", 0.95); + g2.beginPath(); + g2.ellipse(0.13, 0.03, 0.02, 0.018 * o.baa, 0, 0, Math.PI * 2); + g2.fill(); } - crack(t, vel = 0.25, pan = 0, big = false) { - const n = big ? 7 : 3; - for (let i = 0; i < n; i++) this.noiseHit(t + i * (0.018 + this.r() * 0.03), 0.03 + this.r() * 0.03, "highpass", 2500 + this.r() * 2500, 1800, vel * (0.6 + this.r() * 0.5), pan, 0.7, 0.1); - if (big) this.tone(t, 160, 70, 0.25, vel * 0.9, "sine", pan, 0.2); + g2.restore(); + g2.restore(); + } + function wolf(g2, o) { + const s = o.s, face = o.face ?? 1, seed = o.seed ?? 3, t = o.t; + const mode = o.mode ?? "stand"; + const iw = Math.max(1.1, s * 0.012) / s; + g2.save(); + g2.globalAlpha = o.alpha ?? 1; + g2.translate(o.x, o.y); + g2.scale(face * s, s); + const run = mode === "run", sneak = mode === "sneak"; + const ph = t * (run ? 12 : sneak ? 3 : 0) + seed; + const low = sneak ? 0.07 : 0; + const bounce = run ? Math.abs(Math.sin(ph)) * 0.04 : 0; + const by = -0.4 + low - bounce; + const stretch = run ? 1.12 : 1; + const fur = "#6d6f73", dark = "#3f4145", belly = "#b9b6ad"; + const tw = run ? -0.1 : sneak ? 0.12 : 0.05 + 0.03 * Math.sin(t * 2); + wash(g2, [[-0.32 * stretch, by - 0.04], [-0.55 * stretch, by + 0.02 - tw], [-0.68 * stretch, by + 0.12 - tw], [-0.6 * stretch, by + 0.16 - tw], [-0.4 * stretch, by + 0.08]], fur, 0.92, { lw: iw }); + brush(g2, spline([[-0.32 * stretch, by - 0.04], [-0.55 * stretch, by + 0.02 - tw], [-0.68 * stretch, by + 0.12 - tw]], 6), iw * 2, { color: dark, alpha: 0.8, dry: 0.7, seed: seed + 1 }); + const legs = [[-0.22, 0, false], [0.2, Math.PI, false], [-0.17, Math.PI * 0.6, true], [0.26, Math.PI * 1.6, true]]; + for (const [lx0, off, front] of legs) { + if (front) continue; + drawLeg(lx0, off, dark); } - flap(t, n, rate, vel = 0.12, pan = 0) { - for (let i = 0; i < n; i++) this.noiseHit(t + i / rate, 0.12, "bandpass", 900, 300, vel * (0.8 + this.r() * 0.3), pan, 0.9, 0.1); + function drawLeg(lx0, off, col) { + const lx = lx0 * stretch; + const sw = run ? Math.sin(ph + off) * 0.16 : sneak ? Math.sin(ph + off) * 0.05 : 0; + const top = [lx, by + 0.08], knee = [lx + sw * 0.5 + (lx < 0 ? -0.04 : 0.02), by + 0.22 - low * 0.5], foot2 = [lx + sw, -(run ? Math.max(0, Math.cos(ph + off)) * 0.06 : 0)]; + brush(g2, [top, knee, foot2], 0.055, { color: col, alpha: 0.95, taperIn: 0, taperOut: 0.35, seed: seed + lx0 * 10 }); } - thump(t, vel = 0.4, f = 95) { - this.tone(t, f, f * 0.55, 0.22, vel, "sine", 0, 0.15); - this.noiseHit(t, 0.05, "lowpass", 1200, 300, vel * 0.5, 0, 0.7, 0.05); + const body = [[0.3 * stretch, by - 0.1], [0.05, by - 0.14], [-0.25 * stretch, by - 0.12], [-0.36 * stretch, by - 0.02], [-0.28 * stretch, by + 0.12], [0, by + 0.12], [0.26 * stretch, by + 0.1], [0.36 * stretch, by + 0]]; + wash(g2, body, fur, 0.95, { lw: iw }); + blot(g2, 0, by + 0.06, 0.3, belly, 0.35, 1.3, 0.3); + outline(g2, body, iw * 1.3, { seed: seed + 2, alpha: 0.85, breaks: [0.02, 0.4, 0.7, 0.98] }); + for (let i = 0; i < 6; i++) { + const x0 = (-0.3 + i * 0.11) * stretch; + brush(g2, [[x0, by - 0.13], [x0 + 0.05, by - 0.17 - 0.02 * hash(i + seed)], [x0 + 0.1, by - 0.12]], 0.025, { color: dark, alpha: 0.75, dry: 0.8, seed: seed + 10 + i, taperIn: 0.1 }); } - shot(t, pan = 0.5) { - this.noiseHit(t, 0.5, "lowpass", 6e3, 200, 0.85, pan, 0.5, 0.6); - this.tone(t, 120, 40, 0.5, 0.6, "sine", pan, 0.6); - this.noiseHit(t + 0.05, 1.6, "lowpass", 900, 120, 0.18, pan, 0.6, 0.9); + for (const [lx0, off, front] of legs) if (front) drawLeg(lx0, off, fur); + const howl2 = mode === "howl", snarl = mode === "snarl"; + const hx = 0.4 * stretch, hy = by - 0.12 + low * 0.6 + (run ? 0.04 : 0); + const ha = howl2 ? -0.8 : sneak ? 0.25 : run ? 0.1 : -0.05; + g2.save(); + g2.translate(hx, hy); + g2.rotate(ha); + wash(g2, [[-0.14, 0.12], [-0.06, -0.07], [0.06, -0.06], [0.08, 0.08], [-0.02, 0.16]], fur, 0.95, { lw: iw }); + const head = [[-0.06, -0.08], [0.06, -0.09], [0.24, -0.03], [0.26, 0.01], [0.2, 0.04], [0.06, 0.07], [-0.06, 0.05]]; + wash(g2, head, fur, 0.97, { lw: iw }); + outline(g2, head, iw * 1.2, { seed: seed + 3, alpha: 0.9, breaks: [0.05, 0.55, 0.95] }); + for (const [ex, k] of [[-0.03, 0.85], [0.03, 1]]) { + g2.fillStyle = rgba(k < 1 ? dark : fur, 0.97); + g2.beginPath(); + g2.moveTo(ex - 0.03, -0.07); + g2.lineTo(ex + 0.01, -0.2 * k); + g2.lineTo(ex + 0.05, -0.08); + g2.closePath(); + g2.fill(); + g2.strokeStyle = rgba(INK, 0.8); + g2.lineWidth = iw; + g2.stroke(); } - swanCall(t, vel = 0.08, pan = -0.3) { - const c2 = this.ctx; - for (const [dt, f] of [[0, 520], [0.32, 620]]) { - const o = c2.createOscillator(); - o.type = "sawtooth"; - o.frequency.setValueAtTime(f, t + dt); - o.frequency.linearRampToValueAtTime(f * 0.94, t + dt + 0.28); - const bp = c2.createBiquadFilter(); - bp.type = "bandpass"; - bp.frequency.value = 1300; - bp.Q.value = 4; - const g2 = c2.createGain(); - g2.gain.setValueAtTime(0, t + dt); - g2.gain.linearRampToValueAtTime(vel, t + dt + 0.04); - g2.gain.exponentialRampToValueAtTime(1e-4, t + dt + 0.3); - o.connect(bp); - bp.connect(g2); - this.out(g2, this.sfx, pan, 0.8); - o.start(t + dt); - o.stop(t + dt + 0.35); + g2.fillStyle = INK; + g2.beginPath(); + g2.arc(0.255, -0.015, 0.018, 0, Math.PI * 2); + g2.fill(); + const open = howl2 ? 0.07 : snarl ? 0.05 + 0.01 * Math.sin(t * 20) : run ? 0.03 : 0; + if (open > 0) { + g2.fillStyle = rgba("#5a1e1a", 0.95); + g2.beginPath(); + g2.moveTo(0.06, 0.04); + g2.lineTo(0.24, 0.02); + g2.lineTo(0.2, 0.02 + open); + g2.lineTo(0.06, 0.05); + g2.closePath(); + g2.fill(); + if (snarl || run) { + g2.fillStyle = "#f6efdf"; + for (let i = 0; i < 3; i++) { + const x = 0.12 + i * 0.035; + g2.beginPath(); + g2.moveTo(x, 0.025); + g2.lineTo(x + 0.01, 0.045); + g2.lineTo(x + 0.02, 0.025); + g2.fill(); + } } + } else { + g2.strokeStyle = INK; + g2.lineWidth = iw; + g2.beginPath(); + g2.moveTo(0.08, 0.045); + g2.lineTo(0.22, 0.03); + g2.stroke(); } - splash(t, vel = 0.35) { - this.noiseHit(t, 0.6, "lowpass", 3e3, 400, vel, 0, 0.6, 0.3); - for (let i = 0; i < 6; i++) this.tone(t + 0.08 + this.r() * 0.4, 900 + this.r() * 900, 1500 + this.r() * 900, 0.05, vel * 0.2, "sine", this.r() - 0.5, 0.3); + const glow = o.glow ?? (sneak ? 0.6 : 0.3); + blot(g2, 0.08, -0.035, 0.05, "#c8f05a", 0.6 * glow); + g2.fillStyle = "#c8e050"; + g2.beginPath(); + g2.ellipse(0.08, -0.035, 0.025, 0.014, -0.25, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = INK; + g2.beginPath(); + g2.ellipse(0.085, -0.035, 6e-3, 0.012, 0, 0, Math.PI * 2); + g2.fill(); + g2.strokeStyle = INK; + g2.lineWidth = iw * 1.3; + g2.beginPath(); + g2.moveTo(0.04, -0.06); + g2.lineTo(0.12, -0.045); + g2.stroke(); + if (o.carry) { + g2.save(); + g2.translate(0.16, 0.16); + g2.rotate(-0.2); + sheep(g2, { x: 0, y: 0, s: 0.42, t, face: -1, lamb: true, scared: 1, seed: 9 }); + g2.restore(); } - heart(t) { - this.tone(t, 70, 50, 0.16, 0.28, "sine", 0, 0.05); - this.tone(t + 0.2, 64, 48, 0.14, 0.2, "sine", 0, 0.05); + g2.restore(); + g2.restore(); + } + function eyesInDark(g2, x, y, s, t, a) { + if (a <= 0) return; + const blink = (t + 0.3) % 2.6 < 0.12 ? 0.15 : 1; + for (const dx of [-0.5, 0.5]) { + blot(g2, x + dx * s, y, s * 0.9, "#c8f05a", 0.35 * a); + g2.fillStyle = rgba("#d8f070", a); + g2.beginPath(); + g2.ellipse(x + dx * s, y, s * 0.32, s * 0.16 * blink, dx * 0.4, 0, Math.PI * 2); + g2.fill(); + g2.fillStyle = rgba(INK, a); + g2.beginPath(); + g2.ellipse(x + dx * s, y, s * 0.06, s * 0.14 * blink, 0, 0, Math.PI * 2); + g2.fill(); } - clap(t, vel = 0.08, pan = -0.5) { - this.noiseHit(t, 0.06, "bandpass", 1600 + this.r() * 600, 900, vel, pan, 1.5, 0.2); + } + function cottage(g2, x, y, s, seed) { + const r = rng(seed); + const wall = [[x - s * 0.5, y], [x - s * 0.5, y - s * 0.45], [x + s * 0.5, y - s * 0.45], [x + s * 0.5, y]]; + wash(g2, wall, "#d9cba8", 0.92); + outline(g2, wall, Math.max(1.2, s * 0.012), { seed, alpha: 0.75 }); + wash(g2, [[x - s * 0.1, y], [x - s * 0.1, y - s * 0.3], [x + s * 0.08, y - s * 0.3], [x + s * 0.08, y]], "#4a3a2a", 0.85); + wash(g2, [[x + s * 0.22, y - s * 0.2], [x + s * 0.22, y - s * 0.33], [x + s * 0.38, y - s * 0.33], [x + s * 0.38, y - s * 0.2]], "#3a3530", 0.8); + const roof = [[x - s * 0.66, y - s * 0.4], [x - s * 0.3, y - s * 0.82], [x + s * 0.3, y - s * 0.84], [x + s * 0.66, y - s * 0.4]]; + wash(g2, roof, "#b8955a", 0.92); + for (let i = 0; i < 26; i++) { + const u = r(), px = lerp(x - s * 0.62, x + s * 0.62, u), top = y - s * 0.8 + Math.abs(u - 0.5) * s * 0.7; + brush(g2, [[px - s * 0.02, top], [px + s * 0.01, lerp(top, y - s * 0.42, 0.6)], [px + s * 0.02, y - s * 0.4]], Math.max(1, s * 0.01), { color: "#7a5a2a", alpha: 0.55, seed: seed + i, taperIn: 0.1, dry: 0.4 }); } - swish(t, vel = 0.08) { - this.noiseHit(t, 0.35, "bandpass", 2500, 900, vel, 0, 1, 0.25); + brush(g2, [[x - s * 0.68, y - s * 0.39], [x, y - s * 0.43], [x + s * 0.68, y - s * 0.39]], Math.max(1.5, s * 0.025), { color: "#6a4a22", alpha: 0.8, seed: seed + 50, dry: 0.5 }); + } + function chimneySmoke(g2, x, y, s, t, seed, a = 0.4) { + for (let i = 0; i < 6; i++) { + const q2 = (t * 0.18 + i / 6 + hash(seed)) % 1; + blot(g2, x + Math.sin(q2 * 5 + i) * s * 0.15 + q2 * s * 0.4, y - q2 * s * 1.6, s * (0.08 + q2 * 0.25), "#9a958c", a * (1 - q2)); } - drop(t) { - this.tone(t, 700, 1500, 0.12, 0.3, "sine", 0, 0.5); - this.noiseHit(t, 0.08, "highpass", 3e3, 2e3, 0.1, 0, 0.7, 0.4); + } + function terraces(g2, x0, y0, x1, y1, rows, seed) { + const r = rng(seed); + for (let i = 0; i < rows; i++) { + const ya = lerp(y0, y1, i / rows), yb = lerp(y0, y1, (i + 1) / rows); + const pts = [], pts2 = []; + for (let k = 0; k <= 12; k++) { + const u = k / 12, x = lerp(x0, x1, u); + pts.push([x, ya + Math.sin(u * 3 + i + seed) * (yb - ya) * 0.25]); + pts2.push([x, yb + Math.sin(u * 3 + i + 1 + seed) * (yb - ya) * 0.25]); + } + const col = i % 2 ? "#a9b86a" : "#c9b56a"; + wash(g2, [...pts, ...pts2.reverse()], col, 0.42 + 0.1 * r(), { closed: true }); + brush(g2, pts, Math.max(1.2, (yb - ya) * 0.06), { color: "#5a5a32", alpha: 0.55, seed: seed + i, dry: 0.5, jitter: 0.4 }); + for (let k = 0; k < 14; k++) { + const x = lerp(x0, x1, r()), y = lerp(ya, yb, 0.3 + 0.5 * r()); + brush(g2, [[x, y], [x + 1, y - (yb - ya) * 0.18]], Math.max(1, (yb - ya) * 0.04), { color: "#4f6a3e", alpha: 0.6, seed: seed + k * 7 }); + } } - lap(t0, t1, vel = 0.05) { - for (let t = t0; t < t1; t += 0.6 + this.r() * 0.9) this.noiseHit(t, 0.5, "bandpass", 700 + this.r() * 300, 300, vel, this.r() * 1.2 - 0.6, 1.5, 0.3); + } + function rock(g2, x, y, w, h, seed) { + const loop = [[x - w * 0.5, y], [x - w * 0.46, y - h * 0.55], [x - w * 0.2, y - h * 0.95], [x + w * 0.15, y - h], [x + w * 0.45, y - h * 0.6], [x + w * 0.52, y]]; + wash(g2, loop, "#8a8a82", 0.85); + outline(g2, loop, Math.max(1.5, w * 0.02), { seed, alpha: 0.85, breaks: [0, 0.5, 0.86] }); + const r = rng(seed); + for (let i = 0; i < 8; i++) { + const px = x + (r() - 0.5) * w * 0.7, py = y - h * (0.2 + r() * 0.6); + brush(g2, [[px, py], [px + w * 0.08, py + h * 0.08], [px + w * 0.12, py + h * 0.2]], Math.max(1.2, w * 0.018), { color: "#3a3a36", alpha: 0.6, dry: 0.7, seed: seed + i }); } - }; - function score(m) { - const B = 60 / 72; - const S = sceneStart, D = sceneDur; - const r = rng(808); - const arp = (t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) => { - let i = 0; - for (let t2 = t0; t2 < t1 - 0.05; t2 += step, i++) { - const ch = chords[Math.floor((t2 - t0) / (step * 8)) % chords.length]; - m.pluck(t2, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.8 }); - } - }; - const G = [N.G3, N.D4, N.B4], Em = [N.E3, N.B3, N.G4], Dm = [N.D3, N.A3, N.D4], Am = [N.A2, N.E3, N.A3], Gh = [N.G4, N.B4, N.D5]; - let t = S("title"); - m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5], 0.07, 0.28); - m.pad(t + 0.5, S("summer") + 1, [N.G2, N.D3], 0.05); - m.melody(t + 2.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1.5]], 0.17); - t = S("summer"); - m.pad(t, S("hatch") + 0.5, [N.G2, N.D3, N.B3], 0.045); - arp(t + 0.3, S("hatch"), [G, Em, G, Dm], B / 2, 0.2); - m.melody(t + 1.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["E5", 1], ["D5", 1], ["B4", 1], ["D5", 0.5], ["B4", 0.5], ["A4", 2], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.13); - t = S("hatch"); - for (let i = 0; i < Math.floor(D("hatch") / B); i++) { - if (i % 2 === 0) m.block(t + i * B, 0.07, 1100, -0.3); - m.pluck(t + i * B + B / 2, [N.G4, N.B4, N.D5, N.E5][i % 4], 0.13, 0.3, { dur: 0.5, bright: 0.9 }); + blot(g2, x - w * 0.15, y - h * 0.7, w * 0.25, "#f2ecd8", 0.35); + } + function bigTree(g2, x, y, h, t, seed, o = {}) { + const col = o.color ?? "#5f8a4c"; + brush(g2, spline([[x, y], [x - h * 0.03, y - h * 0.3], [x + h * 0.02, y - h * 0.55]], 8), h * 0.08, { color: "#3a3027", alpha: 0.92, seed, dry: 0.5, jitter: 0.3, taperIn: 0 }); + brush(g2, spline([[x, y - h * 0.4], [x + h * 0.12, y - h * 0.52], [x + h * 0.22, y - h * 0.6]], 6), h * 0.035, { color: "#3a3027", alpha: 0.9, seed: seed + 1 }); + brush(g2, spline([[x, y - h * 0.45], [x - h * 0.14, y - h * 0.56], [x - h * 0.22, y - h * 0.62]], 6), h * 0.03, { color: "#3a3027", alpha: 0.9, seed: seed + 2 }); + const r = rng(seed); + for (let i = 0; i < 14; i++) { + const a = r() * Math.PI * 2, d = r() * h * 0.32; + const cx = x + Math.cos(a) * d * 1.2 + Math.sin(t * 0.8 + i) * h * 4e-3, cy = y - h * 0.72 + Math.sin(a) * d * 0.6; + blot(g2, cx, cy, h * (0.12 + r() * 0.08), i % 3 ? col : mixHex(col, "#2a3a22", 0.45), 0.42 + 0.1 * (o.dark ?? 0)); + } + for (let i = 0; i < 40; i++) { + const a = r() * Math.PI * 2, d = r() * h * 0.38; + g2.fillStyle = rgba("#2f4a28", 0.5); + g2.beginPath(); + g2.arc(x + Math.cos(a) * d * 1.2, y - h * 0.72 + Math.sin(a) * d * 0.6, h * 8e-3, 0, Math.PI * 2); + g2.fill(); } - m.pad(t, S("bigegg"), [N.G2, N.D3], 0.035); - t = S("bigegg"); - const BE = CUE.bigegg; - m.pad(t, t + BE.open, [N.E2, N.B2], 0.05, 500); - for (let x = t + 0.6; x < t + BE.open - 0.05; x += 0.09) m.pluck(x, N.E3, 0.06 + 0.1 * (x - t) / BE.open, 0, { dur: 0.3, send: 0.1 }); - m.pluck(t + BE.surprise, N.D4, 0.3, 0, { bend: -2 }); - m.pluck(t + BE.surprise + 0.25, N.A3, 0.3, 0, { bend: -2 }); - m.pluck(t + BE.question + 0.05, N.B4, 0.22, 0.2, { bend: 2 }); - arp(t + BE.walk, S("bullied"), [Em, G], B / 2, 0.12); - t = S("bullied"); - m.pad(t, S("leave") + 0.5, [N.E2, N.B2, N.G3], 0.05, 700); - for (let i = 0; i < 12; i++) m.pluck(t + 0.3 + i * B, [N.E4, N.D4, N.B3, N.A3, N.G3, N.A3, N.B3, N.E3][i % 8], 0.16, -0.2, { dur: 2, bend: i % 4 === 3 ? -1 : 0 }); - t = S("leave"); - m.pad(t, S("marsh") + 0.3, [N.E2, N.B2], 0.045, 600); - m.melody(t + 0.4, B * 1.1, [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["E4", 2]], 0.13); - t = S("marsh"); - for (let x = t + 0.2; x < t + CUE.marsh.shots[0] - 0.02; x += 0.125) m.pluck(x, Math.floor((x - t) / 0.5) % 2 ? N.B2 : N.E2, 0.17, 0, { dur: 0.25, send: 0.05 }); - m.pad(t + CUE.marsh.hide, S("autumn") + 0.5, [N.E2], 0.04, 400); - for (let x = t + CUE.marsh.hide + 0.6; x < t + 13; x += 0.95) m.heart(x); - t = S("autumn"); - m.pad(t, S("winter") + 0.5, [N.E2, N.B2, N.G3], 0.04, 800); - arp(t + 0.3, S("winter"), [Em, Am, G, Em], B * 0.75, 0.13); - m.bell(t + CUE.autumn.swans + 0.6, N.B5, 0.07, 0.4); - m.bell(t + CUE.autumn.swans + 3, N.E6, 0.06, -0.3); - m.melody(t + CUE.autumn.swans + 1.4, B, [["B4", 1.5], ["D5", 0.5], ["E5", 1], ["G5", 1], ["E5", 2], ["D5", 1], ["B4", 1], ["A4", 2]], 0.11, 0); - t = S("winter"); - m.pad(t, S("spring") + 0.5, [N.E2, N.B2], 0.035, 350); - for (let i = 0; i < 6; i++) m.bell(t + 1 + i * 2.1, [N.E5, N.B4, N.D5, N.A4, N.B4, N.E5][i], 0.05, i % 2 ? 0.4 : -0.4); - t = S("spring"); - m.bell(t + 0.15, N.G5, 0.1, 0); - m.bell(t + 0.4, N.D6, 0.07, 0.3); - m.pad(t, S("reflection") + 0.5, [N.G2, N.D3, N.B3], 0.045); - const SP = CUE.spring; - arp(t + 0.5, t + SP.flapHard, [G, Em], B / 2, 0.15); - arp(t + SP.flapHard, S("reflection"), [G, Dm, Em, G], B / 4, 0.14, [0, 1, 2, 1, 2, 1]); - m.gliss(t + SP.lift - 0.05, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4, N.D5, N.E5, N.G5], 0.05, 0.25); - m.melody(t + SP.lift + 0.4, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["E5", 0.5], ["D5", 2]], 0.13); - t = S("reflection"); - const RF = CUE.reflection; - m.pad(t, t + RF.reveal, [N.E2, N.B2, N.E3], 0.04, 600); - for (let i = 0; i < 10; i++) m.pluck(t + 0.8 + i * B * 1.05, [N.B3, N.D4, N.E4, N.D4, N.B3, N.A3, N.B3, N.D4, N.E4, N.G4][i], 0.12, 0, { dur: 2 }); - m.bell(t + RF.mirror, N.E5, 0.06, 0); - m.bell(t + RF.mirror + 0.3, N.B5, 0.04, 0.3); - m.gliss(t + RF.reveal - 0.05, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5, N.B5, N.D6], 0.06, 0.3); - [N.G5, N.B5, N.D6, N.G5].forEach((x, i) => m.bell(t + RF.reveal + i * 0.18, x, 0.08, i % 2 ? 0.3 : -0.3)); - m.pad(t + RF.reveal, S("accepted") + 0.5, [N.G2, N.D3, N.B3, N.D4], 0.05, 1200); - m.melody(t + RF.reveal + 1.2, B, [["G5", 1.5], ["E5", 0.5], ["D5", 1], ["B4", 1], ["D5", 2]], 0.14); - t = S("accepted"); - arp(t + 0.2, S("fly"), [G, Em, Dm, G], B / 2, 0.15); - m.pad(t, S("fly") + 0.3, [N.G2, N.D3], 0.04); - m.melody(t + 0.8, B, [["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 0.5], ["G4", 0.5], ["G4", 2]], 0.12); - t = S("fly"); - m.pad(t, S("ending") + 1, [N.G2, N.D3, N.G3, N.B3], 0.06, 1600); - arp(t, S("ending"), [Gh, G, Em, Gh], B / 4, 0.16, [0, 1, 2, 1, 2, 1, 0, 2]); - for (let i = 0; i < Math.floor(D("fly") / B); i++) m.block(t + i * B, 0.08, 800, 0); - m.melody(t + 0.2, B, [["D5", 1], ["E5", 0.5], ["G5", 0.5], ["A5", 1], ["G5", 1], ["E5", 1], ["G5", 0.5], ["A5", 0.5], ["B5", 2]], 0.15, 0); - t = S("ending"); - m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); - const EN = CUE.ending.end; - arp(t + 0.2, t + EN - 0.2, [G, Em, G, Dm], B * 0.75, 0.12); - m.gliss(t + EN, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.22); - m.bell(t + EN + 0.1, N.G5, 0.08); - m.bell(t + EN + 0.35, N.D6, 0.05, 0.3); - m.pluck(t + EN + 0.6, N.G3, 0.2, 0, { dur: 2 }); - m.pluck(t + EN + 0.6, N.D4, 0.15, 0, { dur: 2 }); - m.pluck(t + EN + 0.6, N.G4, 0.12, 0, { dur: 2 }); - void r; } - function effects(m) { - const S = sceneStart, D = sceneDur; - const r = m.r; - let t = S("title"); - const TT = CUE.title; - m.drop(t + TT.drop); - m.swish(t + TT.write); - m.swish(t + TT.write + 0.45); - m.swish(t + TT.write + 0.9); - m.thump(t + TT.seal + 0.01, 0.45); - for (let i = 0; i < 6; i++) m.peep(t + 2.6 + i * 0.65, 0.04, 0.3); - t = S("summer"); - m.wind(t, D("summer"), 0.025); - for (let x = t + 0.6; x < t + D("summer"); x += 1.1 + r() * 1.6) m.chirp(x, 0.035, r() * 1.4 - 0.7); - t = S("hatch"); - for (let k = 0; k < 4; k++) { - const tc = t + 0.4 + k * 0.75; - m.crack(tc + 0.05, 0.22, -0.3 + k * 0.2); - m.crack(tc + 0.25, 0.18, -0.3 + k * 0.2); - m.noiseHit(tc + 0.55, 0.08, "bandpass", 1800, 900, 0.18, -0.3 + k * 0.2, 2, 0.1); - m.peep(tc + 0.75, 0.1, -0.3 + k * 0.2); - for (let p = 1; p < 5; p++) { - const pt = tc + 1 + 2.3 * (p - 1); - if (pt - t < D("hatch")) m.peep(pt + 0.05, 0.07, -0.3 + k * 0.2); + function bush(g2, x, y, w, seed, dark = 0) { + const r = rng(seed); + for (let i = 0; i < 7; i++) blot(g2, x + (r() - 0.5) * w, y - r() * w * 0.35, w * (0.22 + r() * 0.15), mixHex("#4f6a3e", "#1e2a1c", dark), 0.55 + 0.25 * dark, 1.3, 0.8); + for (let i = 0; i < 10; i++) { + const px = x + (r() - 0.5) * w * 1.1, py = y - r() * w * 0.5; + brush(g2, [[px, py], [px + w * 0.05 * (r() - 0.5), py - w * 0.12]], Math.max(1, w * 0.02), { color: "#1e2a1c", alpha: 0.6, seed: seed + i }); + } + } + function stormCloud(g2, x, y, w, a, seed) { + if (a <= 0) return; + const r = rng(seed); + for (let i = 0; i < 8; i++) blot(g2, x + (r() - 0.5) * w, y + (r() - 0.5) * w * 0.15, w * (0.2 + r() * 0.15), "#3e4248", a * 0.55, 1.5, 0.6); + brush(g2, spline([[x - w * 0.5, y + w * 0.08], [x - w * 0.15, y + w * 0.12], [x + w * 0.2, y + w * 0.09], [x + w * 0.5, y + w * 0.13]], 6), Math.max(1.5, w * 0.01), { color: "#2a2c30", alpha: a * 0.7, dry: 0.6, seed }); + } + function flowers(g2, x0, x1, y0, y1, n, seed, a = 0.8) { + const r = rng(seed); + for (let i = 0; i < n; i++) { + const x = lerp(x0, x1, r()), y = lerp(y0, y1, r()), c2 = ["#e8d06a", "#f2f0e6", "#d88aa0"][i % 3]; + g2.fillStyle = rgba(c2, a); + g2.beginPath(); + g2.arc(x, y, 2 + r() * 3, 0, Math.PI * 2); + g2.fill(); + } + } + function shoutText(g2, text, x0, y0, dirx, size, t, o = {}) { + if (t <= 0) return; + const chars = [...text], per = o.per ?? 0.16, col = o.color ?? "#b8322a", sc = o.scatter ?? 0, seed = o.seed ?? 1; + chars.forEach((ch, i) => { + const q2 = t - i * per; + if (q2 <= 0) return; + const p = smooth(q2 / 0.5); + const x = x0 + dirx * (size * 0.45 + i * size * 0.98 * p + q2 * size * 0.25), y = y0 - size * 0.1 * Math.sin(i * 1.3) - q2 * size * 0.08; + const k = lerp(0.35, 1, p) * (1 + 0.04 * Math.sin(q2 * 30) * (1 - p)); + let a = Math.min(1, q2 * 5) * (o.fade ?? 1); + g2.save(); + g2.translate(x, y); + g2.rotate(-0.08 * dirx + 0.05 * Math.sin(i + q2 * 3)); + if (sc > 0) { + const s2 = smooth(sc), r = rng(seed * 31 + i); + a *= 1 - s2 * 0.9; + for (let qd = 0; qd < 4; qd++) { + const ox = (qd % 2 ? 1 : -1) * size * 0.5, oy = (qd < 2 ? -1 : 1) * size * 0.5; + g2.save(); + g2.beginPath(); + g2.rect(qd % 2 ? 0 : -size, qd < 2 ? -size : 0, size, size); + g2.clip(); + g2.translate(ox * s2 * 0.8 + s2 * size * 1.5 * dirx * (0.5 + r()), oy * s2 * 0.6 - s2 * size * 0.6 * r()); + g2.rotate(s2 * (r() - 0.5) * 1.5); + g2.globalAlpha *= a; + g2.fillStyle = col; + g2.font = `${size * k}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.fillText(ch, 0, 0); + g2.restore(); + } + for (let d = 0; d < 6; d++) { + const r2 = rng(seed + i * 7 + d); + g2.fillStyle = rgba(col, 0.6 * s2 * (1 - s2)); + g2.beginPath(); + g2.arc(dirx * s2 * size * (1 + r2() * 2), (r2() - 0.5) * size * 1.5 * s2, size * 0.04 * (1 + r2()), 0, Math.PI * 2); + g2.fill(); + } + } else { + g2.globalAlpha *= a; + g2.font = `${size * k}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.shadowColor = rgba(col, 0.35); + g2.shadowBlur = size * 0.06; + g2.lineJoin = "round"; + g2.strokeStyle = rgba(PAPER, 0.7); + g2.lineWidth = size * 0.08; + g2.strokeText(ch, 0, 0); + g2.fillStyle = col; + g2.fillText(ch, 0, 0); } - m.noiseHit(tc + 3, 0.05, "lowpass", 600, 200, 0.12, -0.3 + k * 0.2, 0.7, 0.05); + g2.restore(); + }); + } + function shoutWaves(g2, x, y, s, t, dirx, a = 1) { + for (let k = 0; k < 3; k++) { + const q2 = (t * 1.6 + k / 3) % 1; + g2.strokeStyle = rgba(INK, a * 0.6 * (1 - q2)); + g2.lineWidth = Math.max(1.2, s * 0.03); + g2.beginPath(); + g2.arc(x, y, s * (0.3 + q2 * 1.4), dirx > 0 ? -0.6 : Math.PI - 0.6, dirx > 0 ? 0.6 : Math.PI + 0.6); + g2.stroke(); + } + } + + // src/films/wolf/cues.ts + var CUE2 = { + title: { drop: 0.32, write: 1.1, seal: 4.55 }, + village: { boyIn: 4.8 }, + bored: { chew: 6.1, yawn: 8, idea: 9.8, sly: 10.3 }, + shout1: { hop: 0.6, cup: 2.4, shouts: [4.2, 5.6, 7] }, + run1: { hear: 0.6, grab: 2.8, run: 4.6, arrive: 7.3 }, + laugh1: { laugh: 0.5, angry: 4.6, leave: 6.6 }, + shout2: { days: 0.5, shouts: [4.1, 5.5], run: 7.2 }, + warn: { laugh: 0.5, point: 3.1 }, + wolf: { dark: 0.3, bush: 4, eyes: 4.9, creep: 7.3, reveal: 9.3 }, + cry: { scared: 0.5, toRock: 2.3, shouts: [4.1, 5.3, 7.2] }, + noone: { look: 0.6, shake: 2.6, wave: 5.2, work: 8 }, + chaos: { dash: 0.4, scatter: 1.8, grab: 4 }, + regret: { hug: 1.8, cry: 4.4 }, + moral: { write: 4.9, end: 8.2 } + }; + + // src/films/wolf/scenes.ts + var ks2 = (s) => s.u * (s.portrait ? 0.95 : 1); + var cam2 = (s, z, x, y, extra = {}) => ({ z: z * (s.portrait ? 1.22 : 1), x, y, ...extra }); + function sky2(g2, W2, y1, top, a0, bottom, a1) { + const gr = g2.createLinearGradient(0, 0, 0, y1); + gr.addColorStop(0, rgba(top, a0)); + gr.addColorStop(1, rgba(bottom, a1)); + g2.fillStyle = gr; + g2.fillRect(0, 0, W2, y1); + } + function band2(g2, W2, y0, y1, color, a, seed, rough = 18) { + const pts = []; + for (let i = 0; i <= 24; i++) pts.push([i / 24 * W2, y0 + nz(i * 0.6, seed) * rough]); + const gr = g2.createLinearGradient(0, y0 - rough, 0, y1); + gr.addColorStop(0, rgba(color, a)); + gr.addColorStop(1, rgba(color, a * 0.5)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, y1); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, y1); + g2.closePath(); + g2.fill(); + brush(g2, pts, 2.2, { color, alpha: Math.min(1, a * 1.3), seed, jitter: 0.6, dry: 0.5 }); + } + function strokesH2(g2, W2, y0, y1, n, color, a, seed, u) { + const r = rng(seed); + for (let i = 0; i < n; i++) { + const y = lerp(y0, y1, r()), x = r() * W2, len = (30 + r() * 120) * u; + brush(g2, [[x, y], [x + len / 2, y + (r() - 0.5) * 4 * u], [x + len, y]], (1.5 + r() * 2.5) * u, { color, alpha: a * (0.5 + r() * 0.5), seed: seed + i, dry: 0.6, jitter: 0.5 }); } - t = S("bigegg"); - const BE = CUE.bigegg; - for (let x = t + 0.5; x < t + BE.crackFrom; x += Math.max(0.12, 0.5 - (x - t) * 0.12)) m.block(x, 0.05 + (x - t) * 0.02, 420, 0); - m.crack(t + BE.crackFrom + 0.05, 0.2); - m.crack(t + BE.crackFrom + 0.4, 0.22); - m.crack(t + BE.open, 0.35, 0, true); - m.noiseHit(t + BE.surprise, 0.25, "bandpass", 2400, 1200, 0.06, -0.4, 1.2, 0.2); - m.peep(t + BE.surprise + 0.1, 0.08, -0.4); - m.peep(t + BE.surprise + 0.2, 0.08, -0.5); - for (let x = t + BE.walk + 0.1; x < t + BE.walk + 2.6; x += 0.6) m.noiseHit(x, 0.05, "lowpass", 600, 200, 0.1, 0, 0.7, 0.05); - t = S("bullied"); - const BU = CUE.bullied; - for (let x = t + BU.laugh1[0] + 0.1; x < t + BU.laugh1[1] - 0.2; x += 0.35 + r() * 0.3) m.quack(x, 0.09, -0.5 + r() * 0.3, 1.25 + r() * 0.2); - for (let x = t + BU.laugh2[0] + 0.1; x < t + BU.laugh2[1] - 0.2; x += 0.4 + r() * 0.35) m.quack(x, 0.07, -0.5 + r() * 0.3, 1.25 + r() * 0.2); - m.flap(t + BU.roosterIn, 6, 9, 0.12, 0.5); - m.tone(t + BU.roosterIn + 0.2, 600, 900, 0.25, 0.08, "sawtooth", 0.5, 0.3); - m.tone(t + BU.roosterIn + 0.45, 900, 500, 0.5, 0.07, "sawtooth", 0.5, 0.4); - for (const p of BU.pecks) { - m.block(t + p, 0.25, 1500, 0.2); - m.flap(t + p - 0.15, 2, 10, 0.08, 0.3); + } + function particles2(n, seed, fn) { + const r = rng(seed); + for (let i = 0; i < n; i++) fn(r, i); + } + function writeText2(g2, text, x, y, fs, t0, per, t, o = {}) { + const chars = [...text], gap = o.gap ?? 1.08; + g2.save(); + g2.font = `${fs}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + chars.forEach((ch, i) => { + const off = (i - (chars.length - 1) / 2) * fs * gap; + const cx = o.vertical ? x : x + off, cy = o.vertical ? y + off : y; + const p = inv(t0 + i * per, t0 + i * per + per * 1.2, t); + if (p <= 0) return; + g2.save(); + g2.beginPath(); + g2.rect(cx - fs * 0.7, cy - fs * 0.7, fs * 1.4, fs * 1.4 * easeOut(p)); + g2.clip(); + g2.shadowColor = rgba(INK, 0.35); + g2.shadowBlur = fs * 0.06; + g2.fillStyle = o.color ?? INK; + g2.fillText(ch, cx, cy); + g2.restore(); + }); + g2.restore(); + } + function marks(g2, x, y, s, a, txt, color = INK) { + if (a <= 0) return; + g2.save(); + g2.globalAlpha = a; + g2.fillStyle = color; + g2.font = `${s}px ${FONT_BRUSH}`; + g2.textAlign = "center"; + g2.textBaseline = "middle"; + g2.fillText(txt, x, y); + g2.restore(); + } + function sparkle(g2, x, y, s, a) { + if (a <= 0) return; + for (let i = 0; i < 4; i++) { + const ang = i * Math.PI / 2 + 0.3; + brush(g2, [[x + Math.cos(ang) * s * 0.3, y + Math.sin(ang) * s * 0.3], [x + Math.cos(ang) * s, y + Math.sin(ang) * s]], s * 0.12, { alpha: a * 0.85, taperIn: 0.1, taperOut: 0.6, seed: i }); } - m.swish(t + BU.shoo, 0.07); - t = S("leave"); - m.wind(t, D("leave") + 1, 0.07, -0.2); - for (let x = t + CUE.leave.walk + 0.2; x < t + D("leave"); x += 0.62) m.noiseHit(x, 0.04, "lowpass", 500, 200, 0.05, 0, 0.7, 0.05); - t = S("marsh"); - m.lap(t, t + D("marsh"), 0.035); - const MA = CUE.marsh; - for (let x = t + MA.laugh[0] + 0.1; x < t + MA.laugh[1] - 0.2; x += 0.3 + r() * 0.3) m.quack(x, 0.1, 0.4 + r() * 0.3, 0.95 + r() * 0.15); - m.shot(t + MA.shots[0], 0.6); - m.shot(t + MA.shots[1], 0.45); - m.flap(t + MA.scatter, 14, 12, 0.13, 0.4); - for (let i = 0; i < 6; i++) m.quack(t + MA.scatter + 0.05 + i * 0.12, 0.08, 0.5, 1.1); - m.splash(t + MA.hide + 0.2, 0.12); - t = S("autumn"); - m.wind(t, D("autumn"), 0.03, 0.2); - const AU = CUE.autumn.swans; - m.swanCall(t + AU + 0.8, 0.06, 0.5); - m.swanCall(t + AU + 3, 0.07, 0.1); - m.swanCall(t + AU + 5.6, 0.05, -0.5); - m.flap(t + AU + 1.5, 12, 1.4, 0.04, 0.2); - t = S("winter"); - m.wind(t, D("winter") + 0.8, 0.12, 0); - m.wind(t + 2, D("winter") - 2, 0.06, 0.6); - m.crack(t + CUE.winter.ice, 0.2, 0, true); - m.noiseHit(t + CUE.winter.ice + 0.05, 0.9, "bandpass", 300, 120, 0.12, 0, 4, 0.5); - t = S("spring"); - for (let x = t + 0.4; x < t + D("spring"); x += 0.7 + r() * 1.1) m.chirp(x, 0.04, r() * 1.4 - 0.7); - const SP = CUE.spring; - m.flap(t + SP.spread, 2, 2, 0.1, 0); - m.flap(t + SP.spread + 0.9, Math.floor((SP.flapHard - SP.spread - 0.9) * 0.83), 0.83, 0.08, 0); - m.flap(t + SP.flapHard, Math.floor((SP.lift - SP.flapHard) * 1.6), 1.6, 0.13, 0); - m.flap(t + SP.lift, 6, 1.46, 0.1, 0.2); - m.noiseHit(t + SP.lift - 0.1, 1.2, "bandpass", 400, 2400, 0.12, 0, 1, 0.4); - m.pluck(t + SP.question, N.B4, 0.2, 0, { bend: 2 }); - t = S("reflection"); - const RF = CUE.reflection; - m.flap(t, 5, 7, 0.1, -0.2); - m.splash(t + RF.land, 0.3); - m.lap(t + 1.2, t + D("reflection"), 0.03); - m.bell(t + RF.reveal, 91, 0.06, 0, m.sfx); - t = S("accepted"); - m.lap(t, t + D("accepted"), 0.03); - const CH = CUE.accepted.cheer; - for (let x = t + CH + 0.1; x < t + CH + 4.5; x += 0.11 + r() * 0.08) m.clap(x, 0.06 * (1 - (x - t - CH) / 5), -0.55 + r() * 0.2); - for (let i = 0; i < 4; i++) m.chirp(t + CH + 0.4 + i * 1.3, 0.03, 0.4); - t = S("fly"); - m.wind(t, D("fly") + 1, 0.07, 0); - m.flap(t + 0.3, Math.floor(D("fly") * 3.2 / 2 / Math.PI * 2), 3.2 / Math.PI, 0.09, 0); - m.noiseHit(t + 0.9, 1.6, "bandpass", 300, 2e3, 0.1, 0, 1, 0.5); - t = S("ending"); - m.lap(t, t + D("ending"), 0.025); - m.thump(t + CUE.ending.end + 0.12, 0.4); } - var cached = null; - function buildSoundtrack(voiceUrl, onStage) { - if (cached) return cached; - cached = (async () => { - const len = Math.ceil(SR * (TOTAL + 0.3)); - const ctx = new OfflineAudioContext(2, len, SR); - onStage?.("\u52A0\u8F7D\u65C1\u767D\u2026"); - let voiceBuf = null; - try { - const ab = await (await fetch(voiceUrl)).arrayBuffer(); - voiceBuf = await ctx.decodeAudioData(ab); - } catch (e) { - console.warn("voice load failed", e); + function hoePose(ph, kind) { + const st = 0.5 + 0.5 * Math.sin(ph); + const th = lerp(-2.05, 0.95, Math.pow(st, 1.6)); + const piv = [0.06, -0.6]; + const d = [Math.cos(th), Math.sin(th)]; + const a = [piv[0] - d[0] * 0.12, piv[1] - d[1] * 0.12], b = [piv[0] + d[0] * 0.78, piv[1] + d[1] * 0.78]; + return { hl: [piv[0] - d[0] * 0.08, piv[1] - d[1] * 0.08], hr: [piv[0] + d[0] * 0.14, piv[1] + d[1] * 0.14], fl: [-0.1, 0], fr: [0.12, 0], lean: 0.08 + 0.18 * st, tool: { kind: "hoe", a, b } }; + void kind; + } + function carryPose(ph, kind, run, tool = "hoe") { + const w = POSE.walk(ph, kind, run); + const a = tool === "hoe" ? [-0.3, -0.62] : [-0.42, -0.77], b = tool === "hoe" ? [0.42, -0.98] : [0.42, -0.8]; + return { ...w, hr: [0.1, -0.84], tool: { kind: tool, a, b } }; + } + function standPose(kind, mode = "still", t = 0) { + const base = POSE.stand(kind); + if (mode === "hips") return { ...base, hl: [-0.12, -0.5], hr: [0.13, -0.5] }; + if (mode === "point") return { ...base, hr: [0.34, -0.8 + 0.02 * Math.sin(t * 8)], hl: [-0.12, -0.5], lean: 0.08 }; + if (mode === "wave") return { ...base, hr: [0.22 + 0.06 * Math.sin(t * 9), -0.86], hl: [-0.06, -0.46] }; + if (mode === "shade") return { ...base, hr: [0.1, -0.93] }; + return base; + } + var FARMERS = [ + { kind: "man", hat: "scarf", jacket: "#5d7385", pants: "#3a4048", tool: "hoe" }, + { kind: "man", hat: "straw", jacket: "#6b5a48", pants: "#3e3a36", tool: "pole" }, + { kind: "woman", hat: "bun", jacket: "#8a5a62", pants: "#3a3a44", tool: "hoe" }, + { kind: "man", hat: "none", jacket: "#4f6a5a", pants: "#2e3438", tool: "hoe" } + ]; + function farmer(g2, i, x, y, s, t, pose, extra = {}) { + const f = FARMERS[i % FARMERS.length]; + person(g2, { x, y, s, t, kind: f.kind, hat: f.hat, jacket: f.jacket, pants: f.pants, seed: 10 + i * 7, ...POSE.stand(f.kind), ...pose, ...extra }); + } + function boy(g2, x, y, s, t, pose, extra = {}) { + person(g2, { x, y, s, t, kind: "boy", seed: 3, ...POSE.stand("boy"), ...pose, ...extra }); + } + var SHOUT = { hl: [0.16, -0.8], hr: [0.15, -0.78], fl: [-0.08, 0], fr: [0.1, 0], lean: 0.12 }; + var LAUGH = (t) => ({ hl: [0.05, -0.5], hr: [0.08, -0.52], fl: [-0.06, 0], fr: [0.08, 0], lean: -0.14 - 0.05 * Math.abs(Math.sin(t * 9)), bob: -0.01 * Math.abs(Math.sin(t * 9)), expr: { eye: "happy", mouth: "laugh" } }); + var SCARED = (t) => ({ hl: [-0.14, -1 + 0.02 * Math.sin(t * 20)], hr: [0.2, -0.98], fl: [-0.09, 0], fr: [0.09, 0], lean: -0.05, shiver: 1, expr: { eye: "wide", mouth: "shout", brow: "worried" } }); + var SIT = { hipY: -0.13, fl: [0.22, 0], fr: [0.26, -0.01], hl: [0.2, -0.34], hr: [0.22, -0.32], lean: 0.2 }; + var hillY = (s, x) => s.gy + 30 * s.u + 110 * s.u * Math.pow((x - s.W * 0.48) / (s.W * 0.75), 2); + function bgHill(mood, o = {}) { + return (s) => { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + const seed = o.seed ?? 1; + if (mood === "day") sky2(g2, W2, gy, "#9fbccb", 0.22, "#ffffff", 0); + if (mood === "storm") sky2(g2, W2, gy + 40 * u, "#3e464e", 0.45, "#8a9094", 0.2); + if (mood === "sunset") { + sky2(g2, W2, gy, "#e08a50", 0.35, "#f0c890", 0.45); + sun(g2, X(-0.35), gy - 60 * u, 110 * u, "#c8463a", 0.9); } - onStage?.("\u5408\u6210\u914D\u4E50\u4E0E\u97F3\u6548\u2026"); - const m = new Mixer(ctx); - const mg = m.music.gain; - mg.setValueAtTime(0.9, 0); - for (const s of TL.scenes) { - if (!s.voice_start || !s.voice_dur) continue; - const a = s.voice_start, b = s.voice_start + s.voice_dur; - mg.setTargetAtTime(0.42, Math.max(0, a - 0.25), 0.08); - mg.setTargetAtTime(0.9, b + 0.1, 0.25); + if (mood === "dusk") sky2(g2, W2, gy, "#4a5068", 0.4, "#d8a878", 0.35); + const mc = mood === "storm" ? "#4a5258" : mood === "sunset" ? "#7a5048" : mood === "dusk" ? "#3a3a48" : "#7d8a8c"; + mountains(g2, W2, gy - 40 * u, 170 * u, mc, mood === "day" ? 0.3 : 0.45, 3 + seed); + const vx = X(-0.8), vy = gy + 10 * u; + for (let i = 0; i < 4; i++) cottage(g2, vx + i * 46 * u - i % 2 * 10 * u, vy + i % 2 * 14 * u, 40 * u, 30 + i); + const pts = []; + for (let i = 0; i <= 30; i++) { + const x = i / 30 * W2; + pts.push([x, hillY(s, x) + nz(i * 0.7, seed) * 6 * u]); } - score(m); - effects(m); - m.flushPlucks(); - m.q.sort((a, b) => a.t - b.t); - let qi = 0; - const until = (tt) => { - while (qi < m.q.length && m.q[qi].t < tt) { - m.q[qi].fn(); - qi++; + const gc = mood === "storm" ? "#6a7a5a" : mood === "sunset" ? "#8a8a50" : mood === "dusk" ? "#4a5a48" : "#8fae6a"; + const gr = g2.createLinearGradient(0, gy, 0, H2); + gr.addColorStop(0, rgba(gc, 0.6)); + gr.addColorStop(1, rgba(mixHex(gc, "#4a5a32", 0.4), 0.5)); + g2.fillStyle = gr; + g2.beginPath(); + g2.moveTo(0, H2); + for (const p of pts) g2.lineTo(p[0], p[1]); + g2.lineTo(W2, H2); + g2.closePath(); + g2.fill(); + brush(g2, pts, 3 * u, { color: "#3e5a2e", alpha: 0.7, seed, dry: 0.5, jitter: 0.5 }); + strokesH2(g2, W2, gy + 80 * u, H2, 30, "#4f6a3e", 0.35, seed + 9, u); + if (mood === "day") flowers(g2, 0, W2, gy + 90 * u, H2 - 20 * u, 40, seed + 4); + if (o.tree !== false) bigTree(g2, X(0.62), hillY(s, X(0.62)) + 10 * u, 520 * u, 0, 17, { color: mood === "storm" ? "#4a6a3e" : mood === "sunset" ? "#6a7040" : "#5f8a4c" }); + if (o.rock !== false) rock(g2, X(0.05), hillY(s, X(0.05)) + 40 * u, 190 * u, 110 * u, 5); + }; + } + var rockTop = (s) => [s.X(0.05), hillY(s, s.X(0.05)) + 40 * s.u - 105 * s.u]; + function bgVillage(storm = false) { + return (s) => { + const { g: g2, W: W2, H: H2, u, gy, X } = s; + sky2(g2, W2, gy, storm ? "#4e565e" : "#a9c3c9", storm ? 0.4 : 0.2, "#ffffff", 0); + mountains(g2, W2, gy - 70 * u, 200 * u, storm ? "#4a5258" : "#7d8a8c", 0.35, 41); + mountains(g2, W2, gy - 20 * u, 120 * u, storm ? "#3a4248" : "#5e6c62", 0.4, 47); + band2(g2, W2, gy - 10 * u, H2, storm ? "#7a8a6a" : "#a8b878", 0.35, 21); + terraces(g2, X(-0.1), gy + 30 * u, W2 + 20 * u, H2, 6, 9); + for (const [fx, dy, sz, sd] of [[-0.88, 40, 150, 1], [-0.62, 70, 130, 2], [-0.92, 160, 170, 3]]) cottage(g2, X(fx), gy + dy * u, sz * u, sd); + const path = spline([[X(-0.55), H2 + 20 * u], [X(-0.35), gy + 120 * u], [X(-0.2), gy + 10 * u], [X(0.1), gy - 60 * u]], 10); + for (let i = 0; i < path.length - 1; i++) brush(g2, [path[i], path[i + 1]], lerp(90, 14, i / path.length) * u, { color: "#d8c49a", alpha: 0.55, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + if (storm) { + g2.fillStyle = rgba("#2a3038", 0.18); + g2.fillRect(0, 0, W2, H2); + } + }; + } + var fieldSpot = (s, i) => [s.X(0.05 + i * 0.26), s.gy + (120 + i % 2 * 70) * s.u]; + function bgSlope(s) { + const { g: g2, W: W2, H: H2, u, X } = s; + sky2(g2, W2, H2 * 0.5, "#9fbccb", 0.22, "#ffffff", 0); + mountains(g2, W2, H2 * 0.42, 200 * u, "#7d8a8c", 0.3, 61); + const slope = [[-20 * u, H2 * 0.62], [X(-0.3), H2 * 0.5], [X(0.3), H2 * 0.32], [X(0.75), H2 * 0.24], [W2 + 20 * u, H2 * 0.26], [W2 + 20 * u, H2 + 20 * u], [-20 * u, H2 + 20 * u]]; + wash(g2, slope, "#8fae6a", 0.55); + brush(g2, spline(slope.slice(0, 5), 10), 3 * u, { color: "#3e5a2e", alpha: 0.7, seed: 3, dry: 0.5 }); + terraces(g2, -20 * u, H2 * 0.74, X(-0.1), H2 + 20 * u, 4, 13); + for (const [fx, fy, sz, sd] of [[-0.9, 0.72, 120, 1], [-0.7, 0.8, 100, 2]]) cottage(g2, X(fx), H2 * fy, sz * u, sd); + const path = slopePath(s); + for (let i = 0; i < path.length - 1; i++) brush(g2, [path[i], path[i + 1]], 34 * u, { color: "#d8c49a", alpha: 0.6, taperIn: 0, taperOut: 0, jitter: 0.1, seed: i }); + strokesH2(g2, W2, H2 * 0.4, H2, 34, "#4f6a3e", 0.3, 7, u); + bigTree(g2, X(0.88), H2 * 0.26, 360 * u, 0, 21); + rock(g2, X(0.7), H2 * 0.27, 120 * u, 70 * u, 8); + } + var slopePath = (s) => spline([[s.X(-0.85), s.H * 0.92], [s.X(-0.45), s.H * 0.68], [s.X(-0.05), s.H * 0.55], [s.X(0.3), s.H * 0.4], [s.X(0.6), s.H * 0.3]], 12); + var along = (pts, p) => { + const f = clamp(p) * (pts.length - 1), i = Math.min(pts.length - 2, Math.floor(f)), k = f - i; + return [lerp(pts[i][0], pts[i + 1][0], k), lerp(pts[i][1], pts[i + 1][1], k)]; + }; + var FLOCK = [ + { f: -0.55, dy: 60, s: 1, face: 1, seed: 1 }, + { f: -0.32, dy: 120, s: 1.1, face: -1, seed: 2 }, + { f: -0.15, dy: 40, s: 0.9, face: 1, seed: 3 }, + { f: 0.28, dy: 140, s: 1.15, face: -1, seed: 4 }, + { f: 0.45, dy: 70, s: 0.95, face: 1, seed: 5 }, + { f: 0.82, dy: 110, s: 1.05, face: -1, seed: 6 }, + { f: -0.75, dy: 150, s: 0.7, face: 1, seed: 7 } + ]; + function flock(s, o = () => ({})) { + const { g: g2, u, t, X } = s; + const k = ks2(s); + FLOCK.forEach((sp, i) => { + const e = o(sp, i); + const x = X(sp.f) + (e.dx ?? 0), y = hillY(s, X(sp.f)) + sp.dy * u + (e.dy ?? 0); + const graze = e.graze ?? (0.5 + 0.5 * Math.sin(t * 0.5 + sp.seed * 2) > 0.35 ? 1 : 0.2); + sheep(g2, { x, y, s: 150 * k * sp.s, t, face: e.face ?? sp.face, seed: sp.seed, graze, look: e.look, run: e.run, alpha: e.alpha, baa: e.baa, scared: e.scared }); + }); + } + var SCENES3 = { + title: { + label: "\u7247\u5934", + draw(s) { + const { g: g2, W: W2, H: H2, u, t, portrait } = s; + const C = CUE2.title; + const cx = W2 / 2, cy = portrait ? H2 * 0.37 : H2 * 0.42; + if (t < C.drop) { + g2.fillStyle = rgba(INK, 0.95); + g2.beginPath(); + g2.ellipse(cx, lerp(-30 * u, cy, easeIn(t / C.drop)), 9 * u, 15 * u, 0, 0, Math.PI * 2); + g2.fill(); + } + const p = easeOut(inv(C.drop, 2.4, t)); + if (p > 0) { + const R = (portrait ? 420 : 350) * u * p; + wash(g2, blobLoop(cx, cy, R, 9, 0.06, 32), "#8a9a7a", 0.14 + 0.16 * (1 - p), { edge: 1.4 }); + particles2(10, 71, (r) => { + const a2 = r() * Math.PI * 2, d = (R * 0.9 + 60 * u * r()) * easeOut(inv(C.drop, 0.8, t)); + g2.fillStyle = rgba(INK, 0.6 * (1 - 0.6 * p)); + g2.beginPath(); + g2.arc(cx + Math.cos(a2) * d, cy + Math.sin(a2) * d * 0.9, (3 + r() * 8) * u, 0, Math.PI * 2); + g2.fill(); + }); + } + const a = smooth(inv(0.8, 2.6, t)); + if (a > 0) { + g2.save(); + g2.globalAlpha = a; + const by = portrait ? H2 * 0.82 : H2 * 0.88; + mountains(g2, W2, by - 40 * u, 120 * u, "#7d8a8c", 0.3, 5, { soft: 1 }); + band2(g2, W2, by, H2, "#8fae6a", 0.45, 3, 10 * u); + bush(g2, W2 * 0.86, by + 10 * u, 160 * u, 4, 0.7); + g2.restore(); + eyesInDark(g2, W2 * 0.86, by - 20 * u, 14 * u, t, smooth(inv(3.6, 4.2, t)) * a); + } + const fs = (portrait ? 230 : 210) * u; + writeText2(g2, "\u72FC\u6765\u4E86", cx, cy, fs, C.write, 0.45, t, { vertical: portrait }); + const sa = smooth(inv(2.9, 3.6, t)); + if (sa > 0) { + g2.save(); + g2.globalAlpha = sa; + g2.font = `${46 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.textBaseline = "middle"; + const sy = portrait ? cy + fs * 1.95 : cy + fs * 0.95; + g2.fillText("\u4F0A \u7D22 \u5BD3 \u8A00", cx, sy); + brush(g2, [[cx - 150 * u, sy + 38 * u], [cx, sy + 44 * u], [cx + 150 * u, sy + 36 * u]], 4 * u, { alpha: 0.5 * sa, seed: 8, dry: 0.6 }); + g2.restore(); + } + const sp = inv(C.seal, C.seal + 0.3, t); + if (sp > 0) { + const sc = lerp(1.5, 1, easeOut(sp)), sx = portrait ? cx + fs * 0.78 : cx + fs * 1.82, sy = portrait ? cy + fs * 1.15 : cy + fs * 0.32; + g2.save(); + g2.translate(sx, sy); + g2.scale(sc, sc); + g2.rotate(-0.04); + seal(g2, 0, 0, 92 * u, ["\u5BD3\u8A00"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + const wy = portrait ? H2 * 0.8 : H2 * 0.92; + const wp = inv(1.8, 6.6, t); + if (wp > 0) { + const x0 = lerp(-150 * u, W2 * 0.6, wp); + for (let i = 0; i < 3; i++) sheep(g2, { x: x0 + 150 * u - i * 120 * u, y: wy + i % 2 * 8 * u, s: 85 * u, t, walk: 1, seed: i + 1, graze: 0 }); + boy(g2, x0 - 260 * u, wy, 170 * u, t, POSE.walk(t * 6, "boy"), { tool: { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] }, expr: { mouth: "smile" } }); + } + }, + enterAt: [0.5, 0.45] + }, + village: { + label: "\u5C71\u6751", + bg: bgVillage(false), + draw(s) { + const { g: g2, u, t, gy, X, H: H2 } = s; + const k = ks2(s); + for (const [fx, dy, sz] of [[-0.88, 40, 150], [-0.62, 70, 130]]) chimneySmoke(g2, X(fx) + sz * 0.3 * u, gy + dy * u - sz * 0.8 * u, 60 * u, t, fx * 10); + for (let i = 0; i < 3; i++) { + const [x, y] = fieldSpot(s, i); + farmer(g2, i, x, y, 330 * k, t, hoePose(t * 2.2 + i * 1.7, FARMERS[i].kind), { face: i === 1 ? -1 : 1 }); + } + const p = easeInOut(inv(CUE2.village.boyIn - 1.2, s.D + 1, t)); + const path = spline([[X(-0.6), H2 + 60 * u], [X(-0.4), gy + 160 * u], [X(-0.22), gy + 40 * u], [X(0.05), gy - 40 * u]], 10); + for (let i = 0; i < 4; i++) { + const q2 = clamp(p * 1.05 - 0.02 + i * 0.05); + if (q2 <= 0) continue; + const [x, y] = along(path, q2); + sheep(g2, { x, y, s: 130 * k * (1 - q2 * 0.5), t, walk: 1, seed: i + 2, graze: 0 }); + } + const qb = clamp(p * 1.05 - 0.06); + if (qb > 0) { + const [x, y] = along(path, qb); + boy(g2, x, y, 250 * k * (1 - qb * 0.5), t, POSE.walk(t * 6, "boy"), { tool: { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] }, expr: { mouth: "smile" } }); + } + }, + cam(s) { + const p = easeInOut(inv(CUE2.village.boyIn - 0.5, s.D, s.t)); + return cam2(s, lerp(1.08, 1.35, p), lerp(s.W / 2, s.X(-0.35), p), lerp(s.gy + 60 * s.u, s.gy + 120 * s.u, p)); + }, + enterAt: [0.5, 0.5] + }, + bored: { + label: "\u5C71\u5761\u4E0A", + bg: bgHill("day", { rock: false }), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.bored; + cloud(g2, X(-0.3) + t * 12 * u, s.gy - 420 * u, 380 * u, 3, 0.25); + flock(s); + const bx = X(0.42), by = hillY(s, X(0.42)) + 50 * u; + const yawn = smooth(inv(C.yawn, C.yawn + 0.4, t)) * (1 - smooth(inv(C.yawn + 1.3, C.yawn + 1.7, t))); + const idea = t > C.idea; + g2.save(); + g2.translate(bx, by); + g2.rotate(-Math.PI / 2); + boy(g2, 0, 0, 300 * k, t, { + hl: yawn > 0.3 ? [-0.06, -1.22] : [-0.05, -0.96], + hr: yawn > 0.3 ? [0.04, -1.25] : [0.01, -0.98], + fl: [0, 0], + fr: [0.02, -0.2], + lean: 0, + expr: { eye: idea ? t > C.sly ? "sly" : "wide" : yawn > 0.3 ? "closed" : "open", mouth: yawn > 0.3 ? "shout" : t > C.chew && !idea ? "chew" : idea ? "smile" : "line", brow: idea ? "up" : "none" }, + look: t > C.sly ? 1 : 0 + }); + g2.restore(); + if (t > C.chew - 0.3 && yawn < 0.3) { + const mx = bx - 300 * k * 0.8, my = by - 300 * k * 0.2; + brush(g2, [[mx, my], [mx + 40 * u, my - 30 * u + 6 * u * Math.sin(t * 6)], [mx + 80 * u, my - 70 * u + 10 * u * Math.sin(t * 6)]], 4 * u, { color: "#5f8a3c", alpha: 0.9, taperIn: 0, taperOut: 0.7, seed: 4 }); + } + sparkle(g2, bx - 300 * k * 0.98, by - 300 * k * 0.42, 40 * u, smooth(inv(C.idea, C.idea + 0.2, t)) * (1 - smooth(inv(C.idea + 1.2, C.idea + 1.6, t)))); + if (yawn > 0.3) marks(g2, bx - 300 * k * 1.05, by - 300 * k * 0.5, 50 * u, yawn, "\uFF5E"); + }, + cam(s) { + const C = CUE2.bored, bx = s.X(0.42), by = hillY(s, s.X(0.42)); + const p1 = easeInOut(inv(C.yawn - 4.5, C.yawn - 2, s.t)), p2 = easeInOut(inv(C.idea - 0.5, C.idea + 0.8, s.t)); + return cam2(s, lerp(lerp(1.05, 1.5, p1), 2.2, p2), lerp(lerp(s.W / 2, bx - 80 * s.u, p1), bx - 230 * s.u, p2), lerp(lerp(s.gy + 50 * s.u, by, p1), by - 30 * s.u, p2)); + }, + enterAt: [0.5, 0.5] + }, + shout1: { + label: "\u7B2C\u4E00\u6B21\u558A", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.shout1; + flock(s, (sp) => t > C.shouts[0] + 0.1 ? { graze: 0, look: 0.6, scared: 1, face: sp.f > 0.05 ? -1 : 1 } : {}); + const [rx, ry] = rockTop(s); + const hop = inv(C.hop, C.hop + 0.7, t); + const x = lerp(X(-0.2), rx, easeInOut(hop)), y = lerp(hillY(s, X(-0.2)) + 30 * u, ry, easeInOut(hop)) - Math.sin(Math.PI * hop) * 90 * u; + const cupping = t > C.cup; + const shouting = C.shouts.some((st) => t > st && t < st + 1.1); + boy(g2, x, y, 300 * k, t, cupping ? SHOUT : hop > 0 && hop < 1 ? { hl: [-0.12, -0.7], hr: [0.15, -0.72], fl: [-0.04, -0.06], fr: [0.08, -0.08] } : POSE.stand("boy"), { + face: -1, + expr: { eye: shouting ? "closed" : "sly", mouth: shouting ? "shout" : "smile", brow: shouting ? "up" : "none" }, + tool: cupping ? void 0 : { kind: "whip", a: [0.07, -0.42], b: [0.28, -0.72] } + }); + if (cupping) { + const mx = x - 300 * k * 0.25, my = y - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 1); + C.shouts.forEach((st, i) => shoutText(g2, i === 2 ? "\u5FEB\u6765\u5440\uFF01" : "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 40 * u, -1, 130 * u, t - st, { fade: 1 - smooth(inv(st + 2.6, st + 3.4, t)), seed: i })); + } + }, + cam(s) { + const [rx, ry] = rockTop(s), p = easeInOut(inv(3.8, 5, s.t)); + return cam2(s, lerp(1.6, 1.12, p), lerp(rx, s.X(-0.25), p), lerp(ry - 80 * s.u, s.gy - 20 * s.u, p)); + }, + enterAt: [0.3, 0.6] + }, + run1: { + label: "\u519C\u6C11\u4E0A\u5C71", + bg: bgSlope, + draw(s) { + const { g: g2, u, t, X, H: H2 } = s; + const k = ks2(s), C = CUE2.run1; + const path = slopePath(s); + boy(g2, X(0.7), H2 * 0.27 - 66 * u, 140 * k, t, { hl: [0.05, -0.5], hr: [0.08, -0.52] }, { face: -1, expr: { eye: "happy", mouth: "smile" } }); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const p = easeInOut(inv(C.run + i * 0.15, C.arrive + i * 0.2, t)) * (0.92 - i * 0.07); + const start = along(path, 0.04 + i * 0.03); + const [x, y] = p > 0 ? along(path, 0.04 + i * 0.03 + p) : start; + const fx = x + (p > 0 ? 0 : (i - 1.5) * 90 * u), fy = y + i % 2 * 20 * u; + let pose; + if (t < C.grab) pose = t > C.hear + i * 0.2 ? { ...standPose(f.kind, "shade"), tilt: -0.25 } : hoePose(t * 2.2 + i, f.kind); + else if (p <= 0) pose = carryPose(0, f.kind, 0, f.tool); + else if (t < C.arrive + i * 0.2) pose = carryPose(t * 12 + i, f.kind, 1, f.tool); + else pose = { ...standPose(f.kind, "shade") }; + const arrived = t > C.arrive + i * 0.2; + const lookFace = arrived ? Math.sin(t * 2.5 + i * 1.3) > 0 ? 1 : -1 : 1; + farmer(g2, i, fx, fy, 300 * k, t, pose, { face: lookFace, expr: { eye: "wide", mouth: t > C.grab ? "open" : "line", brow: arrived ? "worried" : "up" }, sweat: arrived ? 1 : 0, tilt: arrived ? 0 : -0.1 }); + if (arrived) marks(g2, fx + 10 * u, fy - 300 * k * 1.12, 70 * u, smooth(inv(C.arrive + 0.3 + i * 0.2, C.arrive + 0.6 + i * 0.2, t)), "\uFF1F"); + } + }, + cam(s) { + const C = CUE2.run1, path = slopePath(s); + const p = easeInOut(inv(C.run, C.arrive + 0.6, s.t)) * 0.85; + const [x, y] = along(path, 0.05 + p); + return cam2(s, 1.55, x + 80 * s.u, y - 90 * s.u); + }, + enterAt: [0.2, 0.8] + }, + laugh1: { + label: "\u54C8\u54C8\u5927\u7B11", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.laugh1; + flock(s); + const [rx, ry] = rockTop(s); + const laughing = t > C.laugh && t < C.angry + 0.8; + boy(g2, rx, ry, 300 * k, t, laughing ? LAUGH(t) : { ...POSE.stand("boy"), expr: { eye: "happy", mouth: "smile" } }, { face: -1 }); + if (laughing) soundMarks(g2, rx - 120 * u, ry - 330 * k, 50 * u, 0.8, 3, -1); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const leave = easeIn(inv(C.leave + i * 0.25, C.leave + 3 + i * 0.25, t)); + const x = X(-0.85 + i * 0.17) - leave * 700 * u, y = hillY(s, X(-0.85 + i * 0.17)) + (30 + i % 2 * 40) * u; + const angry = t > C.angry; + const pose = leave > 0 ? carryPose(t * 7 + i, f.kind, 0, f.tool) : angry ? standPose(f.kind, "hips") : { ...standPose(f.kind, "shade") }; + farmer(g2, i, x, y, 320 * k, t, pose, { + face: leave > 0 ? -1 : 1, + tilt: angry && leave <= 0 ? 0.12 * Math.sin(t * 7 + i) : 0, + sweat: angry ? 0 : 1, + expr: { eye: "open", mouth: angry ? "frown" : "open", brow: angry ? "angry" : "worried" } + }); + } + }, + cam(s) { + const [rx] = rockTop(s); + return cam2(s, lerp(1.3, 1.2, s.t / s.D), lerp(rx, s.X(-0.35), easeInOut(inv(CUE2.laugh1.angry - 0.5, CUE2.laugh1.angry + 1, s.t))), s.gy + 30 * s.u); + }, + enterAt: [0.5, 0.4] + }, + shout2: { + label: "\u53C8\u558A\u72FC\u6765\u4E86", + bg: bgHill("day"), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.shout2; + const dp = inv(0, 3, t); + if (dp < 1) { + const night = Math.max(0, Math.sin(dp * Math.PI * 2 - Math.PI / 2) * 0.5 + 0.5) * 0.3 * (1 - dp); + g2.fillStyle = rgba("#2a3040", night); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + const sa = dp * 2 % 1; + sun(g2, lerp(X(-1), X(1), sa), s.gy - Math.sin(sa * Math.PI) * 360 * u, 50 * u, "#d65a40", 0.8 * (1 - dp)); + } + flock(s, (sp) => t > C.shouts[0] ? { graze: 0, look: 0.5, face: sp.f > 0.05 ? -1 : 1 } : {}); + const [rx, ry] = rockTop(s); + const cupping = t > C.shouts[0] - 0.4; + const shouting = C.shouts.some((st) => t > st && t < st + 1.1); + boy(g2, rx, ry, 300 * k, t, cupping ? SHOUT : POSE.stand("boy"), { face: -1, expr: { eye: shouting ? "closed" : "sly", mouth: shouting ? "shout" : "smile" } }); + if (cupping) { + const mx = rx - 300 * k * 0.25, my = ry - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 1); + C.shouts.forEach((st, i) => shoutText(g2, "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 40 * u, -1, 120 * u, t - st, { fade: 1 - smooth(inv(st + 2.2, st + 3, t)), seed: i + 5 })); + } + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const p = easeOut(inv(C.run + i * 0.2, C.run + 2.6 + i * 0.2, t)); + if (p <= 0) continue; + const x = lerp(X(-1.25) - i * 90 * u, X(-0.75 + i * 0.13), p), y = hillY(s, X(-0.75 + i * 0.13)) + (40 + i % 2 * 40) * u + (1 - p) * 120 * u; + farmer(g2, i, x, y, 320 * k, t, p < 1 ? carryPose(t * 12 + i, f.kind, 1, f.tool) : standPose(f.kind, "shade"), { expr: { eye: "wide", mouth: "open", brow: "worried" }, sweat: 1 }); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + const p = easeInOut(inv(CUE2.shout2.run - 0.4, CUE2.shout2.run + 1.2, s.t)); + return cam2(s, lerp(1.45, 1.05, p), lerp(rx - 60 * s.u, s.W / 2 - 100 * s.u, p), lerp(ry - 60 * s.u, s.gy, p)); + }, + enterAt: [0.5, 0.3] + }, + warn: { + label: "\u8B66\u544A", + bg: bgHill("day"), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.warn; + flock(s); + const [rx, ry] = rockTop(s); + boy(g2, rx, ry, 300 * k, t, t < C.point + 0.4 ? LAUGH(t) : { ...POSE.stand("boy"), lean: 0.05 }, { face: -1, expr: t < C.point + 0.4 ? { eye: "happy", mouth: "laugh" } : { eye: "open", mouth: "line", brow: "worried" } }); + for (let i = 0; i < 4; i++) { + const f = FARMERS[i]; + const lead = i === 0; + const step = lead ? easeOut(inv(C.point - 0.3, C.point + 0.4, t)) : 0; + const x = X(-0.82 + i * 0.16) + step * 140 * u, y = hillY(s, X(-0.82 + i * 0.16)) + (30 + i % 2 * 40) * u; + const pose = lead && t > C.point ? standPose(f.kind, "point", t) : standPose(f.kind, "hips"); + farmer(g2, i, x, y, 330 * k, t, pose, { expr: { eye: "open", mouth: lead && t > C.point ? "open" : "frown", brow: "angry" }, tilt: lead && t > C.point ? -0.05 : 0 }); + if (lead && t > C.point) marks(g2, x + 60 * u, y - 330 * k * 1.15, 90 * u, smooth(inv(C.point, C.point + 0.2, t)), "\uFF01", "#b8322a"); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + const p = easeInOut(inv(CUE2.warn.point - 0.4, CUE2.warn.point + 0.6, s.t)); + return cam2(s, lerp(1.6, 1.45, p), lerp(rx - 40 * s.u, s.X(-0.62), p), lerp(ry - 50 * s.u, s.gy - 10 * s.u, p)); + }, + enterAt: [0.3, 0.5] + }, + wolf: { + label: "\u72FC\u771F\u7684\u6765\u4E86", + bg: bgHill("storm", { tree: false }), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.wolf; + for (let i = 0; i < 4; i++) stormCloud(g2, X(-1.2 + i * 0.7) + t * 30 * u, s.gy - (380 + i % 2 * 90) * u, 520 * u, smooth(inv(C.dark, C.dark + 3, t)), 40 + i); + for (let i = 0; i < 18; i++) grass(g2, i / 18 * W2 + hash(i) * 40 * u, H2 - hash(i * 3) * 160 * u, 70 * u, t, 100 + i, "#3e5a2e", 0.6, 2.5); + flock(s, () => ({ graze: t > C.creep ? 0 : 0.6 + 0.4 * Math.sin(t * 2), look: t > C.creep ? 0.4 : 0, scared: t > C.creep ? 1 : 0 })); + const bx = X(0.78), by = hillY(s, X(0.78)) + 80 * u; + const creep = easeInOut(inv(C.creep, C.reveal, t)); + if (t > C.creep - 0.2) { + const mode = t > C.reveal ? "snarl" : "sneak"; + wolf(g2, { x: bx - creep * 300 * u, y: by + 10 * u, s: 330 * k, t, face: -1, mode, glow: 0.8 }); + } + bush(g2, bx + 40 * u, by + 30 * u, 260 * u, 11, 0.85); + bush(g2, bx + 180 * u, by + 10 * u, 200 * u, 12, 0.9); + eyesInDark(g2, bx + 20 * u, by - 70 * u, 18 * u, t, smooth(inv(C.eyes, C.eyes + 0.6, t)) * (1 - smooth(inv(C.creep, C.creep + 0.3, t)))); + const fl = t > C.reveal && t < C.reveal + 0.18 ? 0.6 : t > C.reveal + 0.28 && t < C.reveal + 0.36 ? 0.35 : 0; + if (fl > 0) { + g2.fillStyle = rgba("#f6f4ee", fl); + g2.fillRect(-W2, -H2, W2 * 3, H2 * 3); + } + }, + cam(s) { + const C = CUE2.wolf, bx = s.X(0.78), by = hillY(s, s.X(0.78)); + const p1 = easeInOut(inv(C.bush - 0.6, C.eyes, s.t)), p2 = easeInOut(inv(C.reveal - 0.3, C.reveal + 0.5, s.t)); + const sh = s.t > C.reveal ? Math.sin(s.t * 70) * 6 * s.u * Math.exp(-(s.t - C.reveal) * 4) : 0; + return cam2(s, lerp(lerp(1.05, 1.8, p1), 2.1, p2), lerp(lerp(s.W / 2, bx + 20 * s.u, p1), bx - 230 * s.u, p2), lerp(lerp(s.gy - 80 * s.u, by - 40 * s.u, p1), by - 40 * s.u, p2), { dx: sh }); + }, + enterAt: [0.5, 0.2] + }, + cry: { + label: "\u547C\u6551", + bg: bgHill("storm"), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.cry; + for (let i = 0; i < 4; i++) stormCloud(g2, X(-1.2 + i * 0.7) + t * 40 * u, s.gy - (380 + i % 2 * 90) * u, 520 * u, 1, 50 + i); + for (let i = 0; i < 5; i++) { + const q2 = (t * 0.6 + i * 0.21) % 1, y2 = H2 * (0.15 + 0.12 * i); + brush(g2, [[lerp(W2 * 1.2, -W2 * 0.3, q2), y2], [lerp(W2 * 1.2, -W2 * 0.3, q2) - 220 * u, y2 + 8 * u]], 3 * u, { color: "#cfd3d4", alpha: 0.5, dry: 0.9, seed: i }); } - }; - until(1.5); - for (let k = 1; k < TOTAL + 0.3; k++) { - const tt = k; - ctx.suspend(tt).then(() => { - until(tt + 1.5); - ctx.resume(); + flock(s, () => ({ graze: 0, look: 0.4, scared: 1 })); + wolf(g2, { x: X(1.05) - t * 25 * u, y: hillY(s, X(0.95)) + 60 * u, s: 230 * k, t, face: -1, mode: "sneak", glow: 0.9 }); + const [rx, ry] = rockTop(s); + const run = easeInOut(inv(C.toRock, C.toRock + 1, t)); + const x = lerp(X(0.4), rx, run), y = lerp(hillY(s, X(0.4)) + 40 * u, ry, run) - Math.sin(Math.PI * run) * 70 * u; + const shouting = C.shouts.some((st) => t > st && t < st + 1.2); + const pose = t < C.toRock ? SCARED(t) : run < 1 ? POSE.walk(t * 14, "boy", 1) : shouting ? { ...SHOUT, shiver: 0.6 } : SCARED(t); + boy(g2, x, y, 300 * k, t, pose, { face: -1, expr: { eye: "wide", mouth: "shout", brow: "worried" }, tears: t > C.shouts[1] ? 1 : 0, shiver: 0.6 }); + if (run >= 1) { + const mx = rx - 300 * k * 0.25, my = ry - 300 * k * 0.8; + if (shouting) shoutWaves(g2, mx, my, 120 * u, t, -1, 0.8); + C.shouts.forEach((st, i) => shoutText(g2, i === 2 ? "\u6551\u547D\u554A\uFF01" : "\u72FC\u6765\u4E86\uFF01", mx - 40 * u, my - 140 * u - i * 50 * u, -1, 130 * u, t - st, { scatter: inv(st + 0.9, st + 2.2, t), seed: i + 11 })); + } + }, + cam(s) { + const [rx, ry] = rockTop(s); + return cam2(s, lerp(1.35, 1.25, s.t / s.D), lerp(s.X(0.3), rx - 120 * s.u, easeInOut(inv(1.8, 3.6, s.t))), ry - 60 * s.u); + }, + enterAt: [0.4, 0.5] + }, + noone: { + label: "\u6CA1\u4EBA\u6765", + bg: bgVillage(true), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.noone; + shoutText(g2, "\u72FC\u6765\u4E86", X(0.85), s.gy - 300 * u, -1, 60 * u, t - 0.2, { scatter: inv(1.4, 3.2, t), color: "#b8322a", seed: 21 }); + for (let i = 0; i < 3; i++) { + const [x, y] = fieldSpot(s, i); + const look = t > C.look + i * 0.15 && t < C.work; + const lead = i === 0; + let pose = hoePose(t * 2.2 + i * 1.7, FARMERS[i].kind); + if (look) pose = { ...POSE.stand(FARMERS[i].kind), hl: [0.1, -0.62], hr: [0.16, -0.66], tool: { kind: "hoe", a: [0.05, -0.05], b: [0.18, -0.95] } }; + if (lead && t > C.wave && t < C.work) pose = { ...standPose(FARMERS[i].kind, "wave", t), tool: { kind: "hoe", a: [-0.12, -0.05], b: [-0.05, -0.95] } }; + const shake = t > C.shake && t < C.wave ? 0.14 * Math.sin(t * 8 + i) : 0; + farmer(g2, i, x, y, 330 * k, t, pose, { + face: t > C.work ? i === 1 ? 1 : -1 : i === 1 ? -1 : 1, + tilt: look ? -0.18 + shake : 0, + expr: { eye: "open", mouth: lead && t > C.wave && t < C.work ? "open" : look ? "frown" : "line", brow: t > C.shake ? "angry" : "up" } + }); + } + }, + cam(s) { + const C = CUE2.noone, [lx, ly] = fieldSpot(s, 0); + const p = easeInOut(inv(C.wave - 0.6, C.wave + 0.4, s.t)) * (1 - easeInOut(inv(C.work - 0.3, C.work + 0.9, s.t))); + return cam2(s, lerp(1.3, 1.9, p), lerp(s.X(0.25), lx + 40 * s.u, p), lerp(s.gy + 20 * s.u, ly - 180 * s.u, p)); + }, + enterAt: [0.8, 0.2] + }, + chaos: { + label: "\u7F8A\u7FA4\u56DB\u6563", + bg: bgHill("storm", { rock: true }), + draw(s) { + const { g: g2, W: W2, H: H2, u, t, X } = s; + const k = ks2(s), C = CUE2.chaos; + for (let i = 0; i < 3; i++) stormCloud(g2, X(-1 + i * 0.8) + t * 30 * u, s.gy - 400 * u, 520 * u, 1, 60 + i); + const [rx, ry] = rockTop(s); + boy(g2, rx, ry, 300 * k, t, SCARED(t), { face: 1, tears: 1 }); + const wx = lerp(X(1.2), X(0.35), easeOut(inv(C.dash, C.scatter, t))) + easeIn(inv(C.grab, C.grab + 2.2, t)) * 900 * u; + const wy = hillY(s, X(0.4)) + 90 * u; + FLOCK.forEach((sp, i) => { + const x0 = X(sp.f), y0 = hillY(s, x0) + sp.dy * u; + const run = inv(C.scatter + i * 0.06, C.scatter + 0.3 + i * 0.06, t); + const dir = x0 < X(0.35) ? -1 : 1, q2 = Math.max(0, t - C.scatter - i * 0.06); + if (i === 3 && t > C.grab) return; + sheep(g2, { x: x0 + dir * q2 * (260 + hash(i) * 200) * u, y: y0 + (hash(i + 3) - 0.5) * q2 * 60 * u, s: 150 * k * sp.s, t, face: run > 0 ? dir : sp.face, run: run > 0 ? 1 : 0, scared: run > 0 ? 1 : 0, baa: run > 0 ? Math.abs(Math.sin(t * 6 + i)) : 0, seed: sp.seed, graze: run > 0 ? 0 : 1 }); + if (run > 0 && q2 < 1.2) blot(g2, x0 + dir * q2 * 200 * u, y0 + 10 * u, 60 * u, "#a89a78", 0.3 * (1 - q2 / 1.2)); }); - } - if (voiceBuf) { - const v = ctx.createBufferSource(); - v.buffer = voiceBuf; - const vg = ctx.createGain(); - vg.gain.value = 1.15; - const hp = ctx.createBiquadFilter(); - hp.type = "highpass"; - hp.frequency.value = 80; - v.connect(hp); - hp.connect(vg); - vg.connect(m.voice); - v.start(1 / 30); - } - const out = await ctx.startRendering(); - onStage?.("\u54CD\u5EA6\u6807\u51C6\u5316\uFF08\u221214 LUFS\uFF09\u2026"); - normalize(out, -14, 0.84); - onStage?.("\u5B8C\u6210"); - return out; - })(); - cached.catch(() => { - cached = null; - }); - return cached; + wolf(g2, { x: wx, y: wy, s: 340 * k, t, face: t > C.grab ? 1 : -1, mode: t < C.grab - 0.4 ? "run" : t < C.grab ? "snarl" : "run", carry: t > C.grab }); + for (let i = 0; i < 12; i++) grass(g2, i / 12 * W2, H2 - hash(i) * 100 * u, 60 * u, t, 200 + i, "#3e5a2e", 0.6, 2.5); + particles2(6, 9, (r, i) => { + const q2 = t - C.scatter; + if (q2 > 0 && q2 < 2.5) leaf(g2, X(0.35) + (r() - 0.5) * 400 * u + q2 * 100 * u, wy - q2 * 120 * u * r(), 10 * u, q2 * 4 + i, "#f2ece0", 0.8 * (1 - q2 / 2.5)); + }); + }, + cam(s) { + const sh = s.t > CUE2.chaos.scatter && s.t < CUE2.chaos.scatter + 1 ? Math.sin(s.t * 60) * 5 * s.u : 0; + return cam2(s, 1.15, s.X(0.15), s.gy + 20 * s.u, { dx: sh }); + }, + enterAt: [0.8, 0.5] + }, + regret: { + label: "\u540E\u6094", + bg: bgHill("sunset", { rock: false }), + draw(s) { + const { g: g2, u, t, X } = s; + const k = ks2(s), C = CUE2.regret; + for (let i = 0; i < 3; i++) farBird(g2, X(-0.6 + i * 0.12) + t * 20 * u, s.gy - (330 + i * 30) * u, 18 * u, t * 7 + i, "#2a2220", 0.8); + sheep(g2, { x: X(-0.4), y: hillY(s, X(-0.4)) + 80 * u, s: 140 * k, t, graze: 1, seed: 2 }); + sheep(g2, { x: X(0.45), y: hillY(s, X(0.45)) + 120 * u, s: 130 * k, t, face: -1, graze: 0.3, seed: 5 }); + const bx = X(0.02), by = hillY(s, X(0.02)) + 100 * u; + const hug = smooth(inv(C.hug - 0.4, C.hug + 0.4, t)); + const crying = t > C.cry; + boy(g2, bx, by, 300 * k, t, SIT, { expr: { eye: crying ? "sad" : "open", mouth: crying ? "cry" : "frown", brow: "worried" }, tears: crying ? 1 : 0, tilt: 0.12 * hug, shiver: crying ? 0.4 : 0 }); + sheep(g2, { x: bx + 300 * k * 0.2, y: by - 300 * k * 0.06, s: 100 * k, t, lamb: true, face: -1, graze: 0, look: 0.3, seed: 9, alpha: 0.4 + 0.6 * hug }); + if (crying) { + tear(g2, bx + 300 * k * 0.2, by - 300 * k * 0.6 + t * 40 % 50 * u, 8 * u, 1); + } + }, + cam(s) { + const bx = s.X(0.02), by = hillY(s, bx); + return cam2(s, lerp(1.15, 1.6, easeInOut(s.t / s.D)), bx + 20 * s.u, by - 40 * s.u); + }, + enterAt: [0.3, 0.3] + }, + moral: { + label: "\u5BD3\u610F", + bg: bgHill("dusk", { rock: false, tree: true }), + draw(s) { + const { g: g2, H: H2, u, t, X, portrait } = s; + const C = CUE2.moral; + for (let i = 0; i < 4; i++) blot(g2, X(-0.8) + i * 46 * u + 10 * u, s.gy - 2 * u + i % 2 * 14 * u, 14 * u, "#ffd890", 0.7 * smooth(inv(0.5 + i * 0.3, 1.5 + i * 0.3, t))); + const p = inv(0, s.D, t); + const x = lerp(X(0.5), X(-0.3), p), y = hillY(s, lerp(X(0.5), X(-0.3), p)) + 60 * u; + boy(g2, x, y, 170 * u, t, POSE.walk(t * 5, "boy"), { face: -1, alpha: 0.85, expr: { mouth: "line", eye: "sad" } }); + sheep(g2, { x: x - 110 * u, y: y + 4 * u, s: 70 * u, t, walk: 1, face: -1, lamb: true, seed: 9 }); + shoutText(g2, "\u72FC\u6765\u4E86", X(-0.2), H2 * 0.25, 1, 80 * u, t - 0.6, { scatter: inv(2, 4.6, t), seed: 31, color: "#9a3a32" }); + const fs = (portrait ? 200 : 180) * u; + const vx = portrait ? X(0.6) : X(0.68), vy = portrait ? H2 * 0.28 : H2 * 0.36; + writeText2(g2, "\u8BDA\u5B9E", vx, vy, fs, C.write, 0.55, t, { vertical: true, gap: 1.02 }); + const ea = smooth(inv(C.end - 0.3, C.end + 0.3, t)); + if (ea > 0) { + g2.save(); + g2.globalAlpha = ea; + g2.font = `${40 * u}px ${FONT_KAI}`; + g2.fillStyle = rgba(INK, 0.85); + g2.textAlign = "center"; + g2.save(); + g2.translate(vx - fs * 0.95, vy - fs * 0.9); + [..."\u72FC\u6765\u4E86 \xB7 \u5B8C"].forEach((c2, i) => g2.fillText(c2, 0, i * 46 * u)); + g2.restore(); + g2.restore(); + } + const sp = inv(C.end + 0.1, C.end + 0.35, t); + if (sp > 0) { + g2.save(); + g2.translate(vx - fs * 0.95, vy + fs * 0.9); + g2.scale(lerp(1.5, 1, easeOut(sp)), lerp(1.5, 1, easeOut(sp))); + seal(g2, 0, 0, 80 * u, ["\u5BD3\u8A00"], { alpha: Math.min(1, sp * 2) }); + g2.restore(); + } + }, + cam(s) { + return cam2(s, lerp(1, 1.06, s.t / s.D), s.W / 2, s.H / 2); + }, + enterAt: [0.5, 0.4] + } + }; + + // src/films/wolf/music.ts + var B = 60 / 96; + var G = [N.G3, N.D4, N.B4]; + var Em = [N.E3, N.B3, N.G4]; + var D = [N.D3, N.A3, N.D4]; + var Am = [N.A2, N.E3, N.A3]; + var Gh = [N.G4, N.B4, N.D5]; + function arp(m, t0, t1, chords, step, vel, pattern = [0, 1, 2, 1]) { + let i = 0; + for (let t = t0; t < t1 - 0.05; t += step, i++) { + const ch = chords[Math.floor((t - t0) / (step * 8)) % chords.length]; + m.pluck(t, ch[pattern[i % pattern.length] % ch.length], vel * (i % 4 === 0 ? 1 : 0.7), i % 2 ? 0.25 : -0.25, { dur: 1.6 }); + } } - function toWav(b) { - const ch = b.numberOfChannels, len = b.length, sr = b.sampleRate; - const buf = new ArrayBuffer(44 + len * ch * 2), v = new DataView(buf); - const w = (o2, s) => { - for (let i = 0; i < s.length; i++) v.setUint8(o2 + i, s.charCodeAt(i)); - }; - w(0, "RIFF"); - v.setUint32(4, 36 + len * ch * 2, true); - w(8, "WAVE"); - w(12, "fmt "); - v.setUint32(16, 16, true); - v.setUint16(20, 1, true); - v.setUint16(22, ch, true); - v.setUint32(24, sr, true); - v.setUint32(28, sr * ch * 2, true); - v.setUint16(32, ch * 2, true); - v.setUint16(34, 16, true); - w(36, "data"); - v.setUint32(40, len * ch * 2, true); - const data = Array.from({ length: ch }, (_, c2) => b.getChannelData(c2)); - let o = 44; - for (let i = 0; i < len; i++) for (let c2 = 0; c2 < ch; c2++) { - const s = Math.max(-1, Math.min(1, data[c2][i])); - v.setInt16(o, s < 0 ? s * 32768 : s * 32767, true); - o += 2; + var MISCHIEF = [["G4", 0.5], ["B4", 0.5], ["D5", 0.5], ["B4", 0.5], ["A4", 0.5], ["G4", 0.5], ["E4", 1]]; + var PASTORAL = [["D5", 1], ["E5", 0.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1], ["A4", 0.5], ["B4", 0.5], ["D5", 2]]; + var LAMENT = [["E5", 1.5], ["D5", 0.5], ["B4", 1], ["A4", 1], ["G4", 1.5], ["A4", 0.5], ["E4", 2]]; + function score2(m) { + const S = sceneStart, Dd = sceneDur; + let t = S("title"); + m.gliss(t + 0.35, [N.G3, N.A3, N.B3, N.D4, N.E4, N.G4, N.A4, N.B4], 0.07, 0.26); + m.pad(t + 0.5, S("village") + 1, [N.G2, N.D3], 0.045); + m.melody(t + 2, B, MISCHIEF, 0.15, 0, "pluck"); + for (let i = 0; i < 6; i++) m.block(t + 2 + i * B, 0.06, 1e3, 0.2); + t = S("village"); + m.pad(t, S("bored") + 0.5, [N.G2, N.D3, N.B3], 0.04); + arp(m, t + 0.3, S("bored"), [G, Em, G, D], B / 2, 0.16); + m.melody(t + 0.8, B * 1.2, PASTORAL, 0.12); + t = S("bored"); + m.pad(t, S("shout1") + 0.3, [N.G2, N.D3], 0.04); + for (let i = 0; i < Math.floor(CUE2.bored.idea / (B * 2)); i++) m.pluck(t + 0.4 + i * B * 2, [N.G3, N.D4, N.B3, N.D4][i % 4], 0.13, 0, { dur: 2.4 }); + m.tone(t + CUE2.bored.yawn, 520, 240, 1.1, 0.035, "triangle", 0, 0.3); + m.bell(t + CUE2.bored.idea, N.D6, 0.08, 0.2); + m.bell(t + CUE2.bored.idea + 0.12, N.G6 ?? 91, 0.06, 0.2); + for (let i = 0; i < 6; i++) m.pluck(t + CUE2.bored.sly + i * B / 2, [N.E4, N.G4, N.E4, N.B4, N.A4, N.G4][i], 0.14, 0.2, { dur: 0.4, bright: 0.9 }); + t = S("shout1"); + m.pad(t, S("run1"), [N.G2, N.D3], 0.035); + for (let i = 0; i < 4; i++) m.block(t + 0.6 + i * 0.12, 0.05, 1300, 0); + for (const st of CUE2.shout1.shouts) { + m.pluck(t + st, N.G2, 0.3, 0, { dur: 1.2 }); + m.pluck(t + st, N.D3, 0.25, 0, { dur: 1.2 }); } - return buf; + t = S("run1"); + m.pad(t, S("laugh1"), [N.D3, N.A3], 0.035); + for (let x = t + CUE2.run1.run; x < t + CUE2.run1.arrive + 0.6; x += B / 2) { + m.block(x, 0.07, 900, 0); + m.pluck(x + B / 4, [N.D4, N.E4, N.G4, N.A4, N.B4, N.D5][Math.floor((x - t) / (B / 2)) % 6], 0.12, 0.2, { dur: 0.3, bright: 0.9 }); + } + m.pluck(t + CUE2.run1.arrive + 0.3, N.B4, 0.2, 0, { bend: 2 }); + m.pluck(t + CUE2.run1.arrive + 1.4, N.A4, 0.18, 0, { bend: 2 }); + t = S("laugh1"); + arp(m, t + 0.2, t + CUE2.laugh1.angry, [G, Gh], B / 2, 0.13, [0, 2, 1, 2]); + const wah = [N.D4, N.D4 - 1, N.D4 - 2, N.D4 - 3]; + wah.forEach((nn, i) => m.tone(t + CUE2.laugh1.angry + i * 0.42, 440 * Math.pow(2, (nn - 69) / 12), 440 * Math.pow(2, (nn - 69.4) / 12), i === 3 ? 0.9 : 0.36, 0.05, "sawtooth", 0, 0.3)); + m.pad(t + CUE2.laugh1.angry, S("shout2") + 0.4, [N.E2, N.B2], 0.035, 600); + t = S("shout2"); + m.melody(t + 0.4, B, MISCHIEF, 0.13, 0, "pluck"); + for (const st of CUE2.shout2.shouts) { + m.pluck(t + st, N.G2, 0.28, 0, { dur: 1.2 }); + m.pluck(t + st, N.D3, 0.22, 0, { dur: 1.2 }); + } + for (let x = t + CUE2.shout2.run; x < S("warn"); x += B / 2) m.block(x, 0.06, 900, 0); + t = S("warn"); + arp(m, t + 0.2, t + CUE2.warn.point, [G, Gh], B / 2, 0.12, [0, 2, 1, 2]); + m.pluck(t + CUE2.warn.point, N.E2, 0.35, 0, { dur: 2.5 }); + m.pluck(t + CUE2.warn.point, N.B2, 0.25, 0, { dur: 2.5 }); + m.pad(t + CUE2.warn.point, S("wolf") + 1, [N.E2, N.B2], 0.045, 500); + t = S("wolf"); + m.pad(t, S("cry") + 0.5, [N.E2, N.B2, N.E3], 0.05, 420); + for (let x = t + 1; x < t + CUE2.wolf.reveal; x += 0.11) m.pluck(x, Math.floor((x - t) / 0.88) % 2 ? N.B2 : N.E2 - 12 + 12, 0.04 + 0.14 * (x - t) / CUE2.wolf.reveal, 0, { dur: 0.25, send: 0.05 }); + for (let x = t + CUE2.wolf.eyes; x < t + CUE2.wolf.reveal; x += 0.8) m.heart(x); + [N.E3, N.E3 + 1, N.B3, N.E4 + 1].forEach((nn) => m.pluck(t + CUE2.wolf.reveal, nn, 0.22, 0, { dur: 2.5 })); + t = S("cry"); + for (let x = t + 0.2; x < S("noone"); x += 0.12) m.pluck(x, Math.floor((x - t) / 0.48) % 2 ? N.E3 : N.B2, 0.1, 0, { dur: 0.2, send: 0.05 }); + m.flute(t + CUE2.cry.shouts[0] + 0.1, N.E5, 0.9, 0.08); + m.flute(t + CUE2.cry.shouts[2] + 0.1, N.G5, 1.1, 0.08); + t = S("noone"); + m.pad(t, S("chaos") + 0.4, [N.E2, N.B2], 0.04, 500); + for (let i = 0; i < 7; i++) m.pluck(t + 0.5 + i * B * 2.2, [N.B3, N.A3, N.G3, N.E3, N.G3, N.A3, N.E3][i], 0.13, -0.1, { dur: 2.2 }); + t = S("chaos"); + m.pad(t, S("regret") + 0.6, [N.E2, N.B2, N.E3 + 1], 0.05, 700); + for (let x = t + 0.3; x < S("regret"); x += 0.09) m.pluck(x, [N.E3, N.G3, N.E3, N.B3][Math.floor((x - t) / 0.09) % 4], 0.11, x * 7 % 1 - 0.5, { dur: 0.18, send: 0.05 }); + for (let i = 0; i < 8; i++) m.block(t + CUE2.chaos.scatter + i * 0.3, 0.12, 500, 0); + t = S("regret"); + m.pad(t, S("moral") + 0.6, [N.E2, N.B2, N.G3], 0.04, 700); + arp(m, t + 0.3, S("moral"), [Em, Am, Em, D], B, 0.1); + m.melody(t + 1, B * 1.25, LAMENT, 0.12); + t = S("moral"); + m.pad(t, TOTAL, [N.G2, N.D3, N.B3], 0.045); + arp(m, t + 0.2, t + CUE2.moral.end, [G, Em, G, D], B * 0.75, 0.12); + m.melody(t + CUE2.moral.write, B, [["D5", 1], ["B4", 1], ["A4", 1], ["G4", 2]], 0.12); + m.gliss(t + CUE2.moral.end, [N.G3, N.B3, N.D4, N.G4, N.B4, N.D5, N.G5], 0.08, 0.2); + m.bell(t + CUE2.moral.end + 0.1, N.G5, 0.08); + m.bell(t + CUE2.moral.end + 0.35, N.D6, 0.05, 0.3); + void Dd; } - function loudness(b) { - const fs = b.sampleRate; - const biquad = (x, b0, b1, b2, a1, a2) => { - const y = new Float32Array(x.length); - let x1 = 0, x2 = 0, y1 = 0, y2 = 0; - for (let i = 0; i < x.length; i++) { - const v = b0 * x[i] + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; - x2 = x1; - x1 = x[i]; - y2 = y1; - y1 = v; - y[i] = v; - } - return y; - }; - let K = Math.tan(Math.PI * 1681.974450955533 / fs), Q = 0.7071752369554196; - const Vh = Math.pow(10, 3.999843853973347 / 20), Vb = Math.pow(Vh, 0.4996667741545416); - let a0 = 1 + K / Q + K * K; - const s1 = [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; - K = Math.tan(Math.PI * 38.13547087602444 / fs); - Q = 0.5003270373238773; - a0 = 1 + K / Q + K * K; - const s2 = [1, -2, 1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0]; - const chans = Array.from({ length: b.numberOfChannels }, (_, c2) => { - const y = biquad(b.getChannelData(c2), s1[0], s1[1], s1[2], s1[3], s1[4]); - return biquad(y, s2[0], s2[1], s2[2], s2[3], s2[4]); + function baa(m, t, vel = 0.08, pan = 0, pitch = 1, len = 0.6) { + m.at(t, () => { + const c2 = m.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(330 * pitch, t); + o.frequency.linearRampToValueAtTime(290 * pitch, t + len); + const am = c2.createOscillator(); + am.frequency.value = 22; + const amg = c2.createGain(); + amg.gain.value = 0.45; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.05); + g2.gain.setValueAtTime(vel, t + len * 0.7); + g2.gain.linearRampToValueAtTime(0, t + len); + const trem = c2.createGain(); + trem.gain.value = 0.6; + am.connect(amg); + amg.connect(trem.gain); + const f = c2.createBiquadFilter(); + f.type = "bandpass"; + f.frequency.value = 950 * pitch; + f.Q.value = 2.5; + const f2 = c2.createBiquadFilter(); + f2.type = "peaking"; + f2.frequency.value = 2500; + f2.gain.value = 6; + o.connect(trem); + trem.connect(f); + f.connect(f2); + f2.connect(g2); + m.out(g2, m.sfx, pan, 0.25); + o.start(t); + o.stop(t + len + 0.05); + am.start(t); + am.stop(t + len + 0.05); }); - const blk = Math.round(0.4 * fs), hop = Math.round(0.1 * fs), z = []; - for (let st = 0; st + blk <= b.length; st += hop) { - let sum = 0; - for (const y of chans) { - let e = 0; - for (let i = st; i < st + blk; i++) e += y[i] * y[i]; - sum += e / blk; - } - z.push(sum); - } - const L = (e) => -0.691 + 10 * Math.log10(e + 1e-12); - const abs = z.filter((e) => L(e) > -70); - if (!abs.length) return -70; - const rel = L(abs.reduce((a, e) => a + e, 0) / abs.length) - 10; - const gated = abs.filter((e) => L(e) > rel); - return L(gated.reduce((a, e) => a + e, 0) / Math.max(1, gated.length)); } - function normalize(b, target = -14, ceiling = 0.84) { - const L = loudness(b); - const g2 = Math.pow(10, (target - L) / 20); - const n = b.length, ch = Array.from({ length: b.numberOfChannels }, (_, c2) => b.getChannelData(c2)); - const want = new Float32Array(n); - for (let i = 0; i < n; i++) { - let m = 0; - for (const d of ch) { - d[i] *= g2; - const a = Math.abs(d[i]); - if (a > m) m = a; - } - want[i] = m > ceiling ? ceiling / m : 1; + function growl(m, t, dur = 1.4, vel = 0.12) { + m.at(t, () => { + const c2 = m.ctx; + const o = c2.createOscillator(); + o.type = "sawtooth"; + o.frequency.setValueAtTime(78, t); + o.frequency.linearRampToValueAtTime(95, t + dur * 0.6); + o.frequency.linearRampToValueAtTime(70, t + dur); + const n = c2.createBufferSource(); + n.buffer = m.noise; + n.loop = true; + const am = c2.createOscillator(); + am.frequency.value = 17; + const amg = c2.createGain(); + amg.gain.value = 0.5; + const mix = c2.createGain(); + mix.gain.value = 0.5; + am.connect(amg); + amg.connect(mix.gain); + const lp3 = c2.createBiquadFilter(); + lp3.type = "lowpass"; + lp3.frequency.value = 650; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.25); + g2.gain.setValueAtTime(vel, t + dur - 0.3); + g2.gain.linearRampToValueAtTime(0, t + dur); + o.connect(mix); + n.connect(mix); + mix.connect(lp3); + lp3.connect(g2); + m.out(g2, m.sfx, 0.3, 0.2); + o.start(t); + o.stop(t + dur + 0.05); + n.start(t, t * 0.37 % 1.5); + n.stop(t + dur + 0.05); + am.start(t); + am.stop(t + dur + 0.05); + }); + } + function howl(m, t, vel = 0.09) { + m.at(t, () => { + const c2 = m.ctx, dur = 2.4; + const o = c2.createOscillator(); + o.type = "triangle"; + o.frequency.setValueAtTime(330, t); + o.frequency.linearRampToValueAtTime(600, t + 0.6); + o.frequency.setValueAtTime(600, t + 1.4); + o.frequency.linearRampToValueAtTime(420, t + dur); + const lfo = c2.createOscillator(); + lfo.frequency.value = 5.5; + const lg = c2.createGain(); + lg.gain.value = 8; + lfo.connect(lg); + lg.connect(o.frequency); + const bp = c2.createBiquadFilter(); + bp.type = "bandpass"; + bp.frequency.value = 900; + bp.Q.value = 0.8; + const g2 = c2.createGain(); + g2.gain.setValueAtTime(0, t); + g2.gain.linearRampToValueAtTime(vel, t + 0.4); + g2.gain.setValueAtTime(vel, t + 1.6); + g2.gain.linearRampToValueAtTime(0, t + dur); + o.connect(bp); + bp.connect(g2); + m.out(g2, m.sfx, 0.4, 0.9); + o.start(t); + o.stop(t + dur + 0.05); + lfo.start(t); + lfo.stop(t + dur + 0.05); + }); + } + function thunder(m, t, vel = 0.3) { + m.noiseHit(t, 0.25, "lowpass", 3e3, 300, vel, 0, 0.6, 0.5); + for (let i = 0; i < 5; i++) m.noiseHit(t + 0.15 + i * 0.32, 0.9, "lowpass", 400, 60, vel * 0.6 * (1 - i * 0.15), i % 2 ? 0.3 : -0.3, 0.7, 0.8); + } + var whip = (m, t, pan = 0) => { + m.noiseHit(t, 0.04, "highpass", 5e3, 2500, 0.25, pan, 0.7, 0.2); + m.tone(t, 2400, 700, 0.05, 0.05, "triangle", pan, 0.2); + }; + var thud = (m, t, vel = 0.12, pan = 0) => { + m.tone(t, 110, 60, 0.12, vel, "sine", pan, 0.1); + m.noiseHit(t, 0.06, "lowpass", 900, 200, vel * 0.7, pan, 0.7, 0.05); + }; + var steps = (m, t0, t1, rate, vel = 0.06, pan = 0) => { + for (let x = t0; x < t1; x += 1 / rate) m.noiseHit(x, 0.05, "lowpass", 700, 180, vel, pan, 0.7, 0.05); + }; + var crickets = (m, t0, t1, r) => { + for (let x = t0; x < t1; x += 0.6 + r() * 0.8) for (let k = 0; k < 3; k++) m.tone(x + k * 0.05, 4600, 4500, 0.035, 0.012, "sine", r() - 0.5, 0.3); + }; + function echoLines(m, sceneId, gain = 0.3) { + const sc = TL.scenes.find((s) => s.id === sceneId); + for (const l of sc?.lines ?? []) if (l.who === "boy" && /狼|救命|快来/.test(l.text)) m.echo(l.t0, l.dur, 0.45, gain, 3); + } + function hoeHits(m, t0, t1, rate, offset, vel = 0.06, pan = 0) { + for (let n = 0; n < 200; n++) { + const x = (Math.PI / 2 + n * Math.PI * 2 - offset) / rate; + if (x < 0) continue; + const tt = t0 + x; + if (tt > t1) break; + thud(m, tt, vel, pan); } - const att = 1 / (4e-3 * b.sampleRate), rel = 1 / (0.12 * b.sampleRate); - for (let i = n - 2; i >= 0; i--) want[i] = Math.min(want[i], want[i + 1] + att); - let cur = 1; - for (let i = 0; i < n; i++) { - cur = Math.min(want[i], cur + rel); - for (const d of ch) d[i] *= cur; + } + function effects2(m) { + const S = sceneStart, Dd = sceneDur; + const r = rng(77); + let t = S("title"); + m.drop(t + CUE2.title.drop); + m.swish(t + CUE2.title.write); + m.swish(t + CUE2.title.write + 0.45); + m.swish(t + CUE2.title.write + 0.9); + m.thump(t + CUE2.title.seal + 0.01, 0.45); + baa(m, t + 2.6, 0.06, 0.2); + baa(m, t + 4, 0.05, -0.1, 1.15); + whip(m, t + 3.3); + t = S("village"); + for (let x = t + 0.5; x < t + Dd("village"); x += 1.2 + r() * 1.5) m.chirp(x, 0.03, r() * 1.4 - 0.7); + for (let i = 0; i < 3; i++) hoeHits(m, t, t + Dd("village"), 2.2, i * 1.7 + 2.2 * 0, 0.05, -0.2 + i * 0.25); + baa(m, t + 5.6, 0.07, -0.3); + baa(m, t + 7.8, 0.06, -0.2, 1.1); + whip(m, t + 6.4, -0.3); + t = S("bored"); + m.wind(t, Dd("bored"), 0.03); + baa(m, t + 1.8, 0.06, 0.4); + baa(m, t + 4.6, 0.05, -0.4, 1.12); + baa(m, t + 7.2, 0.05, 0.3, 0.95); + t = S("shout1"); + m.noiseHit(t + CUE2.shout1.hop, 0.3, "bandpass", 600, 1800, 0.06, 0, 1, 0.2); + thud(m, t + CUE2.shout1.hop + 0.7, 0.12); + echoLines(m, "shout1"); + baa(m, t + CUE2.shout1.shouts[0] + 0.4, 0.07, 0.3, 1.1); + baa(m, t + CUE2.shout1.shouts[1] + 0.3, 0.06, -0.3, 1.2); + t = S("run1"); + for (let i = 0; i < 3; i++) hoeHits(m, t, t + CUE2.run1.hear, 2.2, i, 0.05, -0.3 + i * 0.3); + m.noiseHit(t + CUE2.run1.grab, 0.08, "bandpass", 1200, 600, 0.08, 0, 1, 0.1); + steps(m, t + CUE2.run1.run, t + CUE2.run1.arrive + 0.6, 7, 0.07, -0.1); + steps(m, t + CUE2.run1.run + 0.07, t + CUE2.run1.arrive + 0.8, 6.3, 0.05, 0.2); + for (let i = 0; i < 4; i++) m.noiseHit(t + CUE2.run1.arrive + 0.2 + i * 0.4, 0.25, "bandpass", 900, 500, 0.03, 0, 1.2, 0.1); + t = S("laugh1"); + steps(m, t + CUE2.laugh1.leave, t + Dd("laugh1") + 0.5, 3, 0.05, -0.3); + t = S("shout2"); + echoLines(m, "shout2", 0.27); + steps(m, t + CUE2.shout2.run, t + Dd("shout2"), 7, 0.07, -0.3); + t = S("warn"); + m.thump(t + CUE2.warn.point, 0.25, 80); + t = S("wolf"); + thunder(m, t + 0.4, 0.18); + m.wind(t, Dd("wolf") + 0.5, 0.09, 0.2); + m.noiseHit(t + CUE2.wolf.bush, 0.8, "bandpass", 2500, 1500, 0.05, 0.4, 0.8, 0.2); + growl(m, t + CUE2.wolf.creep, 1.6, 0.1); + thunder(m, t + CUE2.wolf.reveal, 0.35); + howl(m, t + CUE2.wolf.reveal + 0.4, 0.08); + t = S("cry"); + m.wind(t, Dd("cry") + 0.5, 0.12, -0.2); + echoLines(m, "cry", 0.12); + growl(m, t + 5.5, 1.2, 0.06); + t = S("noone"); + m.wind(t, Dd("noone"), 0.05, 0.3); + for (let i = 0; i < 3; i++) { + hoeHits(m, t, t + CUE2.noone.look, 2.2, i * 1.7, 0.05, -0.2 + i * 0.25); + hoeHits(m, t + CUE2.noone.work, t + Dd("noone"), 2.2, i * 1.7 + CUE2.noone.work * 2.2, 0.05, -0.2 + i * 0.25); } - return { before: L, gain: g2 }; + t = S("chaos"); + growl(m, t + 0.2, 0.9, 0.12); + for (let x = t + CUE2.chaos.scatter; x < t + CUE2.chaos.scatter + 3; x += 0.05 + r() * 0.07) m.noiseHit(x, 0.05, "lowpass", 800, 200, 0.05 + r() * 0.04, r() * 1.6 - 0.8, 0.7, 0.05); + for (let i = 0; i < 9; i++) baa(m, t + CUE2.chaos.scatter + 0.1 + i * 0.3 + r() * 0.15, 0.06, r() * 1.4 - 0.7, 1 + r() * 0.3, 0.45); + m.noiseHit(t + CUE2.chaos.grab, 0.2, "lowpass", 1500, 300, 0.12, 0.4, 0.7, 0.2); + baa(m, t + CUE2.chaos.grab + 0.1, 0.07, 0.5, 1.35, 0.8); + t = S("regret"); + crickets(m, t + 0.5, t + Dd("regret"), r); + baa(m, t + CUE2.regret.hug + 0.6, 0.05, 0.1, 1.3, 0.7); + for (let i = 0; i < 3; i++) m.tone(t + 1.2 + i * 1.7, 520, 380, 0.25, 0.02, "sawtooth", -0.5, 0.6); + t = S("moral"); + crickets(m, t, t + Dd("moral"), r); + m.thump(t + CUE2.moral.end + 0.12, 0.4); + void TOTAL; } - // src/film/live.json - var live_default = [ - { id: "01-egg", scene: "bigegg", from: 2.2, dur: 3, cover: 2.4, caption: "\u6700\u5927\u7684\u86CB", index: "\u5176\u4E00", xhs: "\u6700\u540E\u4E00\u9897\u86CB\u6700\u5927\uFF0C\u597D\u4E45\u624D\u88C2\u5F00\uFF0C\u94BB\u51FA\u6765\u4E00\u53EA\u53C8\u5927\u53C8\u7070\u7684\u5C0F\u5BB6\u4F19\u3002" }, - { id: "02-laugh", scene: "bullied", from: 3, dur: 3, cover: 1, caption: "\u4E11\u5C0F\u9E2D", index: "\u5176\u4E8C", xhs: "\u54E5\u54E5\u59D0\u59D0\u7B11\u8BDD\u4ED6\uFF0C\u516C\u9E21\u8FFD\u7740\u5544\u4ED6\uFF0C\u5927\u5BB6\u90FD\u53EB\u4ED6\u300C\u4E11\u5C0F\u9E2D\u300D\u3002" }, - { id: "03-swans", scene: "autumn", from: 4.6, dur: 3, cover: 1.6, caption: "\u4EF0\u671B", index: "\u5176\u4E09", xhs: "\u79CB\u5929\u508D\u665A\uFF0C\u4E00\u7FA4\u6D01\u767D\u7684\u5927\u9E1F\u98DE\u8FC7\uFF0C\u4ED6\u671B\u7740\u5929\u7A7A\uFF0C\u5FC3\u91CC\u8BF4\u4E0D\u51FA\u7684\u5411\u5F80\u3002" }, - { id: "04-snow", scene: "winter", from: 6.4, dur: 3, cover: 1.6, caption: "\u5BD2\u51AC", index: "\u5176\u56DB", xhs: "\u96EA\u591C\u91CC\u6E56\u9762\u7ED3\u4E86\u51B0\uFF0C\u4ED6\u7F29\u6210\u4E00\u56E2\uFF0C\u71AC\u8FC7\u4E86\u4E00\u4E2A\u53C8\u4E00\u4E2A\u5BD2\u51B7\u7684\u591C\u665A\u3002" }, - { id: "05-reflect", scene: "reflection", from: 5.4, dur: 3, cover: 2.4, caption: "\u5012\u5F71", index: "\u5176\u4E94", xhs: "\u4ED6\u4F4E\u4E0B\u5934\uFF0C\u770B\u89C1\u6C34\u91CC\u7684\u5012\u5F71\u2014\u2014\u90A3\u662F\u4E00\u53EA\u7F8E\u4E3D\u7684\u767D\u5929\u9E45\u3002" }, - { id: "06-fly", scene: "fly", from: 2.6, dur: 3, cover: 1.6, caption: "\u5C55\u7FC5", index: "\u5176\u516D", xhs: "\u539F\u6765\uFF0C\u4ED6\u672C\u6765\u5C31\u662F\u4E00\u53EA\u5929\u9E45\u3002\u5F20\u5F00\u7FC5\u8180\uFF0C\u98DE\u5411\u9AD8\u9AD8\u7684\u84DD\u5929\u3002" } + // src/films/wolf/content.ts + var COVERS = (tl3) => [ + { id: "shout", name: "\u5927\u558A\u72FC\u6765\u4E86", t: sceneAt(tl3, "shout1", 5), title: "\u72FC\u6765\u4E86", sub: "\u653E\u7F8A\u5A03\u7684\u73A9\u7B11", side: "right", vertical: true }, + { id: "wolf", name: "\u72FC\u771F\u7684\u6765\u4E86", t: sceneAt(tl3, "wolf", 10.2), title: "\u72FC\u6765\u4E86", sub: "\u8FD9\u4E00\u6B21\uFF0C\u662F\u771F\u7684", side: "left", vertical: true }, + { id: "run", name: "\u519C\u6C11\u4E0A\u5C71", t: sceneAt(tl3, "run1", 6.4), title: "\u72FC\u6765\u4E86", sub: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, + { id: "regret", name: "\u5915\u9633\u540E\u6094", t: sceneAt(tl3, "regret", 5.5), title: "\u8BDA\u5B9E", sub: "\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u8BF4\u771F\u8BDD\u4E5F\u6CA1\u4EBA\u4FE1", side: "right", vertical: true } ]; + var PLATFORMS2 = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66", + titleMax: 20, + tagMax: 10, + color: "#ff2442", + titles: ["\u6C34\u58A8\u7248\u72FC\u6765\u4E86\uFF5C\u50CF\u8BED\u6587\u4E66\u91CC\u7684\u63D2\u56FE", "\u7761\u524D\u6545\u4E8B\uFF5C\u653E\u7F8A\u5A03\u548C\u72FC\u6765\u4E86", "\u8BDA\u5B9E\u7684\u5B69\u5B50\u624D\u6709\u4EBA\u4FE1\uFF5C\u72FC\u6765\u4E86"], + desc: `\u8FD8\u8BB0\u5F97\u90A3\u4E2A\u653E\u7F8A\u5A03\u5417\uFF1F +\u8FD9\u6B21\u628A\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86\u4E00\u90E8\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF1A\u5C71\u6751\u3001\u68AF\u7530\u3001\u7F8A\u7FA4\uFF0C\u8FD8\u6709\u4E00\u4E2A\u7231\u5F00\u73A9\u7B11\u7684\u5C0F\u5B69\u3002 - // src/film/overlays.ts - var at = (id, dt) => (TL.scenes.find((s) => s.id === id)?.start ?? 0) + dt; - var COVER_DESIGNS = [ - { id: "reflect", name: "\u5012\u5F71\u63ED\u6653", t: at("reflection", 7.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u539F\u6765\uFF0C\u4ED6\u662F\u4E00\u53EA\u767D\u5929\u9E45", side: "left", vertical: true }, - { id: "hatch", name: "\u7834\u58F3\u800C\u51FA", t: at("bigegg", 4.4), title: "\u4E11\u5C0F\u9E2D", sub: "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u6C34\u58A8\u624B\u7ED8\u52A8\u753B", side: "right", vertical: true }, - { id: "fly", name: "\u5C55\u7FC5\u9AD8\u98DE", t: at("fly", 4.2), title: "\u76F8\u4FE1\u81EA\u5DF1", sub: "\u4F60\u4E5F\u4F1A\u5C55\u5F00\u5C5E\u4E8E\u81EA\u5DF1\u7684\u7FC5\u8180", side: "right", vertical: true }, - { id: "autumn", name: "\u79CB\u65E5\u4EF0\u671B", t: at("autumn", 6.6), title: "\u4E11\u5C0F\u9E2D", sub: "\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE", side: "left", vertical: true } +\u{1F411} \u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u4ED6\u773C\u73E0\u4E00\u8F6C +\u{1F4E2} \u300C\u72FC\u6765\u4E86\uFF01\u300D\u2014\u2014\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71 +\u{1F602} \u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D +\u{1F43A} \u6709\u4E00\u5929\uFF0C\u7EFF\u83B9\u83B9\u7684\u773C\u775B\u4ECE\u6811\u4E1B\u91CC\u63A2\u51FA\u6765\u2026\u2026 + +\u8FD9\u4E00\u6B21\u4ED6\u558A\u7834\u4E86\u55D3\u5B50\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u6765\u3002 +\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002 + +\u65C1\u767D\u3001\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u4E09\u4E2A\u58F0\u97F3\uFF0C\u9002\u5408\u5F53\u7761\u524D\u6545\u4E8B\u548C\u5B69\u5B50\u4E00\u8D77\u770B \u{1F440}`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u513F\u7AE5\u6545\u4E8B", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8\u52A8\u753B", "\u4EB2\u5B50\u9605\u8BFB", "\u8BDA\u5B9E", "\u5BD3\u8A00\u6545\u4E8B"], + tip: "\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\uFF1B\u8BDD\u9898\u7C98\u8D34\u540E\u5728 App \u91CC\u70B9\u9009\u4E00\u4E0B\u5373\u53EF\u53D8\u6210\u84DD\u8272\u8BDD\u9898\u3002" + }, + { + id: "dy", + name: "\u6296\u97F3", + titleMax: 30, + tagMax: 5, + color: "#111111", + titles: ["\u7528\u4EE3\u7801\u4E00\u7B14\u4E00\u753B\uFF0C\u753B\u4E86\u4E00\u90E8\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B", "\u5C0F\u65F6\u5019\u542C\u8FC7\u7684\u300A\u72FC\u6765\u4E86\u300B\uFF0C\u8FD9\u6B21\u662F\u6C34\u58A8\u624B\u7ED8\u7248", "\u8BF4\u4E86\u4E24\u6B21\u8C0E\uFF0C\u72FC\u771F\u7684\u6765\u4E86\uFF1A\u6C34\u58A8\u7248\u653E\u7F8A\u5A03"], + desc: `\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\u2014\u2014\u628A\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\u3002 +\u653E\u7F8A\u5A03\u4E24\u6B21\u558A\u300C\u72FC\u6765\u4E86\u300D\u9A97\u5927\u5BB6\u4E0A\u5C71\uFF0C\u7B2C\u4E09\u6B21\u72FC\u771F\u7684\u6765\u4E86\uFF0C\u5374\u518D\u4E5F\u6CA1\u6709\u4EBA\u76F8\u4FE1\u4ED6\u3002 +\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u6545\u4E8B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE\u52A8\u753B"], + tip: "\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + }, + { + id: "sph", + name: "\u5FAE\u4FE1\u89C6\u9891\u53F7", + titleMax: 16, + tagMax: null, + color: "#07c160", + titles: ["\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B", "\u653E\u7F8A\u5A03\u8BF4\u8C0E\uFF0C\u72FC\u771F\u7684\u6765\u4E86", "\u505A\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50"], + desc: `\u4E00\u90E8\u50CF\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u7684\u6C34\u58A8\u52A8\u753B\u3002 +\u5C71\u5761\u4E0A\u653E\u7F8A\u7684\u5B69\u5B50\u89C9\u5F97\u65E0\u804A\uFF0C\u5927\u558A\u300C\u72FC\u6765\u4E86\u300D\u9A97\u519C\u6C11\u4EEC\u4E0A\u5C71\uFF1B\u7B2C\u4E8C\u6B21\u53C8\u9A97\u4E86\u4E00\u56DE\u3002\u7B49\u72FC\u771F\u7684\u6765\u4E86\uFF0C\u4ED6\u558A\u7834\u55D3\u5B50\uFF0C\u8C01\u4E5F\u6CA1\u6709\u6765\u3002 +\u8F6C\u7ED9\u5BB6\u91CC\u7684\u5C0F\u670B\u53CB\uFF0C\u5F53\u4F5C\u4ECA\u665A\u7684\u7761\u524D\u6545\u4E8B\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u5BD3\u8A00\u6545\u4E8B", "\u56FD\u98CE", "\u624B\u7ED8", "\u4EB2\u5B50", "\u8BDA\u5B9E", "\u8BED\u6587\u8BFE\u672C", "\u6210\u957F"], + tip: "\u6807\u9898 \u226416 \u5B57\uFF0C\u8BDD\u9898\u6570\u91CF\u4E0D\u9650\u3002" + }, + { + id: "bili", + name: "\u54D4\u54E9\u54D4\u54E9", + titleMax: 80, + tagMax: 10, + color: "#fb7299", + titles: ["\u3010\u6C34\u58A8\u624B\u7ED8\u3011\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\uFF5C\u8BED\u6587\u8BFE\u672C\u63D2\u56FE\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u591A\u89D2\u8272\u914D\u97F3\uFF0B\u539F\u521B\u56FD\u98CE\u914D\u4E50\uFF0C\u5168\u7A0B\u4EE3\u7801\u9010\u5E27\u7ED8\u5236", "\u3010\u513F\u7AE5\u52A8\u753B\u3011\u6C34\u58A8\u7248\u300A\u72FC\u6765\u4E86\u300B\uFF1A\u653E\u7F8A\u5A03\u7684\u4E09\u58F0\u300C\u72FC\u6765\u4E86\u300D"], + desc: `\u4E00\u90E8\u7EA6 2 \u5206 18 \u79D2\u7684\u6C34\u58A8\u624B\u7ED8\u98CE\u513F\u7AE5\u52A8\u753B\uFF0C\u6539\u7F16\u81EA\u4F0A\u7D22\u5BD3\u8A00\u300A\u72FC\u6765\u4E86\u300B\uFF08\u653E\u7F8A\u7684\u5B69\u5B50\uFF09\u3002 + +\u753B\u9762\uFF1A\u5168\u90E8\u7531\u4EE3\u7801\u5728\u6D4F\u89C8\u5668\u91CC\u9010\u5E27\u7ED8\u5236\u2014\u2014\u5BA3\u7EB8\u7EB9\u7406\u3001\u53D8\u5BBD\u6BDB\u7B14\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u3001\u6731\u7802\u5370\u7AE0\uFF1B\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u3001\u7F8A\u7FA4\u548C\u72FC\u90FD\u662F\u7A0B\u5E8F\u753B\u51FA\u6765\u7684\uFF0C\u6A2A\u7AD6\u5C4F\u5404\u81EA\u6784\u56FE\u3002\u300C\u72FC\u6765\u4E86\u300D\u4E09\u4E2A\u6731\u7EA2\u5927\u5B57\u4ECE\u753B\u9762\u91CC\u558A\u51FA\u6765\uFF0C\u7B2C\u4E09\u6B21\u88AB\u98CE\u5439\u6563\u3002 +\u58F0\u97F3\uFF1A\u65C1\u767D\u3001\u653E\u7F8A\u5A03\uFF08\u7AE5\u58F0\uFF09\u3001\u519C\u6C11\u4E09\u4E2A\u58F0\u97F3\uFF1B\u539F\u521B\u4E94\u58F0\u97F3\u9636\u914D\u4E50\uFF1B\u7F8A\u53EB\u3001\u5C71\u8C37\u56DE\u58F0\u3001\u811A\u6B65\u3001\u9504\u5934\u3001\u96F7\u58F0\u3001\u72FC\u568E\u3001\u7F8A\u7FA4\u5954\u9003\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\u3002 + +\u7AE0\u8282\uFF1A +00:00 \u7247\u5934 +00:06 \u5C71\u6751 +00:17 \u5C71\u5761\u4E0A +00:29 \u7B2C\u4E00\u6B21\u558A +00:39 \u519C\u6C11\u4E0A\u5C71 +00:51 \u54C8\u54C8\u5927\u7B11 +01:00 \u53C8\u558A\u72FC\u6765\u4E86 +01:11 \u8B66\u544A +01:19 \u72FC\u771F\u7684\u6765\u4E86 +01:31 \u547C\u6551 +01:40 \u6CA1\u4EBA\u6765 +01:51 \u7F8A\u7FA4\u56DB\u6563 +01:59 \u540E\u6094 +02:07 \u5BD3\u610F`, + tags: ["\u72FC\u6765\u4E86", "\u4F0A\u7D22\u5BD3\u8A00", "\u6C34\u58A8\u52A8\u753B", "\u513F\u7AE5\u52A8\u753B", "\u56FD\u98CE", "\u624B\u7ED8\u52A8\u753B", "\u7761\u524D\u6545\u4E8B", "\u539F\u521B\u914D\u4E50", "\u4EE3\u7801\u827A\u672F", "\u5BD3\u8A00"], + tip: "\u6807\u9898 \u226480 \u5B57\uFF0C\u6807\u7B7E \u226410 \u4E2A\uFF1B\u7AE0\u8282\u65F6\u95F4\u53EF\u76F4\u63A5\u7C98\u8FDB\u7B80\u4ECB\u3002" + } + ]; + var PHOTO_POSTS2 = [ + { + id: "xhs", + name: "\u5C0F\u7EA2\u4E66\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 20, + tagMax: 10, + titles: ["\u72FC\u6765\u4E86\u7684\u516D\u4E2A\u77AC\u95F4\uFF5C\u6C34\u58A8\u5B9E\u51B5\u56FE", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF5C\u72FC\u6765\u4E86"], + body: `\u628A\u300A\u72FC\u6765\u4E86\u300B\u753B\u6210\u4E86 6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u957F\u6309\u5C31\u80FD\u770B\u5B83\u52A8\u8D77\u6765 \u2728 + +\u5176\u4E00 \xB7 \u574F\u4E3B\u610F\uFF1A\u5C71\u5761\u4E0A\u592A\u65E0\u804A\uFF0C\u653E\u7F8A\u5A03\u773C\u73E0\u4E00\u8F6C +\u5176\u4E8C \xB7 \u72FC\u6765\u4E86\uFF1A\u8DF3\u4E0A\u77F3\u5934\u5927\u558A\uFF0C\u6731\u7EA2\u5927\u5B57\u98DE\u5411\u5C71\u4E0B +\u5176\u4E09 \xB7 \u6025\u6025\u5FD9\u5FD9\uFF1A\u519C\u6C11\u4EEC\u625B\u7740\u9504\u5934\u8DD1\u4E0A\u5C71 +\u5176\u56DB \xB7 \u54C8\u54C8\u5927\u7B11\uFF1A\u300C\u6211\u662F\u9017\u4F60\u4EEC\u73A9\u7684\uFF01\u300D +\u5176\u4E94 \xB7 \u771F\u7684\u6765\u4E86\uFF1A\u6811\u4E1B\u91CC\uFF0C\u4E00\u53CC\u7EFF\u83B9\u83B9\u7684\u773C\u775B +\u5176\u516D \xB7 \u540E\u6094\uFF1A\u5915\u9633\u4E0B\uFF0C\u4ED6\u62B1\u7740\u5C0F\u7F8A\u54ED\u4E86 + +\u8BF4\u8C0E\u7684\u4EBA\uFF0C\u5C31\u7B97\u8BF4\u4E86\u771F\u8BDD\uFF0C\u4E5F\u6CA1\u6709\u4EBA\u518D\u76F8\u4FE1\u4ED6\u3002 +\u4F60\u5C0F\u65F6\u5019\u542C\u8FC7\u8FD9\u4E2A\u6545\u4E8B\u5417\uFF1F`, + tags: ["\u72FC\u6765\u4E86", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE\u63D2\u753B", "\u624B\u7ED8", "\u5BD3\u8A00\u6545\u4E8B", "\u7761\u524D\u6545\u4E8B", "\u8BDA\u5B9E", "\u52A8\u6001\u58C1\u7EB8"], + tip: "\u53D1\u5E03\u65F6\u9009\u62E9\u300C\u5B9E\u51B5\u56FE\u300D\u4E0A\u4F20 6 \u7EC4 JPG+MOV\uFF1B\u6807\u9898 \u226420 \u5B57\uFF0C\u8BDD\u9898 \u226410 \u4E2A\u3002" + }, + { + id: "dy", + name: "\u6296\u97F3\u56FE\u6587\uFF08\u5B9E\u51B5\u56FE\uFF09", + titleMax: 30, + tagMax: 5, + titles: ["\u6C34\u58A8\u5B9E\u51B5\u56FE\uFF1A\u653E\u7F8A\u5A03\u558A\u72FC\u6765\u4E86\u7684\u516D\u4E2A\u77AC\u95F4", "\u4F1A\u52A8\u7684\u8BFE\u672C\u63D2\u56FE\uFF0C\u72FC\u771F\u7684\u6765\u4E86"], + body: `6 \u5F20\u4F1A\u52A8\u7684\u6C34\u58A8\u63D2\u56FE\uFF0C\u8BB2\u5B8C\u4E00\u4E2A\u300A\u72FC\u6765\u4E86\u300B\u3002 +\u574F\u4E3B\u610F \u2192 \u5927\u558A\u72FC\u6765\u4E86 \u2192 \u519C\u6C11\u4E0A\u5C71 \u2192 \u54C8\u54C8\u5927\u7B11 \u2192 \u72FC\u771F\u7684\u6765\u4E86 \u2192 \u5915\u9633\u4E0B\u540E\u6094\u3002 +\u505A\u4E00\u4E2A\u8BDA\u5B9E\u7684\u5B69\u5B50\u5427\u3002`, + tags: ["\u72FC\u6765\u4E86", "\u5B9E\u51B5\u56FE", "\u6C34\u58A8\u63D2\u753B", "\u4F0A\u7D22\u5BD3\u8A00", "\u56FD\u98CE"], + tip: "\u56FE\u6587\u6A21\u5F0F\u4E0A\u4F20\u5B9E\u51B5\u56FE\uFF1B\u6807\u9898 \u226430 \u5B57\uFF0C\u8BDD\u9898 \u22645 \u4E2A\u3002" + } ]; + + // src/films/wolf/index.ts + var tl2 = timeline_default2; + var wolf2 = { + id: "wolf", + title: "\u72FC\u6765\u4E86", + tagline: "\u653E\u7F8A\u7684\u5B69\u5B50", + intro: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u6C34\u58A8\u624B\u7ED8\u513F\u7AE5\u52A8\u753B\u3002\u5C71\u6751\u3001\u68AF\u7530\u3001\u7F8A\u7FA4\u548C\u4E00\u4E2A\u7231\u5F00\u73A9\u7B11\u7684\u653E\u7F8A\u5A03\u2014\u2014\u5BA3\u7EB8\u3001\u6BDB\u7B14\u3001\u6DE1\u5F69\u3001\u6731\u7EA2\u5370\u7AE0\uFF0C\u50CF\u8BED\u6587\u8BFE\u672C\u91CC\u7684\u63D2\u56FE\u4E00\u6837\u5728\u6D4F\u89C8\u5668\u91CC\u5B9E\u65F6\u753B\u51FA\u6765\uFF1B\u65C1\u767D\u3001\u653E\u7F8A\u5A03\u548C\u519C\u6C11\u5404\u6709\u58F0\u97F3\uFF0C\u914D\u4E50\u4E0E\u97F3\u6548\u540C\u6B65\u64AD\u653E\u3002", + credit: "\u6545\u4E8B\u6539\u7F16\u81EA\u300A\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u72FC\u6765\u4E86\u300B\uFF08\u516C\u6709\u9886\u57DF\uFF09", + assets: "wolf/", + slug: "wolf-coming", + timeline: tl2, + scenes: SCENES3, + seal: ["\u5BD3\u8A00"], + series: "\u4F0A\u7D22\u5BD3\u8A00 \xB7 \u72FC\u6765\u4E86", + subColors: { boy: "#9a2f22", farmer: "#2f4a5a" }, + score: score2, + effects: effects2, + covers: COVERS(tl2), + live: live_default2, + platforms: PLATFORMS2, + photoPosts: PHOTO_POSTS2, + qa: qa_default2, + making: [ + ["\u753B\u9762", "\u4E0E\u300A\u4E11\u5C0F\u9E2D\u300B\u5171\u7528\u4E00\u5957\u4EE3\u7801\u6C34\u58A8\u5F15\u64CE\uFF08\u5BA3\u7EB8\u3001\u53D8\u5BBD\u6BDB\u7B14\u3001\u98DE\u767D\u3001\u6DE1\u5F69\u3001\u5370\u7AE0\uFF09\uFF0C\u672C\u7247\u65B0\u753B\u4E86\u653E\u7F8A\u5A03\u3001\u519C\u6C11\u3001\u7F8A\u7FA4\u3001\u72FC\u3001\u68AF\u7530\u548C\u8349\u5C4B\u3002\u300C\u72FC\u6765\u4E86\u300D\u4E09\u4E2A\u6731\u7EA2\u5927\u5B57\u4ECE\u753B\u9762\u91CC\u558A\u51FA\u6765\uFF0C\u7B2C\u4E09\u6B21\u558A\u65F6\u88AB\u98CE\u5439\u6563\u2014\u2014\u6CA1\u4EBA\u4FE1\u4E86\u3002"], + ["\u58F0\u97F3", "\u4E09\u4E2A\u58F0\u97F3\uFF1A\u65C1\u767D\uFF08\u6653\u6653\uFF09\u3001\u653E\u7F8A\u5A03\uFF08\u4E91\u590F\u7AE5\u58F0\uFF09\u3001\u519C\u6C11\uFF08\u4E91\u5065\uFF09\u9010\u53E5\u5408\u6210\u540E\u6309\u65F6\u95F4\u8F74\u62FC\u63A5\u3002\u539F\u521B\u914D\u4E50\u7531\u8C03\u76AE\u7684\u7AF9\u7B1B\u6728\u9C7C\u3001\u7D27\u5F20\u7684\u4F4E\u97F3\u98A4\u594F\u3001\u60CB\u60DC\u7684\u53E4\u7B5D\u7EC4\u6210\uFF1B\u7F8A\u53EB\u3001\u558A\u58F0\u56DE\u58F0\u3001\u811A\u6B65\u3001\u72FC\u568E\u3001\u7F8A\u7FA4\u5954\u9003\u7B49\u97F3\u6548\u5168\u90E8\u7A0B\u5E8F\u5408\u6210\uFF0C\u6574\u4F53 \u221214 LUFS\u3002"], + ["\u6D41\u7A0B", "\u6309 vibe-video \u6D41\u7A0B\uFF1A\u5206\u955C \u2192 \u591A\u89D2\u8272\u914D\u97F3\u751F\u6210\u65F6\u95F4\u8F74 \u2192 \u63A5\u89E6\u8868 / \u9759\u5E27 \u2192 \u786E\u5B9A\u6027\u81EA\u68C0 \u2192 \u5168\u7247\u6E32\u67D3 \u2192 \u6DF7\u97F3 \u2192 vibe qa\uFF1B\u5B9E\u51B5\u56FE\u6309 livecanvas \u6D41\u7A0B\u914D\u5BF9\u5C01\u88C5\u3002"] + ] + }; + + // src/films/index.ts + var FILMS = [duckling, wolf2]; + + // src/film/overlays.ts + var at2 = (id, dt) => (TL.scenes.find((s) => s.id === id)?.start ?? 0) + dt; function titleBlock(g2, W2, H2, title, sub, side, o = {}) { const u = Math.min(W2, H2) / 1080; const chars = [...title]; @@ -3718,7 +7304,7 @@ }); g2.restore(); const sy = y0 + colH - fs * 0.15; - seal(g2, x + (side === "left" ? -fs * 0.05 : fs * 0.05), sy + fs * 0.1, fs * 0.42, o.seal ?? ["\u7AE5\u8BDD"]); + seal(g2, x + (side === "left" ? -fs * 0.05 : fs * 0.05), sy + fs * 0.1, fs * 0.42, o.seal ?? FILM.seal); if (o.index) { g2.save(); g2.font = `${sfs * 1.1}px ${FONT_BRUSH}`; @@ -3728,15 +7314,15 @@ g2.restore(); } } - var LIVE = live_default; - var liveStart = (l) => at(l.scene, l.from); + var liveStart = (l) => at2(l.scene, l.from); function drawLive(g2, W2, H2, l, lt) { renderFrame(g2, W2, H2, liveStart(l) + lt, { subs: false, fadeEnd: false }); - titleBlock(g2, W2, H2, l.caption, "\u5B89\u5F92\u751F\u7AE5\u8BDD \xB7 \u4E11\u5C0F\u9E2D", "right", { big: 120, index: l.index }); + titleBlock(g2, W2, H2, l.caption, FILM.series, "right", { big: 120, index: l.index }); } // src/film/standalone.ts var q = new URLSearchParams(location.search); + setFilm(FILMS.find((f) => f.id === (q.get("film") || "duckling"))); var W = Number(q.get("w")) || innerWidth; var H = Number(q.get("h")) || innerHeight; var c = document.createElement("canvas"); @@ -3755,19 +7341,19 @@ }); window.renderAudio = async () => { const t0 = performance.now(); - const b = await buildSoundtrack((q.get("base") || "./") + "film/voice.mp3", (s2) => console.log("stage", s2, Math.round(performance.now() - t0))); + const b = await buildSoundtrack((q.get("base") || "./") + FILM.assets + "voice.mp3", (s2) => console.log("stage", s2, Math.round(performance.now() - t0))); const bytes = new Uint8Array(toWav(b)); let s = ""; for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode(...bytes.subarray(i, i + 32768)); return btoa(s); }; - window.__dbg = { ...actors_exports, ...core_exports }; + window.__dbg = { ...actors_exports, ...core_exports, W: actors_exports2 }; window.__g = g; window.__W = W; window.__H = H; - window.__live = LIVE; + window.__live = FILM.live; window.renderLive = (id, lt) => { - const l = LIVE.find((x) => x.id === id); + const l = FILM.live.find((x) => x.id === id); if (l) drawLive(g, W, H, l, lt); }; })(); diff --git a/ugly-duckling/video/film/fonts/mashanzheng.woff2 b/ugly-duckling/video/film/fonts/mashanzheng.woff2 index c6ebf28b26ed73e01389a2ba5bcee1e199f70a24..336f627eea468eaf298942d59a3c615369298a25 100644 --- a/ugly-duckling/video/film/fonts/mashanzheng.woff2 +++ b/ugly-duckling/video/film/fonts/mashanzheng.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009f88eccbb75b93ece3214992da9dbab3c96e3e003d1078a88d753a001b630 -size 343228 +oid sha256:fe7ce130e02290a7dff93743e8a7492fb7c41bdbe9d6b338945144e018735476 +size 420940 diff --git a/ugly-duckling/video/film/fonts/wenkai.woff2 b/ugly-duckling/video/film/fonts/wenkai.woff2 index a9a12a80e05800bdfaaf761ad87db25d19e2e1cf..d3eee58aa0e17d95557ba0ac444a80499e4a0fef 100644 --- a/ugly-duckling/video/film/fonts/wenkai.woff2 +++ b/ugly-duckling/video/film/fonts/wenkai.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c673c2d90b5d3e48ff76694316aec488e221d610b6269440d795d093207566fe -size 200844 +oid sha256:14374c15ca95593a0fc159be8f69f4435a3e7c4206b1817fd7bfba9495222f02 +size 240976 diff --git a/ugly-duckling/web/public/film/fonts/chars.txt b/ugly-duckling/web/public/film/fonts/chars.txt index 5348265823253b4387b6741f8393f709b944c985..5a6a6801656437bb54e03fd44f64af53b5e26e4f 100644 --- a/ugly-duckling/web/public/film/fonts/chars.txt +++ b/ugly-duckling/web/public/film/fonts/chars.txt @@ -1 +1 @@ - !#%+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz·—‘’“”…、。《》「」【】・一三上下不与丑世丛东丝两个中为主丽举久么乎乐也乡书乱了事二于云五些交享亮亲人仅今介从他代以们仰任伙会传伴位低住体何作你例侧便俏保信倒候值停傍像儿充入全公六共关其具内再写冠冬冰冲决况冷冻准几出击函分则创到制前剩副力加动勾匀包化区半单印即压原又叉友发取变叠口古句另只叫可台右叶号叽各合同名后向吓吗吧呆告命和咔咦咬哀哈响哔哥哩哪唧啄喂善喊喙喜喳嘲嘴器噪嚓四回团围固国图圆圈在地场均坐块垂垛域堆塘填境增墨壁声壳处备复夏外多夜大天太失头夹奇奏她好妈始姐姑娘嫌子字存季孤学孩孵它安完官定实宣宫家宽寂寄密寒寸对导封小尖尚就尺尼尾层屋屏展属山岸峰工左差己已布希帘带帧幅幕干平并序应底度廓延建开式引张弦弯弹归当录形彩影往径很徒得循微心快念忽态怕性怪总息悄悬情惊愈意感慢成截所扇手才扑打扫扬把抖折报拉拍拖拢拨择持按挖振换掉排掠接控推描提插揭撇播擎支收改放政故效敢数整文斑断新旁无日时明春是昼显晓晕晚景晰暂暖暗曲更替最有朋望朝期木未本术朱机权村条来极构果枝枪染柔查柳标树校样核根格桃案梅梦梳检楷模横檐欠次欢欺款歌正此步段每比毛气水池治沼沿法波泥泪泽洁流测浏浓浮消淡深混清渐渡温渲游湖湿溅源溪滴漂漫潮灰点烟烧然照熬片版牙物状猎率环现理瓣生用田由电画畅界痕登白的皮皴益盖盛目直相省看真眼着睡瞬瞻短码砂砰破确碍示神离秋种秒积程空窗窝立竖章童竹笆笑笔笛符等筝签简篇篱簧粒粘系素紧红纤约级纳纷纸纹线组终经绒结绘给络绪续维缓编缘缩网置美群羽翅翻翼而肉背能脊脑脖脚脸腿膀臂自致色艺节芦花芽苇苹范茸草荷落蓝藏蛋蜓蜻蝴蝶融行衣补表被裁裂装西要见观规视览觉角解触计让记讲证试话语说请读课调负败质贴资走起路跳身躲轨转轮软轴轻载输辨边过近还这进远连迟述追送适选逐递通遮那部都配采里重野量金钟钻铃铺错键镜长门间阅阴阵阶阻附限除随难雁雪雾需霞露静面音页顶顿预领颈频颗题颜颤风飘飞马驳验高髻鱼鸟鸡鸣鸭鹅鹜麦黄黑齐️︐!()+,:;?|🐓👀🥚🦢 \ No newline at end of file + !#%+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz·—‘’“”…、。《》「」【】・一三上下不与丑专世丛东丝两个中为主丽举久么义之乌乎乐也乡书乱了事二于云互五些交享亮亲人什仅今介从他代以们仰任伊伙会传伤伴伸位低住体何余作你使例供侧便促俏保信倒候值做停健傍像儿充先光免入全公六共关其具内再写农冠冬冰冲决况冷冻准几出击函分切列则创到制刻前剩剪副力加动勾匀包化区升半单印即却卷压原去又叉及友双发取受变叠口古句另只叫可台右叶号叼叽吃各合同名后向吓吗否吧听吹吻呀呆呈告呼命和咔咦咬哀哈响哔哥哩哪哭哼唧啄啊啦喂善喊喘喙喜喳嗓嘲嘴器噪嚎嚓四回团园围固国图圆圈在地场址均坏坐块坡垂型垛域堆塘塞填境墙增墨壁声壳处备复夏夕外多夜大天太失头夹奇奏奔套她好如妈始姐姑姿娃娘嫌子字存季孤学孩孵它安完官定实宣宫家宽寂寄密寒寓寸对导封小尔尖尚就尺尼尾层屋屏展属山岸峰工左差己已巾布希帘带帧帽幅幕干平并序库应底度廓延建开式引张弦弧弯弹强归当录形彩影往径很徒得循微心忙快念忽态怕急性怪总息悄悔悠悬情惊惋惜愈意感慢憋懒成我或截所扁扇手才扑打扛扫扬扰把抖折报抱担拉拍拖拢拨择拱拼拿持挂按挖挨振换掉排掠探接控推描提插握揭摇撇播擎支收改放政故效救敢散数整文斑斜断新方旁无日时明易春是昼显晓晕晚景晰暂暖暗曲更替最有朋望朝期木未末本术朱机权村条来极构果枝枪柄染柔查柳标树校样核根格桃案梅梦梯梳检楷模横檐欠次欢欺款歌正此步段每比毛民气水求汗池沙没治沼沿法波泥泪泽洁洋活流测浏浓浮消润淡深混清渐渡温渲游湖湿溅源溪滑滴漂漫潮灯灰点烟烧然照熬爱片版牙物状独狼猎率玩环现珠理瓣生用田由电画畅界略疏痕登白的皮皴益盖盛目直相省眉看真眼着睛睡瞬瞻短石码砂砰破确碍碎示神离秋种秒秧积移稀程稍空窗窝立竖章童竹笆笑笔笛符第等筐筝签简算篇篱簧类粒粗粘系素索紧红纤约级纳纷纸纹线组细终经绑绒结绘给络绪续维绿缓编缘缩网置署羊美群羽翅翻翼而耳聊肉肢肤背胸能脊脑脖脚脸腮腰腿膀膝臂自致色艺节芦花芽苇苗苹范茅茸草荷莹落蓝藏蛋蜓蜻蝴蝶融行衣补表袖被裁裂装裤襟西要见观规视览觉角解触言警计认让记讲设证词试诚话语说请读课谁调谎谷贝负败质贴资走赶起越跑路跳身躯躲躺轨转轮软轴轻载输辨辫边迁过近还这进远连迟述追送适逃选逐递逗通逼遍道遮避那部都配采里重野量金钟钻铃铺链锄错键镜长门闪间阅阳阴阵阶阻附限除随难雁雪雷雾需霞露静靠面音页顶顿预领颈频颗题颜额颤风飘飞马驳验骗骨高髻鱼鸟鸡鸣鸦鸭鹅鹜麦黄黑默鼻齐️︐!()+,:;?|~🐑🐓🐺👀📢😂🥚🦢 \ No newline at end of file diff --git a/ugly-duckling/web/public/film/fonts/mashanzheng.woff2 b/ugly-duckling/web/public/film/fonts/mashanzheng.woff2 index c6ebf28b26ed73e01389a2ba5bcee1e199f70a24..c86f0d69b2acb5f9e7ca4b019192cbbe92406667 100644 --- a/ugly-duckling/web/public/film/fonts/mashanzheng.woff2 +++ b/ugly-duckling/web/public/film/fonts/mashanzheng.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009f88eccbb75b93ece3214992da9dbab3c96e3e003d1078a88d753a001b630 -size 343228 +oid sha256:27e0895918dceab34deaf286fa50e2dee8abb9b73fb32b2ce13270f8f8215224 +size 423596 diff --git a/ugly-duckling/web/public/film/fonts/wenkai.woff2 b/ugly-duckling/web/public/film/fonts/wenkai.woff2 index a9a12a80e05800bdfaaf761ad87db25d19e2e1cf..5238c23358aee4284723168731e22d88c781f4c1 100644 --- a/ugly-duckling/web/public/film/fonts/wenkai.woff2 +++ b/ugly-duckling/web/public/film/fonts/wenkai.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c673c2d90b5d3e48ff76694316aec488e221d610b6269440d795d093207566fe -size 200844 +oid sha256:88500492b8f490478e791976eef7824f7527c1d788ee205d7dc443c153c3ab8f +size 242444 diff --git a/ugly-duckling/web/public/wolf/live/01-idea.jpg b/ugly-duckling/web/public/wolf/live/01-idea.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27ee8fa76b437375616289d97df7011cc7349060 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/01-idea.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daaf2dd2964d96a85c7c7f299958c8052b5b79bac7ec9c939c69c14386b98042 +size 421326 diff --git a/ugly-duckling/web/public/wolf/live/02-shout.jpg b/ugly-duckling/web/public/wolf/live/02-shout.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f5b9125a707a808e9988cf4605bbaa54517a889 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/02-shout.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:951fca968d620ba60da906b34d4df381edb37598465c016d6dea892926f4a2f1 +size 434382 diff --git a/ugly-duckling/web/public/wolf/live/03-run.jpg b/ugly-duckling/web/public/wolf/live/03-run.jpg new file mode 100644 index 0000000000000000000000000000000000000000..20241e9d8a470f5561d8b062d47a1dc3cd728386 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/03-run.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f2ee14a9c52ceb50aad314c5eb27059566688a46428b4de65b257bb87c48da5 +size 364562 diff --git a/ugly-duckling/web/public/wolf/live/04-laugh.jpg b/ugly-duckling/web/public/wolf/live/04-laugh.jpg new file mode 100644 index 0000000000000000000000000000000000000000..30bc9d7fe97b38f7a104bcada5f51f611dfcddf3 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/04-laugh.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e5b3d0f223ed21cf2c69bf9628a200af4d8919169ff88461682715d50099067 +size 398427 diff --git a/ugly-duckling/web/public/wolf/live/05-eyes.jpg b/ugly-duckling/web/public/wolf/live/05-eyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4b1ae99c2f26fe34cca0cb7f2337eb66b9756832 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/05-eyes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b9c32d86ddeed564159149c5dcdeafc3e35ad15d19358de128f2324340b0128 +size 304244 diff --git a/ugly-duckling/web/public/wolf/live/06-regret.jpg b/ugly-duckling/web/public/wolf/live/06-regret.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f7050989bc067a35eb78337fb42abf6c116b076 --- /dev/null +++ b/ugly-duckling/web/public/wolf/live/06-regret.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d2fc882730873e04b31deb89f22008e6212cdf38b38aae861561bbb337a58b +size 303063 diff --git a/ugly-duckling/web/public/wolf/qa/sheet_landscape.jpg b/ugly-duckling/web/public/wolf/qa/sheet_landscape.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d9b9126ca5f3d13eac9c946f6ffe28fa6a933dda --- /dev/null +++ b/ugly-duckling/web/public/wolf/qa/sheet_landscape.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9c0cd15f17a1b11ae186ff73fc167b63fd53961f4e58fab4bf81f8a32b0e8c7 +size 316968 diff --git a/ugly-duckling/web/public/wolf/qa/sheet_portrait.jpg b/ugly-duckling/web/public/wolf/qa/sheet_portrait.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f7530921263a186742c45927e94d1a06ada0cd5d --- /dev/null +++ b/ugly-duckling/web/public/wolf/qa/sheet_portrait.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fba50b501911db85a198c585d524c352ff1a7c633ad6714d5c516a26a588b9ca +size 164205 diff --git a/ugly-duckling/web/public/wolf/voice.mp3 b/ugly-duckling/web/public/wolf/voice.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7da9c682a8da2c523b61eb98157aa6fb97422c35 --- /dev/null +++ b/ugly-duckling/web/public/wolf/voice.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d161c8ce43a4b1d0cc2927f83ec8a118133e873443bc70dc060f425d584e483 +size 1655162 diff --git a/ugly-duckling/web/src/App.tsx b/ugly-duckling/web/src/App.tsx index fa5c4c8b78c6a6d5ea9f9c1de454e944ae96002c..9cae8caba0c8ce7c5db62a7bfed28fa2df40f344 100644 --- a/ugly-duckling/web/src/App.tsx +++ b/ugly-duckling/web/src/App.tsx @@ -5,7 +5,12 @@ import Covers from '@/components/Covers' import PublishCopy from '@/components/PublishCopy' import LivePhotos from '@/components/LivePhotos' import Making from '@/components/Making' -import { TOTAL } from '@/film/film' +import { TOTAL, FILM, setFilm } from '@/film/film' +import { FILMS, pickFilm } from '@/films' + +const PICK = pickFilm() +setFilm(PICK.film) +if (typeof window !== 'undefined' && !PICK.locked) window.addEventListener('hashchange', () => location.reload()) const isPhone = () => typeof window !== 'undefined' && window.innerWidth < 768 && window.innerHeight > window.innerWidth @@ -29,8 +34,13 @@ export default function App() {
- 鸭 - 丑小鸭 + {[...FILM.title].pop()} + {FILM.title} + + {FILMS.filter((f) => f.id !== FILM.id).map((f) => ( + 系列 · 看《{f.title}》 + ))} + @@ -40,14 +50,12 @@ export default function App() {
-

丑小鸭变成白天鹅

-

- 安徒生童话 · 水墨手绘儿童动画。宣纸、毛笔、淡彩、朱红印章,像小学语文课本里的插图那样,一笔一画在你的浏览器里实时画出来;旁白、原创国风配乐与音效同步播放。 -

+

{FILM.title}{FILM.tagline}

+

{FILM.intro}

{Math.floor(TOTAL / 60)}′{Math.round(TOTAL % 60)}″
片长
-
14
场景
+
{FILM.timeline.scenes.length}
场景
2
画幅
@@ -60,7 +68,7 @@ export default function App() { -
+
@@ -68,7 +76,7 @@ export default function App() {
-
+
@@ -77,7 +85,7 @@ export default function App() {
- 故事改编自安徒生《丑小鸭》(1843,公有领域)· 画面、配乐、音效均由代码原创生成 · 字体:霞鹜文楷、马善政毛笔楷书(SIL OFL) + {FILM.credit} · 画面、配乐、音效均由代码原创生成 · 字体:霞鹜文楷、马善政毛笔楷书(SIL OFL)
) diff --git a/ugly-duckling/web/src/components/Covers.tsx b/ugly-duckling/web/src/components/Covers.tsx index 87275d73a3d7e3769ef17f27e41c690b0dce7a97..61207ada8eaf6a2c065da1d343895f95ab32c4c0 100644 --- a/ugly-duckling/web/src/components/Covers.tsx +++ b/ugly-duckling/web/src/components/Covers.tsx @@ -1,12 +1,14 @@ import { useEffect, useState } from 'react' import { Check, Download, Loader2, PackageOpen } from 'lucide-react' import { zipSync } from 'fflate' -import { COVER_DESIGNS, COVER_RATIOS, drawCover } from '@/film/overlays' +import { COVER_RATIOS, drawCover } from '@/film/overlays' +import { FILM } from '@/film/film' +import type { CoverDesign } from '@/film/types' import { loadFonts } from '@/film/core' const BASE = import.meta.env.BASE_URL type Ratio = (typeof COVER_RATIOS)[number] -type Design = (typeof COVER_DESIGNS)[number] +type Design = CoverDesign type Item = { key: string; ratio: string; design: string; name: string; url: string; w: number; h: number } const THUMB = 420 // 预览图长边;下载时才按原尺寸重新绘制(省内存,手机也流畅) @@ -20,8 +22,8 @@ async function renderCover(ratio: Ratio, d: Design, scale = 1): Promise { c.width = c.height = 1 return blob } -const fullName = (r: Ratio, d: Design) => `丑小鸭封面_${r.id.replace(':', 'x')}_${d.name}_${r.w}x${r.h}.jpg` -const find = (key: string) => { const [r, d] = key.split('|'); return { r: COVER_RATIOS.find((x) => x.id === r)!, d: COVER_DESIGNS.find((x) => x.id === d)! } } +const fullName = (r: Ratio, d: Design) => `${FILM.title}封面_${r.id.replace(':', 'x')}_${d.name}_${r.w}x${r.h}.jpg` +const find = (key: string) => { const [r, d] = key.split('|'); return { r: COVER_RATIOS.find((x) => x.id === r)!, d: FILM.covers.find((x) => x.id === d)! } } export default function Covers() { const [items, setItems] = useState([]) @@ -33,7 +35,7 @@ export default function Covers() { let dead = false ;(async () => { await loadFonts(BASE) - for (const r of COVER_RATIOS) for (const d of COVER_DESIGNS) { + for (const r of COVER_RATIOS) for (const d of FILM.covers) { if (dead) return await new Promise((res) => setTimeout(res, 30)) // 让出主线程 const blob = await renderCover(r, d, THUMB / Math.max(r.w, r.h)) @@ -44,7 +46,7 @@ export default function Covers() { return () => { dead = true } }, []) - const total = COVER_RATIOS.length * COVER_DESIGNS.length + const total = COVER_RATIOS.length * FILM.covers.length const shown = items.filter((i) => tab === '全部' || i.ratio === tab) const toggle = (k: string) => setSel((s) => { const n = new Set(s); if (n.has(k)) n.delete(k); else n.add(k); return n }) const [busyKey, setBusyKey] = useState(null) @@ -55,7 +57,7 @@ export default function Covers() { const files: Record = {} for (const it of items.filter((i) => sel.has(i.key))) { const { r, d } = find(it.key); files[it.name] = new Uint8Array(await (await renderCover(r, d)).arrayBuffer()); await new Promise((res) => setTimeout(res, 10)) } const out = zipSync(files, { level: 0 }) - save(new Blob([out as Uint8Array], { type: 'application/zip' }), `丑小鸭封面_${sel.size}张.zip`) + save(new Blob([out as Uint8Array], { type: 'application/zip' }), `${FILM.title}封面_${sel.size}张.zip`) setZipping(false) } @@ -84,7 +86,7 @@ export default function Covers() { {on && }
- {it.ratio}{COVER_DESIGNS.find((d) => d.id === it.design)?.name} · {it.w}×{it.h} + {it.ratio}{FILM.covers.find((d) => d.id === it.design)?.name} · {it.w}×{it.h}
diff --git a/ugly-duckling/web/src/components/ExportPanel.tsx b/ugly-duckling/web/src/components/ExportPanel.tsx index 282863b27a82df34206c3726406d09811bc7b016..887d0527c70e36597633613765cdaa43ce38826e 100644 --- a/ugly-duckling/web/src/components/ExportPanel.tsx +++ b/ugly-duckling/web/src/components/ExportPanel.tsx @@ -1,7 +1,7 @@ import { useRef, useState } from 'react' import { Download, Film, Loader2, X, Smartphone, Monitor } from 'lucide-react' import { Output, Mp4OutputFormat, BufferTarget, CanvasSource, AudioBufferSource, QUALITY_HIGH, getFirstEncodableVideoCodec, getFirstEncodableAudioCodec } from 'mediabunny' -import { renderFrame, TOTAL } from '@/film/film' +import { renderFrame, TOTAL, FILM } from '@/film/film' import { buildSoundtrack } from '@/film/audio' import { loadFonts } from '@/film/core' @@ -30,7 +30,7 @@ export default function ExportPanel({ defaultAspect }: { defaultAspect: Aspect } try { setMsg('准备字体与音轨…') await loadFonts(BASE) - const full = await buildSoundtrack(`${BASE}film/voice.mp3`) + const full = await buildSoundtrack(`${BASE}${FILM.assets}voice.mp3`) // ?exportSeconds=N 仅用于自动化测试(只导出前 N 秒) const lim = Number(new URLSearchParams(location.search).get('exportSeconds')) || TOTAL const N = Math.round(Math.min(TOTAL, lim) * fps) @@ -68,7 +68,7 @@ export default function ExportPanel({ defaultAspect }: { defaultAspect: Aspect } await output.finalize() const buf = (output.target as BufferTarget).buffer! const blob = new Blob([buf], { type: 'video/mp4' }) - const name = `丑小鸭_${aspect === '16:9' ? '横屏' : '竖屏'}_${W}x${H}_${fps}fps.mp4` + const name = `${FILM.title}_${aspect === '16:9' ? '横屏' : '竖屏'}_${W}x${H}_${fps}fps.mp4` setFile({ url: URL.createObjectURL(blob), name, size: blob.size }) setProg(1) setMsg(`完成:${vcodec.toUpperCase()} 视频${acodec ? ` + ${acodec.toUpperCase()} 音频` : '(此浏览器不支持音频编码,导出为无声视频)'}`) diff --git a/ugly-duckling/web/src/components/LivePhotos.tsx b/ugly-duckling/web/src/components/LivePhotos.tsx index 0b23dc133171cb23d6b3d3945ef0d4a9dfaa5791..b22c265266dde8d37cb71cef028b93ee65989545 100644 --- a/ugly-duckling/web/src/components/LivePhotos.tsx +++ b/ugly-duckling/web/src/components/LivePhotos.tsx @@ -1,9 +1,9 @@ import { Download, PackageOpen, Apple } from 'lucide-react' -import { LIVE } from '@/film/overlays' +import { FILM } from '@/film/film' import { PhotoCopy } from './PublishCopy' const BASE = import.meta.env.BASE_URL -const L = (f: string) => `${BASE}film/live/${f}` +const L = (f: string) => `${BASE}${FILM.assets}live/${f}` export default function LivePhotos() { return ( @@ -11,13 +11,13 @@ export default function LivePhotos() {
- 6 组苹果实况照片(每组 JPG + MOV,3:4 · 1080×1440 · 3 秒 · 30fps),按 livecanvas 流程写入 Live Photo 配对信息(共享 content identifier + still-image-time),另附 .pvt 资源包。 + {FILM.live.length} 组苹果实况照片(每组 JPG + MOV,3:4 · 1080×1440 · 3 秒 · 30fps),按 livecanvas 流程写入 Live Photo 配对信息(共享 content identifier + still-image-time),另附 .pvt 资源包。 在 Mac 上解压后,把同名的 JPG 和 MOV 一起拖进「照片」即合成为实况图,再同步到手机发小红书 / 抖音图文。
- 下载实况图压缩包 + 下载实况图压缩包
- {LIVE.map((l) => ( + {FILM.live.map((l) => (