KPI 卡为什么要砸进屏幕:Remotion 三式冲击波入场动效完整拆解
【免费下载链接】video-shotcraftAI video skill for Claude Code & Codex — cinematic, editable product promos with Remotion: 124 shot recipe cards, 150 motion previews, beat sync, sound design, Motion Workbench & JianYing (CapCut CN) export.项目地址: https://gitcode.com/gh_mirrors/vi/video-shotcraft
第 15 帧,一张 1240×400 的终场比分卡从镜头前 2.75 倍大小加速砸到屏心,落点同帧:冲击环从 120px 扩到 1500px、14 片碎片抛物线飞散、18px 震屏指数衰减、白闪 3 帧——这是 video-shotcraft 的 slam-entrance-moves 词汇卡在 Remotion 里的ScoreSlam落点帧。这个仓库是 124 张镜头配方卡、150 个动效预览的 AI 视频技能包,slam-entrance-moves 提供三个冲击波入场式:KanadaPerspectiveSnap(透视急停甩入)、ScoreSlam(比分砸落)、ImpactBurstKit(落点冲击波及邻卡),源码都在demos/effects/slam-entrance-moves/。本文讲三式各自的帧时间轴、缓动选型、源码里最关键的一两行,以及最先翻车的几个症状。
先判断这张卡配不配砸
砸是入场库里的重武器,全片主角才配得上。判据按"叙事重量"给:
- 该砸:全片主角卡(发布片首映卡)、终场比分、KPI 大数字——每部片子这样的元素不超过 2 个。
- 不该砸:列表项、普通标签、辅助说明卡。这类用 deck-deal-flyin 的发牌式飞入或普通滑入就够,它们"抢"了砸,全片重量感就塌了。
三式各占一个生态位:B(ScoreSlam)是重量担当,单卡从镜头前砸到屏心;A(KanadaPerspectiveSnap)是方向感担当,卡贴着镜头甩进来再弹平;C(ImpactBurstKit)是唯一会把"别人"推开的式,用邻卡的位移替主卡把力量传导出去。三式之间同镜头也只能选一,见后文纪律。
两条边界必须划清:deck-deal-flyin 是"快但不重"的发牌入场,本卡是"入场本身就是冲击";impact-feedback 管的是落位之后的反馈(点击、确认、落定回弹),本卡管的是入场那一拍。落位后的余震归 impact-feedback,砸落那一拍归本卡。
砸的通用物理学:五条跨三式都成立的规则
三式细节不同,但共享同一套"砸"的物理学。先建立这五条规则,后面每式只是它们的特化。
1. 砸用 ease-in,落定用 ease-out。砸落段是"先慢后快"的加速曲线:Easing.in或 ease-in bezier(ScoreSlam 用bezier(0.55, 0.085, 0.68, 0.53),ImpactBurstKit 用bezier(0.55, 0.055, 0.675, 0.19))。观众对"砸"的感知来自速度在落点瞬间达到峰值;ease-out 是"先快后慢",落地时速度已经归零,读作"被轻轻放下",重量感直接消失。落定段的过冲回弹则反过来用 ease-out / spring。
2. 落点帧同帧起爆。环、碎片/尘点、火花、白闪、震屏全部锚定同一个落点帧常量(源码里IMPACT),一帧都不能错开。冲击波是"同时"发生的物理事件;任何错帧——哪怕 1 帧——都会被读作慢动作回放,冲击感碎掉。
3. "啪"读在过冲回弹那一瞬,不在位移结束。位移结束只是"到了",不是"砸到了"。落点感来自到位后冲出去一点再被拉回:ScoreSlam 砸到 scale 0.965 再弹簧回 1,KanadaPerspectiveSnap rotateY 冲到 +6° 再回 0。没有这个 2–4 帧的过冲,落点就是一堵墙,不是一记锤。
4. 扩散与消散解耦成两条时间轴。环/碎片的尺寸走快缓动(out 曲线),透明度走线性帧时间。二者共用一条快缓动时,前段跑得太快,元素扩到一半就淡没了。这是三式源码里都显式拆开的点(ringT对ringLin、shT对shLin、pT对pLin)。
5. 收尾真静止 ≥45f。全部余波(环、碎屑、震屏、包络)归零后,hold 段要逐帧全同、无逐帧噪声,时长按重拳给 ≥45f——是普通 hold 的两倍。余波没归零就进 hold,观众会一直觉得画面在"抖"。
ScoreSlam:重卡砸落(B,三式里最重)
① 适用画面:KPI/比分类重磅数字登场。卡从镜头前 2.75 倍大小加速砸到屏心,落点帧起爆环+碎片+火花+白闪+震屏,再压缩过冲回弹——锤子砸砧的分量。
② 帧时间轴(30fps,总 135f):
| 时间 | 通道 | 起止值 | 缓动 |
|---|---|---|---|
| 0–9 | 悬停蓄力:scale、上提、虚化 | scale 2.75→2.6,dy −40→−70px,dof blur 10px,卡片半透 | EASE.smooth |
| 9–15 | 砸落(主动作 6f) | scale 2.6→0.965,rotate 5°→0,dy→0,dof 10px→0 | EASE_IN_QUAD(ease-in quad) |
| 15 | 落点帧 IMPACT=15:白闪 / 冲击环 / 碎片 / 火花 / 震屏 同帧起爆 | 白闪 op 0.22/0.12/0.04 共 3f | — |
| 15–31 | 冲击环扩散+消散 | 直径 120→1500px,op 1→0,描边 9→1.5px | 扩散 snappy / 消散 linear |
| 15–35 | 碎片飞散 | 14 片,dist 280–580px,size 22–48px | 扩散 EASE.out / 消散 linear |
| 15–26 | 火花 | 16 道,dist 360–680px | bezier(0.1,0.9,0.2,1) / linear |
| 15–20 | 震屏 | 18px 指数衰减,竖向偏置 | amp = 18·exp(−0.85t) |
| 15–26 | 压缩过冲回弹 | scale 0.965→1 | spring(damping 14, stiffness 260) |
| 18 / 21 | 比分数字二次落地(主卡内容晚一拍) | scale 1.16→1,各晚 3f / 6f | spring(damping 13, stiffness 300) |
| 64–135 | hold(71f) | 推镜 1→1.02,余波全归零 | 逐帧全同 |
③ 源码里最关键的一处(扩散与消散解耦,2 行):
const ringT = ramp(frame, IMPACT, 16, EASE.snappy); // 扩散走快缓动 const ringLin = ramp(frame, IMPACT, 16, EASE.linear); // 消散走线性帧时间ringD吃ringT,ringOp吃ringLin。环得以在前半程保持可见、后半程才淡出,而不是扩到 1/4 就消失。
④ 最容易写坏的一处
- 症状:冲击环刚扩开就淡没,只闪 1–2 帧。
- 根因:扩散和消散用了同一条快缓动(都
EASE.snappy),前 20% 时间跑了 70% 进度,环还没扩开透明度已经掉到 0.3。 - 处方:给消散单独建一条
EASE.linear时间轴(ringLin),与ringT解耦;透明度在前 60% 保持 1→0.65,后 40% 才 0.65→0。
KanadaPerspectiveSnap:方向感甩入(A,最轻的一式)
① 适用画面:发布片首映卡、标题卡单卡潇洒亮相。卡贴着镜头从左侧甩入,透视从 260px 短焦收敛到 1800px 长焦,"啪"地弹平——轻快调性首选,暗场配集中线效果最好。
② 帧时间轴(30fps,总 120f):
| 时间 | 通道 | 起止值 | 缓动 |
|---|---|---|---|
| 2–20 | 甩入主通道(18f) | translateX −1350→0,rotate3d(0.5,1,0.1, 62°→0),scale 1.8→1,ty 轻微下弧 | 位移 EASE.snappy;姿态晚收曲线 |
| 2–18 | 透视收敛 | perspective 260→1800px | bezier(0.62, 0, 0.3, 1)(晚收 2f) |
| 15–20 | rotateY 过冲 | 0→+6° | EASE.swift |
| 20 | 落定帧:震屏 + 集中线起 | 震屏 8px 衰减 3f;48 道集中线 9f 向外退散 | 线性衰减 / EASE.out |
| 20 起 | 过冲回零 | rotateY +6°→0 | spring(damping 16, stiffness 260) |
| 23 起 | 卡内文字晚 3f 落定 | translateX −36→0,opacity 0.4→1,片名 skew −10°→0 | EASE.snappy |
| 56–120 | hold(64f) | 极缓推镜 1→1.025,背景大字慢漂 | 逐帧近全同 |
③ 源码里最关键的一处(位移与姿态走两条不同步的曲线):
const progAt = (f) => ramp(f, T0, SNAP - T0, EASE.snappy); // 位移:18f const TWIST = bezier(0.62, 0, 0.3, 1); const twistAt = (f) => ramp(f, T0, SNAP - T0 - 2, TWIST); // 姿态:晚收 2fprogAt管位移(18f 全跑完),twistAt管透视/扭转/缩放(只跑 16f 且用晚收曲线)。卡片是"扭着"冲到位、最后 2 帧才被拍平,鱼眼畸变因此读得到。
④ 最容易写坏的一处
- 症状:鱼眼感只剩第一帧,甩入读成普通滑入。
- 根因:姿态(透视/扭转/缩放)和位移用了同一条缓动、同时收,卡片到位前几帧就已经平了。
- 处方:姿态晚 2 帧收(
SNAP - T0 - 2+ 晚收 bezier);甩入总时长压在 18f,快于 12f 读不到鱼眼畸变,慢于 24f 泄劲。
ImpactBurstKit:波及传导(C,唯一会推开邻卡的式)
① 适用画面:三卡并排、中间空槽、左右有邻卡的页面。主卡砸进空槽,冲击波前沿扫到邻卡,把邻卡可见地推开再弹回——唯一用"波及"说清力量大小的式,前提是邻卡早已驻场。
② 帧时间轴(30fps,总 140f):
| 时间 | 通道 | 起止值 | 缓动 |
|---|---|---|---|
| 0–16 | 邻卡驻场 + 主卡悬停蓄力 | 邻卡稳定驻场 ≥15f;主卡 scale 1.55→1.6、y −110→−130px、dof 7px、槽虚线可见、接触影变实 | EASE.smooth |
| 16–22 | 砸落(主动作 6f) | scale 1.6→1,y −130→0 | EASE_IN_CUBIC(ease-in cubic) |
| 22 | 落点帧 IMPACT=22:压扁 / 冲击环 / 碎屑 / 震屏 同帧 | 压扁 3f 后弹簧回正(damping 12, stiffness 320) | — |
| 22–38 | 冲击环扩散+消散 | 半径 100→1100px,op 0.95→0,描边 10→1.5px,白色压力盘打底 | 扩散 snappy / 消散 linear |
| 22–44 | 碎屑飞散 | 18 片,dist 260–560px,size 12–28px,偏上半球 | EASE.out / linear |
| 22–26 | 震屏 | 8px 线性衰减 4f | 8·(1−t/4) |
| 25 | 邻卡被波及 | 外推 34px、rotate ±3.5°,HIT = IMPACT + 3 | 包络cos(0.5t)·e^(−t/8) |
| 25–65 | 邻卡阻尼弹回 | 包络 40f 硬钳归零 | 同上 |
| 65–140 | hold(75f) | 全静止,余波全归零 | 逐帧全同 |
③ 源码里最关键的一处(波及帧反解 + 硬钳包络):
// 环半径 100→1100/16f out-cubic,前沿到邻卡中心距 514px: // (514−100)/1000 = 0.414 → p≈0.163 → t≈2.6f → 取落点后 +3f const HIT = IMPACT + 3; const pushEnv = (f) => { const t = f - HIT; if (t < 0 || t >= 40) return 0; // 40f 硬钳 return Math.cos(t * 0.5) * Math.exp(-t / 8); }; // 阻尼包络波及触发帧不是拍的,是反解的:环半径 100→1100px、out-cubic 16f,邻卡中心距 514px,(514−100)/1000≈0.414,反解1−(1−p)³得p≈0.163、t≈2.6f,取整为落点后 3f。波前到邻卡比到主卡晚 3 帧,观众才分得清"被震到"和"一起晃"。
④ 最容易写坏的一处
- 症状:邻卡与主卡同帧动,整版一起抖。
- 根因:波及帧直接用了落点帧
IMPACT,没反解前沿到达时刻。 - 处方:邻卡触发 = 落点 + 3f(
HIT = IMPACT + 3),用pushEnv钳位;同帧动只有"一起晃",晚 3f 才读作"被波及"。 - 症状:邻卡还在入场就被推开,因果链读不出来。
- 根因:邻卡驻场不足 15f 就被冲击波扫到。
- 处方:邻卡先稳定驻场 ≥15f 再触发波及,源码把这段写成 0–16f 的显式 hold。
症状 → 处方速查
| 症状 | 根因 | 处方 |
|---|---|---|
| 环刚扩开就淡没 | 扩散与消散同用快缓动 | 消散改走EASE.linear独立时间轴(ringT对ringLin) |
| 砸下来像"被放下来" | 砸落段用了 ease-out | 砸落段改 ease-in(Easing.in/ ease-in bezier) |
| 甩入读成滑入、鱼眼感只有一帧 | 姿态与位移同曲线同时收 | 姿态晚收 2f + 晚收 bezier,甩入压在 18f(12–24f 之间) |
| 落点没有"啪" | 过冲段被裁掉或幅度 <3% | 加 2–4f 过冲:scale 砸到 0.97 / rotateY 冲到 +6° 再 spring 回零 |
| 冲击波读成慢动作 | 环/尘/震错帧起爆 | 全部锚定同一IMPACT常量,同帧起爆 |
| 震屏衰减不完一直抖 | 衰减没用指数、没钳零 | amp = 18·exp(−0.85t),超 5f 后恒 0,帧窗硬切 |
| 邻卡同帧动、整版抖 | 波及帧=落点帧 | 反解前沿到达帧,落点后 3f 触发(HIT = IMPACT + 3) |
| 邻卡还在入场就被推开 | 邻卡驻场 <15f | 邻卡先稳定驻场 ≥15f,再触发波及 |
| 环在亮底上消失 | 环色与底色对比不足 | 亮场:白色压力盘打底 + 饱和强调色环;暗场:白芯 + 色辉 |
| 邻卡推开后悬在半路 | 阻尼包络尾部不归零 | 包络加 40f 硬钳(t≥40 return 0) |
| 白闪读成过曝 | 帧长/峰值过高 | 白闪 ≤2f、峰值 ≤0.22、逐帧递减 |
红线与纪律
- 声音钉帧是硬规则。A 的"啪"、B/C 的音爆必须与落点帧同帧。钉帧一律写成
SHOTS.<shot>.from + offset(相对镜头起点的偏移),绝不写裸数字帧号——否则任何一个镜头改时长,后面全部 SFX 条目失效。详见 sound-design.md §4.5。无声版 B/C 的环+碎片+震屏会读作"卡在抽搐",声音不是装饰,是这组动效的承重墙。 - 同镜头震源唯一。三式都带震屏,震屏族(impact-feedback、montage-rhythm A/C)同一镜头只能有一个震源;三式之间同镜头也只能选一。两个震源叠加,观众分不清主震源,画面读作"故障抖动"。
- 全片砸入 ≤2 次。三次以上,每次都响等于都不响——重拳的价值来自稀缺。
- 邻卡驻场 ≥15f 才可被波及。邻卡自己还在入场时被推开,因果链读不出来。
- C 式与 domino-cascade 不可混用。montage-rhythm 的 domino 是连锁"接力入场"——邻居被撞后自己入场;C 式是"被波及晃一下回原位"——邻居早已在场。两者作用在同一组元素上会互相拆台。
落地前检查清单
- 真实素材回验:三个 demo 均在灰阶/占位素材上调校,参数是调校起点非实战定稿。换真实 KPI 卡、真实页面截图,重渲核对环/碎屑/震屏在真实对比度下是否可感。
- 亮底加压力盘:主场景是亮底(C 式瓷白页)时,冲击环必须配白色压力盘打底,环本身用饱和强调色;亮底白环、暗底黑环都几乎不可见。
- 震屏帧窗核对:逐帧导出落点前后 ±10f,确认衰减曲线归零后无残留、震源唯一。
- 邻卡驻场核查:邻卡入场结束到被波及之间 ≥15f;不足则先把邻卡入场往前挪。
- 真静止段比对:hold 段(≥45f)逐帧截两帧 diff,必须全同;余波、包络、白闪全部归零。
- 声音同帧抽查:落点帧与音爆帧号 diff = 0;所有钉帧均为
SHOTS.<shot>.from + offset形式,无裸帧号。
参考实现(同属demos/effects/slam-entrance-moves/,均基于 RemotionuseCurrentFrame+interpolate/ramp+Easing/bezier,确定性伪随机保证逐帧可复现):
- ScoreSlam.tsx
- KanadaPerspectiveSnap.tsx
- ImpactBurstKit.tsx
声音规范见 sound-design.md,组合进完整宣传片节奏参考 promo-energy-arc.md。
【免费下载链接】video-shotcraftAI video skill for Claude Code & Codex — cinematic, editable product promos with Remotion: 124 shot recipe cards, 150 motion previews, beat sync, sound design, Motion Workbench & JianYing (CapCut CN) export.项目地址: https://gitcode.com/gh_mirrors/vi/video-shotcraft
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考