# B6 光速穿梭·爆炸·重聚 > 同一份提示词,分别交给 Opus 5.5(High)和 DeepSeek-V4.1-Flash(1017,High)做出来的结果就在这个文件夹里。{{ }} 里是要你自己填的内容。 你是一位前端动效工程师。请写**一个完整的 index.html 文件**:只用原生 HTML + JavaScript + Canvas 2D,不要任何外部库、字体文件或图片。做一段 12 秒、**一镜到底**的动画:光速穿梭 → 连闯 5 个光环 → 刹车、粒子被吸向中心 → 爆炸 → 碎片旋转着重聚成标题字。整段是一台摄像机在 3D 空间里一直向前飞,**从头到尾不切镜**;同一批 4000 颗粒子先是星光线,后来变成爆炸的碎片,最后拼成标题。 ## 内容 - 标题:{{默认:俯仰观照}} - 副标:{{默认:于江流见瞬息,于树木见永恒}} ## 输出要求(必须照做) 1. 只输出一个 html 代码块,不要解释。代码必须完整可运行:不要用省略号,不要写"其余同上""此处略"。 2. canvas 固定 1920×1080;用 CSS 让它在窗口里等比缩放并居中,多出来的部分是黑边。 3. 画面是时间 t(秒)的纯函数 `draw(t)`。不要用 `Math.random`;所有随机都用带种子的 mulberry32(7),只在启动时生成一次。不要在 draw 里保存上一帧的状态。同一个 t 每次画出来都一模一样。 4. 默认循环播放(requestAnimationFrame,t = 已播放秒数,对 12 取余);按空格暂停/继续;网址带 `?t=5.7` 时只显示第 5.7 秒那一帧(静止)。 ## 常量 W=1920,H=1080,屏幕中心 (960,540);N=4000 颗粒子;总长 12 秒;隧道长度 L=6000;基础焦距 F0=900。 ## 镜头(相机沿 z 轴前进。先把这几个函数写对) ```js const V0 = 80, V1 = 2600; const z1 = t => V0*t + (V1-V0)*(3/3.2)*Math.pow(Math.min(t,3)/3, 3.2) + V1*Math.max(0, t-3); const Z5 = z1(5.0), zB = Z5 + 780, ZC = zB + 900, Z3 = z1(3), ZT = zB - 700; function camZ(t) { if (t <= 5.0) return z1(t); // 加速到光速 if (t <= 5.6) { const u = (t-5.0)/0.6; return Z5 + 2600*0.6*(u - u*u/2); } // 急刹车 if (t <= 8.0) { const v = (t-5.6)/2.4; return zB - 2200*(1-(1-v)*(1-v)); } // 爆炸后猛地后撤 return zB - 2200 - 120*(t-8.0); // 之后慢慢后撤 } ``` - **5 个光环**:第 k 个(k=0..4)在世界坐标 z = Z3 + 2600·(0.3 + 0.42k),镜头穿过它的时间 tk = 3.3 + 0.42k;k 为偶数时颜色 (125,249,255),奇数时 (255,213,154);半径 R=260。 - **焦距** f(t) = 900 + 对每个 tk≤t 的环加 260·exp(−9(t−tk)) + (t≥5.6 时再加 520·exp(−6(t−5.6)))——每穿过一个环、以及爆炸的瞬间,画面会猛地"顶"一下。 - **翻滚** roll(t) = 0.02·sin(1.7t) + (t≥5.6 时再加 0.12·sin(2π·0.9·(t−5.6))·exp(−2.2(t−5.6)))。 - **抖动**:amp = (t≥5.6 ? 22·exp(−3.2(t−5.6)) : 0),偏移 = (sin(53t)·amp, cos(47t)·amp)。 - 画粒子、光环、核心和冲击波之前:`ctx.translate(960+抖动x, 540+抖动y); ctx.rotate(roll); ctx.translate(-960, -540)`;画完这些再 `restore`,闪白、字幕等不受翻滚和抖动影响。 ## 投影 一个点在"相机空间"的坐标 (X, Y, d)(d 是它到相机的距离)画在屏幕上:`sx = 960 + f·X/d`,`sy = 540 + f·Y/d`。 ## 每颗粒子的固定参数(启动时生成一次) - `a`(0–2π);`rho` = 220 + 1280·rand^0.8;`zw` = rand·L(隧道里的世界深度);`s`(0–1);`cls`(15% 为 1,其余为 0);`ph`(0–2π);`fq`(0.6–1.6); - 爆炸方向 `dir`:zz = −0.55 + 1.55·rand,φ = rand·2π,rxy = √(1−zz²),dir = (rxy·cosφ, rxy·sinφ, zz); - 爆炸力度 `mag` = 300 + 2600·rand^1.6; - 旋转量 `sw` = (1.2 + 2·rand)·(随机正负号); - 标题目标点 `(tx, ty)`:见下。 ## 标题的目标点 用 1200×400 的离屏 canvas,白色粗体字(`bold 280px "Noto Serif SC","Songti SC","SimSun",serif`,水平居中,`textBaseline='middle'`,位置 (600, 205))写出标题;每隔 3 个像素取一个 alpha>128 的点,坐标换算成 ((x−600)·2.093, (y−200)·2.093);带种子洗牌后取 N 个(不够就重复取,并加 ±3 的抖动)。第 i 颗粒子用第 i 个点。 ## 每颗粒子在时间 t 的相机空间位置(写成函数 `place(p, t)`,返回 X, Y, d) - **t < 5.6(隧道)**:k = 1 − 0.92·smoothstep(5.0, 5.6, t)(5.0–5.6 秒粒子向画面中心收拢);X = rho·cos(a)·k,Y = rho·sin(a)·k;d = ((zw − camZ(t)) mod L + L) mod L + 60。 - **5.6 ≤ t ≤ 8(爆炸)**:τ = min(1, (t−5.6)/2.4),g = 1 − (1−τ)³;世界坐标 X = dir.x·mag·g,Y = dir.y·mag·g,Z = ZC + dir.z·mag·g;d = Z − camZ(t)。 - **t > 8(重聚)**:e = smoothstep(8, 10, t);起点 E = (dir.x·mag, dir.y·mag, ZC + dir.z·mag),终点 Tg = (tx, ty, ZT);Q = E + (Tg − E)·e;再把 (Q.x, Q.y) 绕原点旋转 θ = sin(π·e)·sw;X, Y = 旋转后的结果,Z = Q.z;d = Z − camZ(t)。 ## 每帧怎么画 1. **背景** #05060A;再画一个中心 (960,540)、半径 700 的径向渐变:从 `rgba(30,60,90, 0.28·sf·(t<5.6 ? 1 : 0.4))` 到 `rgba(5,6,10,0)`。sf = 0.45 + 0.55·clamp(speed/2600, 0, 1),speed 是相机当前速度:t<3 时 80+2520·(t/3)^2.2;3–5 秒 2600;5–5.6 秒 2600·(1−(t−5)/0.6);其余 0。 2. 下面所有东西用 `globalCompositeOperation='lighter'`,`lineCap='round'`。 3. **粒子**:每颗画成一条线段,从"时间 tp 的屏幕位置"连到"时间 t 的屏幕位置"。tp = max(t − DT, t≥5.6 ? 5.6 : 0),DT = (t<5.6 ? 0.03 : 0.06 − 0.03·smoothstep(8, 9, t));tp 时刻用 tp 对应的焦距。只要任一端 d<30,或两端 d 相差超过 500(刚好绕回隧道开头),这一条就不画。 - **颜色**:t<5.0:cls=1 为 (255,213,154),否则 (191,232,255);5.0–5.6:向白色按 smoothstep(5.0, 5.6, t) 混合;5.6–8:τ<0.4 时从白色混到橙 (255,176,74),之后从橙混到洋红 (255,61,110);8–10:从洋红混到暖白 (246,235,208)(按 smoothstep(8,10,t));t≥10:暖白。 - **透明度**:t<5.6:(1 − d/L)^1.4·(0.25 + 0.55·s)·sf·1.6,限制在 0–1;t≥5.6:0.9·(0.7 + 0.3·sin(fq·t + ph))·(t<8 ? 1−0.4τ : 0.9)。 - **线宽**:t<5.6:(0.9 + 1.8·s)·clamp(√(900/d), 0.5, 2.2);t≥5.6:(2 + 3·s)·clamp(√(900/d), 0.6, 2.2)。 4. **光环(只在 t<5.6 时画!)**:对每个环,d = 环的 z − camZ(t);d<40 或 d>5200 不画;否则沿半径 R=260 画一个 96 段的圆:点 = (960 + f·R·cosθ/d, 540 + f·R·sinθ/d);线宽 clamp(6·900/d, 1.5, 16),透明度 0.6·clamp(1 − d/5200, 0, 1);画一遍之后,再用 3 倍线宽、1/4 透明度多画一遍当光晕。 5. **核心的光**:t≥4.4 且 dc = ZC − camZ(t) > 40 时,画一个白色径向渐变圆(中心 (960,540),从 `rgba(255,255,255,0.9)` 到透明),半径 = 140000/dc·(0.4 + 0.6·smoothstep(4.4, 5.6, t))·(f/900),t≥5.6 时再乘 exp(−3(t−5.6))。 6. **冲击波**:t≥5.6 时,ts = clamp((t−5.6)/1.2, 0, 1),世界半径 Rw = 3600·(1 − (1−ts)³);若 ts<1 且 dd = ZC − camZ(t) > 40:画一个白色描边圆,圆心 (960,540),半径 f·Rw/dd,透明度 0.8·(1−ts),线宽 1 + 8·(1−ts)。 7. 取消平移/旋转(`restore`),再依次画: - **闪白**:全屏白色矩形(lighter),透明度 = (t≥5.6 ? 0.95·exp(−9(t−5.6)) : 0.7·smoothstep(5.4, 5.6, t)) + 每个 t≥tk 的环加 0.3·exp(−14(t−tk)) + (t≥10 ? 0.35·exp(−8(t−10)) : 0),总和限制到 1; - **标题凝固**(source-over):透明度 0.9·smoothstep(9.7, 10.3, t)·(1 − smoothstep(11.4, 12, t)),在 (960, 545) 居中画标题,`bold 300px` 同样的字体,颜色 #F6EBD0; - **副标**:透明度 0.85·smoothstep(9.6, 10.4, t)·(1 − smoothstep(11.4, 12, t)),在 (960, 790) 居中,衬线体 40px,颜色 #EDE6D6; - **暗角**:径向渐变,半径 400→1150,从 `rgba(0,0,0,0)` 到 `rgba(0,0,0,0.55)`; - **首尾淡入淡出**:黑色全屏矩形,透明度 (1 − smoothstep(0, 0.35, t)) + smoothstep(11.4, 12, t)。 smoothstep(a, b, x):t = clamp((x−a)/(b−a), 0, 1),返回 t·t·(3 − 2t)。 ## 不要 `Math.random`;在 `draw` 里保存上一帧的状态;用 DOM 元素做动画;多余的颜色;把光环在爆炸后再画出来(镜头后撤时它们会"迎面"冒出来,必须不画)。 ## 写完后自己过一遍(不要输出) - 同一个 t 刷新两次,画面是否一模一样? - t=2.8 看到的是不是向四周拉长的星光线,中间有几个青色/琥珀色的环? - t=5.3 是不是粒子被吸向中心、中心一团白光?t=5.7 是不是一团白色放射状的爆炸? - t=9.5 是不是粒子在旋转着收拢?t=10.8 是不是清楚的标题字? - 一帧画 4000 条线加 5 个环,会不会卡?