Motion · Ritmo

Beat Cut Moves

Two ways to use hard cuts as percussion — accelerating hard-cut chains (halving intervals to close in) and paparazzi flash freezes (three white flashes, each a different crop).

1920×108016:930 fps4.39s · 132 quadrosremotionLicença: Apache-2.0#shotcraft#remotion#rhythm#transition

Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador

Variantes

beat-cut-accelerandobeat-cut-accelerando 递进硬切串
六视图按 16→12→8→6→4f 间隔减半全屏硬切,加速逼近,末刀戛然定格回主画面轻推收住
冲刺逼近感:预告片式蓄力、功能连打冲向结论
paparazzi-flashpaparazzi-flash 连闪定格
三次白闪各硬切同素材不同裁切(全景→卡片特写→数字特写),快门余韵沉降,第三闪停在数字收束
高光时刻仪式感:颁奖连拍、关键数字的加冕

When to use

Turning "the cut itself" into a drumbeat for highlight/sprint segments; version A is a trailer-style accelerating approach, version B is award-ceremony burst-shot ritual.

Intention

The library's existing rhythm vocabulary only governs rate "within one move" (speed-ramp varies speed inside a single take) and "how long to hold after settling" (the R-series breathing rules) — none of it governs how cut points between multiple shots are arranged. This card fills that gap: hard cuts skip all transitions, and the cut-interval pattern itself becomes the score — shrinking intervals read as an accelerating approach (A), three evenly spaced flashes read as a shutter burst (B). No conflict with the R-series: R governs the hold after settling, this card governs cut-point layout, and after a cut chain ends it must be followed by enough hold to "pay back" the breathing room. Note this is a deliberate exception — the rhythm-preference rule is a one-way "slow down," but the harder the cut chain, the longer the hold after it must be.

Duration & energy

  • Duration: A: full run ~4.3s (build 49f + five-cut chain + 35f freeze hold); B: full run ~4.3s (live footage 30f + three flashes + 60f hold).
  • Energy: High

Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)

意图

库内节奏词汇一直只管"一条运动内"的速率(speed-ramp 是同一条 take 里变速)和"落定后停多久"(R 系呼吸判例),没有词管多镜之间切点 怎么排。本卡补这一块:硬切不做任何过渡,切点间隔本身就是乐谱—— 间隔递减读作加速逼近(A),等距三闪读作快门连拍(B)。与 R 系不 冲突:R 管落定后的停,本卡管切点排布,切串结束必须接足量 hold 把 呼吸还回去。注意这是刻意的例外——节奏偏好判例是单向"放慢",硬切 串切得越狠,切完的 hold 要比平常更长。

两式选型

| 式 | 做法 | 适用 | |----|------|------| | A beat-cut-accelerando 递进硬切串 | 六视图按 16→12→8→6→4f 间隔减半全屏硬切,加速逼近,末刀戛然定格回主画面轻推收住 | 冲刺逼近感:预告片式蓄力、功能连打冲向结论 | | B paparazzi-flash 连闪定格 | 三次白闪各硬切同素材不同裁切(全景→卡片特写→数字特写),快门余韵沉降,第三闪停在数字收束 | 高光时刻仪式感:颁奖连拍、关键数字的加冕 |

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 切点帧 | 0/49/65/77/85/91/95(建立 49f,间隔 16→12→8→6→4f) | 间隔按"减半律"缩,等差缩读不出加速;末间隔 <4f 肉眼跟不上 | | A 视图池 | 6 个 = 两块 FakeDashboard × scale 1/1.8/2.6 + 对焦点,transformOrigin 钉焦点再平移居中 | 视图必须是同一产品的不同构图(语义连续),六个无关画面读作故障 | | A 切帧快门感 | 切帧 1f brightness 1.05 + 叠 6% 不透明度白层 | 只在切帧亮一下,是"咔"不是闪光灯;再厚就抢了 B 式的活 | | A 末段收住 | 定格回主画面 hold 35f,scale 1→1.06 ease-out 慢推 20f 后静止 | 戛然而止是这式的句号——末刀后不许再有任何切 | | B 三闪切点 | 30/52/70(间隔 22f→18f 渐紧),闪前 30f 慢推"活素材"衬托闪后定格 | 闪前必须是活的,全片死素材三闪读不出"定格" | | B 白闪层 | 每闪 opacity 0.95→0 走 4f ease-out 衰减;闪帧 ±2px seed 哈希全屏抖动 | 0.95 起步才有闪光灯的爆点;衰减 >6f 变柔光转场,失去快门感 | | B 快门余韵 | 切入视图 scale 1.03→1 走 6f ease-out 回落 + −16px 沉降归零 | 余韵是"被拍到晃了一下";去掉后三闪读作幻灯片翻页 | | B 三裁切 | 同一 Footage 换 scale/transformOrigin:1.0 全景 / 2.3 卡片 / 4.0 数字 | 层层逼近有叙事方向;乱序(特写→全景→特写)读作剪错 | | B 末段收束 | 第三闪后数字特写 hold 60f 静止 | 三闪是重击,hold 要给足平常两倍以上 |

已知坑

  • **demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,

首次实战须以真实素材回验**

  • 两式都是声音强依赖手法:切点必须钉在音轨拍点上,每切一声

tick(A)/ 快门咔嚓(B),无声版只剩画面乱跳;钉帧走 sound-design.md §4.5 相对 shot 起点,不钉绝对帧

  • A 式六视图要"同一产品的不同构图"而非六个无关画面——观众要在加速

中始终认得这是谁;跨产品/跨场景的素材别用这式

  • 切串全片 ≤1 次(P4 克制原则):这是全片最响的一记,第二次出现

第一次就白打了

  • 节奏偏好判例是单向"放慢",本卡是刻意例外——切完的 hold(A 35f /

B 60f)是底线不是建议,宁多勿少

  • 真硬切 = frame 落区间渲染对应视图,无任何过渡帧;手滑加 2f 交叉

淡化就不是硬切了,节拍感立即糊掉

参考实现

demos/rhythm/beat-cut-moves/ (BeatCutAccelerando.tsx / PaparazziFlash.tsx)

Reference implementation (Remotion)

  • demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx
  • demos/rhythm/beat-cut-moves/PaparazziFlash.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/beat-cut-moves/PaparazziFlash.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.

  1. Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
  2. Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
  3. 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/beat-cut-moves.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("beat-cut-moves")

Prompt

Use the video-shotcraft shot recipe "Beat Cut 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/beat-cut-moves.json
Raw TSX: https://gimgs.net/motion/beat-cut-moves/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.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).

JSON · Markdown · llms.txt

6,128 chars · Apache-2.0Abrir template bruto ↗
// ===== demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, G } from '../../_fixtures/Fixtures';

// beat-cut-accelerando:六个不同构图按 16→12→8→6→4 帧递减间隔全屏硬切,
// 加速逼近,最后一切戛然定格回主画面并 1→1.06 慢推收住。
// 真硬切:frame 落在哪个区间就渲染哪个视图,无任何过渡。

// 一个视图 = FakeDashboard 变体 + 缩放 + 对焦点(画面上要被推到屏幕中心的点)
type View = { variant: 'A' | 'B'; scale: number; cx: number; cy: number };

const VIEWS: View[] = [
  { variant: 'A', scale: 1, cx: 960, cy: 540 },    // v0 全景(建立)
  { variant: 'A', scale: 1.8, cx: 1070, cy: 576 }, // v1 卡片区
  { variant: 'A', scale: 2.6, cx: 600, cy: 340 },  // v2 单卡特写
  { variant: 'B', scale: 1, cx: 960, cy: 540 },    // v3 列表页全景
  { variant: 'B', scale: 1.9, cx: 1070, cy: 500 }, // v4 列表行区
  { variant: 'B', scale: 2.8, cx: 1070, cy: 290 }, // v5 单行特写
];

// 每段起始帧:建立 49f,然后间隔 16→12→8→6→4,末段(回主画面)hold 35f
// 0–48 v0 | 49–64 v1 | 65–76 v2 | 77–84 v3 | 85–90 v4 | 91–94 v5 | 95–129 定格
const CUTS = [0, 49, 65, 77, 85, 91, 95];
const FINAL = 95; // 最后一切:戛然定格回主画面

const ViewShot: React.FC<{ view: View; extraScale?: number }> = ({ view, extraScale = 1 }) => {
  const s = view.scale * extraScale;
  return (
    <div
      style={{
        width: 1920,
        height: 1080,
        transformOrigin: `${view.cx}px ${view.cy}px`,
        transform: `translate(${960 - view.cx}px, ${540 - view.cy}px) scale(${s})`,
      }}
    >
      <FakeDashboard variant={view.variant} />
    </div>
  );
};

export const BeatCutAccelerando: React.FC = () => {
  const frame = useCurrentFrame();

  // 当前落在哪个区间(末段 = 主画面 v0)
  let seg = 0;
  for (let i = 0; i < CUTS.length; i++) {
    if (frame >= CUTS[i]) seg = i;
  }
  const isFinal = seg === CUTS.length - 1;
  const view = isFinal ? VIEWS[0] : VIEWS[seg];

  // 末段慢推:scale 1 → 1.06,ease-out,推 22f 后静止(结尾静止 ≥15f)
  const push = isFinal
    ? interpolate(frame, [FINAL, FINAL + 20], [1, 1.06], {
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp',
        easing: Easing.out(Easing.cubic),
      })
    : 1;

  // 每次硬切的 1f 亮度跳变(约 +5%)模拟快门
  const isCutFrame = CUTS.some((c, i) => i > 0 && frame === c);
  const flash = isCutFrame ? 1.05 : 1;

  return (
    <AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
      <div style={{ position: 'absolute', inset: 0, filter: `brightness(${flash})` }}>
        <ViewShot view={view} extraScale={push} />
      </div>
      {/* 切帧再叠一层极薄白闪,保证肉眼可感 */}
      {isCutFrame && (
        <AbsoluteFill style={{ background: '#ffffff', opacity: 0.06 }} />
      )}
    </AbsoluteFill>
  );
};


// ===== demos/rhythm/beat-cut-moves/PaparazziFlash.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, TitleBlock, G } from '../../_fixtures/Fixtures';

// paparazzi flash 连闪定格:高光时刻三连白闪,每闪硬切同一素材的不同裁切
// (全景→卡片特写→数字特写),每闪切入画面带 1.03→1 回落 + 半格沉降像快门
// 余韵,白闪帧加微位移抖动模拟快门震,第三闪后停在数字特写收束。
const F1 = 30; // 第一闪(间隔 22f)
const F2 = 52; // 第二闪(间隔 18f)
const F3 = 70; // 第三闪,之后 hold 60f
const SETTLE = 6; // 切入回落时长
const DECAY = 4; // 白层衰减时长
const FLASHES = [F1, F2, F3];

// seed 正弦哈希,返回 -1..1(禁 Math.random)
const hash = (i: number) => {
  const s = Math.sin(i * 127.3) * 43758.5453;
  return (s - Math.floor(s)) * 2 - 1;
};

// 同一"素材":全景 dashboard,顶行中间卡片上叠一个大数字,供数字特写裁切
const Footage: React.FC = () => (
  <div style={{ position: 'absolute', width: 1920, height: 1080 }}>
    <FakeDashboard variant="A" />
    <div style={{ position: 'absolute', left: 1070, top: 350, transform: 'translate(-50%, -50%)' }}>
      <TitleBlock text="84,213" size={104} />
    </div>
  </div>
);

// 四个视图:闪前"活素材"慢推 + 三个定格裁切(全景 / 卡片 / 数字)
type View = { scale: number; origin: string };
const VIEW_WIDE: View = { scale: 1.0, origin: '960px 540px' };
const VIEW_CARD: View = { scale: 2.3, origin: '1070px 335px' };
const VIEW_DIGIT: View = { scale: 4.0, origin: '1070px 350px' };

export const PaparazziFlash: React.FC = () => {
  const frame = useCurrentFrame();

  // 当前处于哪一段:-1 = 闪前活素材段
  let seg = -1;
  for (let i = 0; i < FLASHES.length; i++) {
    if (frame >= FLASHES[i]) seg = i;
  }

  let scale: number;
  let origin: string;
  let settleScale = 1;
  let settleY = 0;

  if (seg === -1) {
    // 闪前:中景慢推(活的),衬托闪后定格的"死"
    origin = '900px 480px';
    scale = interpolate(frame, [0, F1], [1.16, 1.2], {
      extrapolateRight: 'clamp',
      easing: Easing.inOut(Easing.quad),
    });
  } else {
    const view = [VIEW_WIDE, VIEW_CARD, VIEW_DIGIT][seg];
    scale = view.scale;
    origin = view.origin;
    // 切入回落:1.03→1 的 6f 收敛 + 半格沉降(-16px 落回 0)像快门余韵
    const t = interpolate(frame, [FLASHES[seg], FLASHES[seg] + SETTLE], [1, 0], {
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp',
      easing: Easing.out(Easing.cubic),
    });
    settleScale = 1 + 0.03 * t;
    settleY = -16 * t;
  }

  // 白闪层:每个切点 0.95→0,4f 衰减,取各闪最大值
  let flash = 0;
  for (const f of FLASHES) {
    const o = interpolate(frame, [f, f + DECAY], [0.95, 0], {
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp',
      easing: Easing.out(Easing.quad),
    });
    if (frame >= f && frame < f + DECAY + 1) flash = Math.max(flash, o);
  }

  // 白闪帧全屏微位移抖动模拟快门震(seed 哈希,非随机)
  const inFlash = FLASHES.some((f) => frame >= f && frame < f + DECAY);
  const jx = inFlash ? 2 * hash(frame * 7 + 1) : 0;
  const jy = inFlash ? 2 * hash(frame * 13 + 5) : 0;

  return (
    <AbsoluteFill style={{ background: G.ink, overflow: 'hidden' }}>
      <div
        style={{
          position: 'absolute',
          inset: 0,
          transform: `translate(${jx}px, ${jy + settleY}px)`,
        }}
      >
        <div
          style={{
            position: 'absolute',
            width: 1920,
            height: 1080,
            transform: `scale(${scale * settleScale})`,
            transformOrigin: origin,
          }}
        >
          <Footage />
        </div>
      </div>
      {/* 白闪层 */}
      {flash > 0 && (
        <AbsoluteFill style={{ background: '#ffffff', opacity: flash }} />
      )}
    </AbsoluteFill>
  );
};


  1. Read the recipe
    Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
  2. Get the code
    tool: git
    git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install
  3. Register + render
    tool: remotion
    npx remotion render <CompositionId> out.mp4 # component: demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.tsx
  4. Or re-implement
    Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
paramtypedefaultdescription
—

Mais nesta categoria