Motion · Luz & ênfase
Slam entrance moves
Three high-energy slam entrances: a whip-in perspective stop, a heavy KPI drop, and a shockwave that hits neighboring cards.
Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador
Variantes
perspective 300→1500px + rotate3d 58°→0 + scale 1.7→1 甩入 18f,末 4f 过冲 +5° 弹平,长斜影收正
单卡潇洒亮相;调性偏动漫/轻快
卡从 scale 2.5/rotate 5° 六帧 Easing.in(quad) 砸落,落点帧圆环扩散+尘点飞散+震屏同帧
KPI/比分类重磅数字登场
B 三件套 + 冲击波前沿按半径-距离精算扫过邻卡帧,邻卡外推 30px + rotate ±3° 阻尼弹回
页面上已有邻居时的"全场震动"
When to use
A heavyweight entrance for a hero card/KPI card; impact-feedback handles the feedback after landing, this card handles the entrance itself being the impact
Intention
The library's entrance vocabulary has always topped out at deck-deal-flyin's "deal" — fast, but not heavy. This card adds the "slam" register, in three flavors: A is a sense of direction — the card whips in with exaggerated fisheye-level perspective right up to the lens and snaps flat with a "thwack," the flair of anime Kanada-style animation; B is a sense of weight — a KPI card drops in at 2.5x its on-screen size, with a ring pulse, dust burst, and screen shake all detonating on the same frame, the heft of a sports-scoreboard popup; C is a sense of transmission — on top of B's three-part combo, the shockwave's leading edge sweeps across the left and right neighboring cards and visibly pushes them away before they spring back, "this hit shook the neighbors" — one card, three ways to say how powerful something is. Choose by narrative need: A says "it's here," B says "it's heavy," C says "it shook the whole room."
Duration & energy
- Duration: Per style: action beat 6–22f + impact aftershock ~16f + hold ≥45f
- Energy: High
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
库内入场词汇的力度上限一直是 deck-deal-flyin 的"发牌"——快但不重。 本卡补"砸"这一档,三种口味:A 是方向感——卡片带鱼眼级夸张透视贴着 镜头甩进来,"啪"地弹平,动漫金田流的潇洒;B 是重量感——KPI 卡从 镜头前 2.5 倍大小砸落,圆环+尘点+震屏三件套同帧起爆,体育比分弹窗 的分量;C 是传导感——B 的三件套之上,冲击波前沿扫过左右邻卡时把 它们可见地推开再弹回,"这一下震到了邻居",一件事说清力量大小。 选型按叙事需要:告诉观众"它来了"用 A,"它很重"用 B,"它震动了 全场"用 C。
三式选型
| 式 | 做法 | 适用 | |----|------|------| | A kanada-perspective-snap | perspective 300→1500px + rotate3d 58°→0 + scale 1.7→1 甩入 18f,末 4f 过冲 +5° 弹平,长斜影收正 | 单卡潇洒亮相;调性偏动漫/轻快 | | B score-slam | 卡从 scale 2.5/rotate 5° 六帧 Easing.in(quad) 砸落,落点帧圆环扩散+尘点飞散+震屏同帧 | KPI/比分类重磅数字登场 | | C impact-burst-kit | B 三件套 + 冲击波前沿按半径-距离精算扫过邻卡帧,邻卡外推 30px + rotate ±3° 阻尼弹回 | 页面上已有邻居时的"全场震动" |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 甩入 | 18f Easing.out(cubic),translateX -700→0 与透视变形同跑;途中 blur 2px 落定即摘 | 快于 12f 读不到鱼眼畸变,慢于 24f 泄劲 | | A 落定 | rotateY 过冲 +5° 4f 回 0 + 6px 震屏 2f + 斜长影收正常投影 | "啪"在过冲回弹那一下,不在位移结束 | | B 砸落 | 6f Easing.in(quad)(加速砸,不是减速落) | ease-out 落地是"放下",ease-in 才是"砸" | | B 三件套 | 圆环 80→860px/14f + 尘点 18–30px 飞 160–320px + 震屏 18px 指数衰减 4f | 原案(720px/10px 尘/8px 震)全档不可感,实渲已 ×1.5–2 | | B 解耦命门 | 环/尘的"扩散"用 out-cubic、"消散"用线性帧时间 | 同用 out-cubic 前段跑太快,刚扩开就淡没 | | C 波及帧 | 前沿过邻卡中心距反解(460px 距 ≈ 落点后 3f);包络 cos(t/2)·e^(−t/8),40f 硬钳 0 | 邻卡与主卡同帧动读作整版抖动,晚 3f 才是"被波及" | | 收尾 | 全部余波归零后真静止 ≥45f | 重拳 hold 按平常两倍给(同 beat-cut B 判例) |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 三式都带震屏,震屏族(impact-feedback、montage-rhythm A/C)同一镜头
只能有一个震源;三式互相之间同镜头也只能选一
- 声音强依赖:A 的"啪"、B/C 的音爆必须同帧钉(sound-design §4.5),
无声版 B/C 的三件套读作"卡在抽搐"
- C 式邻卡必须先驻场稳定 ≥15f 再被波及——邻卡自己还在入场时被推开,
因果链读不出来
- 与 domino-cascade(montage-rhythm C 式)的区别要守住:domino 是
连锁"接力入场"(邻居被撞后自己入场),C 式是"被波及晃一下回原位" (邻居早已在场)——别混用在同一组元素上
- 全片砸入 ≤2 次;三次以上每次都响等于都不响(P4)
参考实现
demos/effects/slam-entrance-moves/ (ImpactBurstKit.tsx / KanadaPerspectiveSnap.tsx / ScoreSlam.tsx)
Reference implementation (Remotion)
- demos/effects/slam-entrance-moves/ImpactBurstKit.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx
- demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx
- demos/effects/slam-entrance-moves/ScoreSlam.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/slam-entrance-moves/ScoreSlam.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.
- Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
- Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
- Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/slam-entrance-moves.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/slam-entrance-moves/ImpactBurstKit.tsx, demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx, demos/effects/slam-entrance-moves/ScoreSlam.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("slam-entrance-moves")Prompt
Use the video-shotcraft shot recipe "Slam entrance moves" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/slam-entrance-moves.json Raw TSX: https://gimgs.net/motion/slam-entrance-moves/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/effects/slam-entrance-moves/ImpactBurstKit.tsx, demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx, demos/effects/slam-entrance-moves/ScoreSlam.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.39s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/effects/slam-entrance-moves/ImpactBurstKit.tsx =====
// 落点冲击套件(impact-burst-kit)——shockwave-ring + particle-burst 组合变异。
// 主卡砸落的落点帧同时触发:冲击波环扩散 + 14 粒子放射迸发 + 震屏,
// 且冲击波前沿扫到左右邻卡的那一帧(按半径-距离算准=落点后 3f)邻卡被
// 向外推开再 spring 弹回——"波及邻居"即两条词汇焊接成立的证据。
// 关键帧:0–14 两侧卡驻场、主卡 scale1.8/y-120 悬停 → 14–20 主卡 6f 加速砸落 →
// 20 落点帧:环 80→900px(14f out-cubic, op .75→0) + 14 粒子飞散 160–340px(22f)
// + 4f 震屏 6px 衰减 + 主卡 6f 压扁回弹 → 23 环前沿过邻卡(中心距 460px):
// 邻卡外推 30px + rotate ±3° 阻尼振荡弹回(40f 内钳到 0) → 63–140 全静止(77f)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, Card, TitleBlock } from '../../_fixtures/Fixtures';
// 伪随机(帧确定)
const h = (n: number): number => {
const s = Math.sin(n * 127.3) * 43758.5453;
return s - Math.floor(s);
};
const CW = 400;
const CH = 280;
const GAP = 60;
const X_L = (1920 - (CW * 3 + GAP * 2)) / 2; // 300
const Y = (1080 - CH) / 2; // 400
const CX = 960; // 主卡中心
const CY = Y + CH / 2; // 540
const IMPACT = 20; // 落点帧
// 冲击波:80→900px 14f out-cubic。前沿到达邻卡中心距 460px 的帧:
// (460-80)/820=0.463 → 1-(1-p)^3 → p≈0.19 → t≈2.6f → 取落点后 3f = 帧 23
const HIT_NEIGHBOR = IMPACT + 3;
// 14 个粒子:方/圆混合,角度带向上偏置,飞散 160–340px 减速缩小消失
const PARTICLES = Array.from({ length: 14 }).map((_, i) => ({
angle: -Math.PI / 2 + (h(i + 1) - 0.5) * Math.PI * 1.7, // 上半球为主
dist: 160 + h(i + 40) * 180,
size: 8 + h(i + 80) * 10,
square: i % 2 === 0,
}));
// 邻卡被推开的阻尼振荡包络:t=0 瞬时到 1,之后余弦衰减弹回,40f 后钳 0 保真静止
const pushEnv = (f: number): number => {
const t = f - HIT_NEIGHBOR;
if (t < 0 || t >= 40) return 0;
return Math.cos(t * 0.5) * Math.exp(-t / 8);
};
export const ImpactBurstKit: React.FC = () => {
const frame = useCurrentFrame();
// ── 主卡砸落:14–20 帧 scale 1.8→1 / y -120→0,加速进场
const dropP = interpolate(frame, [14, IMPACT], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.cubic),
});
const mainScale = interpolate(dropP, [0, 1], [1.8, 1]);
const mainDy = interpolate(dropP, [0, 1], [-120, 0]);
// 落点后 6f 压扁回弹(squash & stretch)
const sq = interpolate(frame, [IMPACT, IMPACT + 3, IMPACT + 6], [0, 1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.quad),
});
const mainSx = mainScale * (1 + 0.07 * sq);
const mainSy = mainScale * (1 - 0.1 * sq);
// ── ① 冲击波环:半径 80→900,14f,opacity 0.75→0
const ringP = interpolate(frame, [IMPACT, IMPACT + 14], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const ringR = interpolate(ringP, [0, 1], [80, 900]);
const ringOp = frame >= IMPACT && frame < IMPACT + 14 ? 0.75 * (1 - ringP) : 0;
// ── ② 粒子:落点起 22f,减速飞散 + 缩小 + 淡出
const pT = interpolate(frame, [IMPACT, IMPACT + 22], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const particlesAlive = frame >= IMPACT && frame < IMPACT + 22;
// ── ③ 邻卡外推 30px + rotate ±3°,阻尼弹回
const env = pushEnv(frame);
const pushX = 30 * env;
const pushRot = 3 * env;
// ── ④ 震屏:落点起 4f,6px 衰减(h 伪随机方向,帧确定)
let shakeX = 0;
let shakeY = 0;
if (frame >= IMPACT && frame < IMPACT + 4) {
const amp = 6 * (1 - (frame - IMPACT) / 4);
shakeX = (h(frame * 3.7) - 0.5) * 2 * amp;
shakeY = (h(frame * 7.1 + 13) - 0.5) * 2 * amp;
}
return (
<div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>
<div style={{ position: 'absolute', inset: 0, transform: `translate(${shakeX}px, ${shakeY}px)` }}>
<div style={{ position: 'absolute', left: 120, top: 96 }}>
<TitleBlock text="IMPACT BURST KIT" size={54} />
</div>
{/* 左邻卡:被冲击波推开再弹回 */}
<div
style={{
position: 'absolute',
left: X_L,
top: Y,
transform: `translateX(${-pushX}px) rotate(${-pushRot}deg)`,
}}
>
<Card w={CW} h={CH} seed={2} />
</div>
{/* 右邻卡 */}
<div
style={{
position: 'absolute',
left: X_L + (CW + GAP) * 2,
top: Y,
transform: `translateX(${pushX}px) rotate(${pushRot}deg)`,
}}
>
<Card w={CW} h={CH} seed={4} />
</div>
{/* 主卡:砸落 + 落点压扁回弹 */}
<div
style={{
position: 'absolute',
left: X_L + CW + GAP,
top: Y + mainDy,
transform: `scale(${mainSx}, ${mainSy})`,
transformOrigin: '50% 100%',
}}
>
<Card w={CW} h={CH} seed={7} style={{ boxShadow: '0 6px 18px rgba(0,0,0,0.16)' }} />
</div>
{/* ② 粒子迸发(画在卡之上) */}
{particlesAlive &&
PARTICLES.map((p, i) => {
const px = CX + Math.cos(p.angle) * p.dist * pT;
const py = CY + Math.sin(p.angle) * p.dist * pT;
const s = p.size * (1 - pT);
if (s < 0.5) return null;
return (
<div
key={i}
style={{
position: 'absolute',
left: px - s / 2,
top: py - s / 2,
width: s,
height: s,
background: G.ink,
borderRadius: p.square ? 2 : '50%',
opacity: 1 - pT * pT,
}}
/>
);
})}
{/* ① 冲击波环(最上层扫过邻卡) */}
{ringOp > 0 && (
<div
style={{
position: 'absolute',
left: CX - ringR,
top: CY - ringR,
width: ringR * 2,
height: ringR * 2,
border: `3px solid ${G.ink}`,
borderRadius: '50%',
opacity: ringOp,
boxSizing: 'border-box',
}}
/>
)}
</div>
</div>
);
};
// ===== demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx =====
// 金田透视急停(kanada-perspective-snap)——金田伊功式夸张透视入场。
// 一张卡片以鱼眼级夸张透视姿态高速甩入画面中心:容器 perspective 300→1500px
// (短焦→长焦,透视畸变随之收敛),卡片 rotate3d(0.5,1,0.1) 58°→0 +
// scale 1.7→1 + translateX -700→0,近角冲出画面感。落定瞬间"啪"地弹平:
// rotateY 过冲 +5° 再 4f 回 0;同时 6px 震屏 2f 衰减,拉长斜影收为正常投影。
// 甩入期整卡叠 blur(2px) 增速感,落定即摘(保证收尾逐帧全同)。
// 关键帧:0–18 透视甩入(out cubic)→ 14–18 rotateY 过冲至 +5° →
// 18–22 回弹归 0 + 震屏衰减 + 阴影收正 → 22–130 全静止(≥45f)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, Card, TitleBlock } from '../../_fixtures/Fixtures';
// 确定性伪随机(震屏抖动用)
const h = (n: number): number => {
const s = Math.sin(n * 127.3) * 43758.5453;
return s - Math.floor(s);
};
const CLAMP = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const;
const CARD_W = 520;
const CARD_H = 340;
const CX = (1920 - CARD_W) / 2; // 700
const CY = (1080 - CARD_H) / 2; // 370
export const KanadaPerspectiveSnap: React.FC = () => {
const frame = useCurrentFrame();
// 0–18f 甩入主通道(out cubic:先猛后缓,急停感)
const p = interpolate(frame, [0, 18], [0, 1], { ...CLAMP, easing: Easing.out(Easing.cubic) });
const persp = interpolate(p, [0, 1], [300, 1500]); // 短焦鱼眼→长焦收平
const angle3d = interpolate(p, [0, 1], [58, 0]); // rotate3d(0.5,1,0.1)
const scale = interpolate(p, [0, 1], [1.7, 1]);
const tx = interpolate(p, [0, 1], [-700, 0]);
// rotateY 过冲通道:14–18f 冲到 +5°,18–22f "啪"地回 0
const rotY =
frame < 18
? interpolate(frame, [14, 18], [0, 5], CLAMP)
: interpolate(frame, [18, 22], [5, 0], { ...CLAMP, easing: Easing.out(Easing.cubic) });
// 落定震屏:18f 起 6px,2f 内衰减到 0(21f 后恒为 0,保证真静止)
const shakeAmp = frame >= 18 ? interpolate(frame, [18, 21], [6, 0], CLAMP) : 0;
const shakeX = shakeAmp * (h(frame * 7 + 1) * 2 - 1);
const shakeY = shakeAmp * (h(frame * 13 + 2) * 2 - 1);
// 阴影:飞行期拉长斜影(大偏移大模糊)→ 落定收为正常投影(18–22f 收拢)
const shOff = frame < 18 ? interpolate(p, [0, 1], [1, 0.35]) : interpolate(frame, [18, 22], [0.35, 0], { ...CLAMP, easing: Easing.out(Easing.quad) });
const shX = interpolate(shOff, [0, 1], [0, 70]);
const shY = interpolate(shOff, [0, 1], [8, 52]);
const shBlur = interpolate(shOff, [0, 1], [14, 36]);
const shAlpha = interpolate(shOff, [0, 1], [0.16, 0.3]);
// 甩速 blur:透视扭曲期叠 2px,落定(18f)即摘,收尾无逐帧滤镜
const blur = frame < 18 ? 2 : 0;
return (
<div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>
<div style={{ position: 'absolute', left: shakeX, top: shakeY, width: 1920, height: 1080 }}>
<div style={{ position: 'absolute', left: 120, top: 96 }}>
<TitleBlock text="KANADA PERSPECTIVE SNAP" size={54} />
</div>
{/* 落点槽位(虚线框,给"甩到哪"一个参照) */}
<div
style={{
position: 'absolute',
left: CX - 20,
top: CY - 20,
width: CARD_W + 40,
height: CARD_H + 40,
border: `3px dashed ${G.bar}`,
borderRadius: 22,
boxSizing: 'border-box',
}}
/>
{/* 透视容器:perspective 随落定从鱼眼收敛到长焦 */}
<div style={{ position: 'absolute', left: CX, top: CY, perspective: `${persp}px`, perspectiveOrigin: '30% 50%' }}>
<div
style={{
transform: `translateX(${tx}px) scale(${scale}) rotate3d(0.5, 1, 0.1, ${angle3d}deg) rotateY(${rotY}deg)`,
transformOrigin: '20% 50%',
filter: `drop-shadow(${shX}px ${shY}px ${shBlur}px rgba(0,0,0,${shAlpha}))${blur ? ` blur(${blur}px)` : ''}`,
}}
>
<Card w={CARD_W} h={CARD_H} seed={3} />
</div>
</div>
</div>
</div>
);
};
// ===== demos/effects/slam-entrance-moves/ScoreSlam.tsx =====
// 比分砸落入场(score-slam)——ESPN 比分弹窗 slam。
// KPI 卡从 scale 2.5 / rotate 5° / y-80 高空加速砸落屏心,落点帧同时触发
// 三件套:冲击波描边圆环扩散消散、8 个尘点小方块 seeded 抛物线飞散、
// 整画面震屏指数衰减。砸落带 3% 压缩过冲回弹落定。
// 关键帧:0–8 环境 hold → 8–14 砸落(Easing.in(quad),scale 2.5→0.97,
// rotate 5→0,y -80→0)→ 14 落点帧触发环/尘/震 → 14–22 过冲回弹 0.97→1 →
// 14–28 圆环 60→720px 直径 op 0.7→0 → 14–30 尘点飞散 → 14–19 震屏 18px 衰减
// → 30–135 全静止(≥45f,无逐帧噪声层)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';
// 库规伪随机
const h = (n: number) => {
const s = Math.sin(n * 127.3) * 43758.5453;
return s - Math.floor(s);
};
const IMPACT = 14; // 落点帧
const CX = 960;
const CY = 540;
const CARD_W = 460;
const CARD_H = 260;
export const ScoreSlam: React.FC = () => {
const frame = useCurrentFrame();
// —— 卡片砸落:8–14 加速下砸到 0.97(压缩过冲),14–22 回弹到 1 ——
const slamScale =
frame < IMPACT
? interpolate(frame, [8, IMPACT], [2.5, 0.97], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.quad),
})
: interpolate(frame, [IMPACT, 22], [0.97, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const slamRot = interpolate(frame, [8, IMPACT], [5, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.quad),
});
const slamY = interpolate(frame, [8, IMPACT], [-80, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.quad),
});
const cardOp = interpolate(frame, [8, 11], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// —— 震屏:落点帧起 5f,18px 指数衰减,seeded 方向,19f 后严格归零 ——
let shakeX = 0;
let shakeY = 0;
if (frame >= IMPACT && frame < IMPACT + 5) {
const t = frame - IMPACT;
const amp = 18 * Math.exp(-t * 0.9);
shakeX = amp * (h(frame * 7 + 1) * 2 - 1);
shakeY = amp * (h(frame * 13 + 2) * 2 - 1);
}
// —— 冲击波圆环:14f 内直径 60→720,opacity 0.7→0 ——
const ringT = interpolate(frame, [IMPACT, IMPACT + 14], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const ringD = interpolate(ringT, [0, 1], [60, 860]);
// 不透明度走线性帧时间(与扩散的 out-cubic 解耦),扩到最大前都保持可见
const ringTLin = interpolate(frame, [IMPACT, IMPACT + 14], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const ringOp = interpolate(ringTLin, [0, 0.65, 1], [0.75, 0.55, 0]);
const ringOn = frame >= IMPACT && frame < IMPACT + 14;
// —— 8 个尘点:seeded 角度抛物线飞散 120–260px,16f 减速缩小消失 ——
const dustT = interpolate(frame, [IMPACT, IMPACT + 16], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const dustTLin = interpolate(frame, [IMPACT, IMPACT + 16], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const dustOn = frame >= IMPACT && frame < IMPACT + 16;
return (
<div
style={{
width: 1920,
height: 1080,
background: G.bg,
position: 'relative',
overflow: 'hidden',
}}
>
{/* 震屏容器:整画面一起抖 */}
<div
style={{
position: 'absolute',
inset: 0,
transform: `translate(${shakeX}px, ${shakeY}px)`,
}}
>
{/* 虚化 dashboard 环境底 */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
filter: 'blur(6px)',
transform: 'scale(1.05)',
transformOrigin: '50% 50%',
}}
>
<FakeDashboard variant="B" />
</div>
<div style={{ position: 'absolute', left: 120, top: 96 }}>
<TitleBlock text="SCORE SLAM" size={54} />
</div>
{/* ② 尘点飞散 */}
{dustOn &&
Array.from({ length: 8 }).map((_, i) => {
const ang = (i / 8) * Math.PI * 2 + (h(i + 3) - 0.5) * 0.7;
const dist = 160 + h(i + 11) * 160; // 160–320px
const size = 18 + h(i + 23) * 12; // 18–30px
const dx = Math.cos(ang) * dist * dustT;
// 抛物线:先随角度飞出,再叠加重力下坠
const dy = Math.sin(ang) * dist * dustT + 90 * dustT * dustT;
const s = size * (1 - 0.75 * dustTLin);
const op = interpolate(dustTLin, [0, 0.75, 1], [0.9, 0.7, 0]);
return (
<div
key={i}
style={{
position: 'absolute',
left: CX + dx - s / 2,
top: CY + CARD_H / 2 - 20 + dy - s / 2,
width: s,
height: s,
background: G.ink,
opacity: op,
borderRadius: 2,
}}
/>
);
})}
{/* ① 冲击波圆环 */}
{ringOn && (
<div
style={{
position: 'absolute',
left: CX - ringD / 2,
top: CY - ringD / 2,
width: ringD,
height: ringD,
borderRadius: '50%',
border: `6px solid ${G.ink}`,
opacity: ringOp,
boxSizing: 'border-box',
}}
/>
)}
{/* KPI 卡本体 */}
{frame >= 8 && (
<div
style={{
position: 'absolute',
left: CX - CARD_W / 2,
top: CY - CARD_H / 2,
width: CARD_W,
height: CARD_H,
background: G.card,
border: `3px solid ${G.border}`,
borderRadius: 18,
boxShadow: '0 10px 30px rgba(0,0,0,0.18)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
opacity: cardOp,
transform: `translateY(${slamY}px) rotate(${slamRot}deg) scale(${slamScale})`,
transformOrigin: '50% 50%',
}}
>
<div
style={{
fontFamily: 'Helvetica, Arial, sans-serif',
fontWeight: 800,
fontSize: 110,
color: G.ink,
letterSpacing: -3,
lineHeight: 1,
}}
>
+247%
</div>
<div
style={{
fontFamily: 'Helvetica, Arial, sans-serif',
fontWeight: 600,
fontSize: 26,
color: G.mid,
letterSpacing: 4,
}}
>
QUARTERLY GROWTH
</div>
</div>
)}
</div>
</div>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/effects/slam-entrance-moves/ImpactBurstKit.tsx, demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx, demos/effects/slam-entrance-moves/ScoreSlam.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Mais nesta categoria

Dark-stage spotlight reveals
Three dark-stage spotlight reveals — A wake-sleep sweep (light in = bright, light out = dark), B edge-hugging glow pan (violet light seeps along UI edges + steady rightward sweep), C corner reveal (radial light expands steadily from a corner to light the whole screen); light itself narrates in the dark.

Scanline assemble fly-in
A scan line sweeps down an empty grid, flying each region's component in from off-screen — finishing as the scan ends.

Scanline annotate focus
A bright scan line sweeps down the page, framing each element it passes and labeling it with monospace annotations.

Scan bracket sweep
A skeleton document pops in, viewfinder brackets frame it, and a scan line sweeps it 5 times while it stays completely still.