Motion · Transition

Card Flip Reveal

A feature card flips 180° on the Y axis, its front UI flashing a highlight band that moves with the angle right at the thinnest edge, revealing a large result number on the back, cards flipping staggered across a row.

1920×108016:930 fps4.89s · 147 framesremotionLicense: Apache-2.0#shotcraft#remotion#transition#data

Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player

When to use

The "feature → result" paired narrative: a row of feature cards each flip to reveal their own metric/result; an element-level transition.

Intention

The library's existing "flip" family already has wall-reveal-moves B (a whole wall flipping in waves, for entrances) and split-flap (character flip-cards, for text). This card is a semantic flip: a card's two faces form a cause-and-effect pair — the front is the feature interface, the back is the number it produces. The flip motion itself answers "so what?" — the standard grammar of Apple bento-grid segments. Flipping staggered by 10f across a row of three cards reads as "results reported one after another."

Duration & energy

  • Duration: Single-card flip 26f, three cards staggered by 10f, full run ~4.9s (with hold).
  • Energy: Medium-high

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

意图

库内"翻"字辈已有 wall-reveal-moves B(整墙波浪翻面,讲入场)和 split-flap(字符翻牌,讲文字)。本卡是语义翻面:一张卡的正反两面 是一对因果——正面是功能界面,背面是它带来的数字结论。翻面动作本身 就是"所以呢?"的回答,Apple bento 段落的标准语法。逐张错峰 10f 扫过 一排,三张卡三个数字节奏读作"成果连报"。

动效核心

  • perspective 1200px + transformStyle preserve-3d;双面各自

backfaceVisibility hidden,背面预转 rotateY(180°)

  • 翻转 18f Easing.bezier(0.55,0,0.3,1) 冲到 192°(12° 过冲,原案 8°

不可感已加码),再 8f Easing.out(poly(5)) 回落 180°

  • 侧棱高光:linear-gradient 位置随角度 35°→145° 从卡左外扫到右外,

强度 sin 包络在 90°(最薄瞬间)达峰 0.32 加深灰——白底用加深不用 提亮(判例);高光与角度联动才读作"光扫过转动的面"

  • 错峰 delay 10f/卡;全部翻完真静止 ≥40f

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 翻转时长 | 18f + 8f 回弹 | <14f 看不到侧棱瞬间;>28f 读作慢慢转 | | 过冲 | 192°→180°(12°) | 无过冲落定读作生硬停表 | | 高光峰值 | 0.32 加深灰 @90° | 峰值错开 90° 读作贴图错误 | | 错峰 | 10f/卡,≤4 卡 | 同时翻读作整墙翻面(那是 wall-reveal B 的活)| | 背面内容 | 大号数字(90px 级)+ 一行小标签 | 背面塞完整 UI 读不完——翻面后只有一拍注意力 | | 收尾 | 末卡落定真静止 ≥40f | R1 |

已知坑

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

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

  • 与 wall-reveal-moves B 式(网格波浪翻面)同技术根不同语义:那是

入场(背面灰卡→正面 UI),这是揭示(正面 UI→背面结论)——同片 两者都用时翻转方向要区分(入场 rotateX、揭示 rotateY)

  • 正反两面必须语义成对(界面→它的成果);翻出无关内容,翻面就

只是花哨的换图

  • 真实素材正面用截图时注意 backface 渲染开销,三卡以上分 Sequence
  • 声音候选位:每卡翻停帧一声轻"啪"(sound-design §4.5),三连啪

节奏感翻倍

参考实现

demos/transition/card-flip-reveal/ (CardFlipReveal.tsx)

Reference implementation (Remotion)

  • demos/transition/card-flip-reveal/CardFlipReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/card-flip-reveal/CardFlipReveal.tsx

The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.

Everything on this page is also machine-readable. Point your agent at the JSON, or add the gimgs MCP server and ask it to search motion templates.

  1. Fetch the JSON — it holds the shot recipe (timing, easing, parameter table, pitfalls) and the full TSX source.
  2. Clone the video-shotcraft repo; the component imports shared fixtures from demos/_fixtures and helpers from assets/lib.
  3. Register the component as a <Composition> in a Remotion project and render with `npx remotion render`, or re-implement the recipe in your own engine.
curl -s https://gimgs.net/motion/card-flip-reveal.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/card-flip-reveal/CardFlipReveal.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("card-flip-reveal")

Prompt

Use the video-shotcraft shot recipe "Card Flip Reveal" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/card-flip-reveal.json
Raw TSX: https://gimgs.net/motion/card-flip-reveal/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/transition/card-flip-reveal/CardFlipReveal.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 4.89s

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

4,179 chars · Apache-2.0Open raw template ↗
// ===== demos/transition/card-flip-reveal/CardFlipReveal.tsx =====
// 功能卡 3D 翻面揭示(card-flip-reveal)——Apple bento 翻转段。
// 横排 3 张占位卡逐张错峰沿 Y 轴翻 180°(perspective 1200px,双面结构
// backface-visibility hidden),背面白卡中央大号结论数字。翻转先加速后
// 弹性落定(末端过冲 +12° 回 180°);翻到侧棱(90°)附近闪过一道随角度
// 移动的加深灰高光带(白底用加深而非提亮)。
// 关键帧(卡 i 起点 = 18 + i*10,i = 0/1/2):
//   0–18 hold → 卡0: 18–36 翻至 192° → 36–44 回弹落 180° →
//   卡1: 28–46–54,卡2: 38–56–64 → 64–145 三卡全静止(81f ≥ 40f)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, Card, TitleBlock } from '../../_fixtures/Fixtures';

const CW = 440;
const CH = 300;
const GAP = 60;
const X0 = (1920 - (CW * 3 + GAP * 2)) / 2; // 240
const Y = (1080 - CH) / 2; // 390
const FLIP_START = 18;
const STAGGER = 10;
const FLIP_DUR = 18;
const SETTLE = 8;
const OVERSHOOT = 12; // 末端过冲角度(原案 8°,肉眼存疑加码到 12°)

const RESULTS = ['4.9×', '−38%', '99.9%'];

// 卡 i 在帧 f 的翻转角:0 → 192(先加速后减速)→ 180(弹性落定),帧确定
const angleAt = (f: number, i: number): number => {
  const s = FLIP_START + i * STAGGER;
  if (f < s + FLIP_DUR) {
    return interpolate(f, [s, s + FLIP_DUR], [0, 180 + OVERSHOOT], {
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp',
      easing: Easing.bezier(0.55, 0, 0.3, 1),
    });
  }
  return interpolate(f, [s + FLIP_DUR, s + FLIP_DUR + SETTLE], [180 + OVERSHOOT, 180], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.out(Easing.poly(5)),
  });
};

// 随角度移动的加深高光带:位置从卡左外扫到右外,强度在 90°(侧棱)达峰
const Sheen: React.FC<{ angle: number }> = ({ angle }) => {
  const pos = interpolate(angle, [35, 145], [-25, 115], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });
  const op = Math.max(0, 1 - Math.abs(angle - 90) / 55);
  if (op <= 0.004) return null;
  return (
    <div
      style={{
        position: 'absolute',
        inset: 0,
        borderRadius: 14,
        pointerEvents: 'none',
        opacity: op,
        background: `linear-gradient(105deg, rgba(0,0,0,0) ${pos - 14}%, rgba(0,0,0,0.32) ${pos}%, rgba(0,0,0,0) ${pos + 14}%)`,
      }}
    />
  );
};

const FlipCard: React.FC<{ i: number; frame: number }> = ({ i, frame }) => {
  const angle = angleAt(frame, i);
  return (
    <div
      style={{
        position: 'absolute',
        left: X0 + i * (CW + GAP),
        top: Y,
        width: CW,
        height: CH,
        perspective: 1200,
      }}
    >
      <div
        style={{
          width: '100%',
          height: '100%',
          position: 'relative',
          transformStyle: 'preserve-3d',
          transform: `rotateY(${angle}deg)`,
        }}
      >
        {/* 正面:占位卡 */}
        <div style={{ position: 'absolute', inset: 0, backfaceVisibility: 'hidden' }}>
          <Card w={CW} h={CH} seed={i + 1} />
          <Sheen angle={angle} />
        </div>
        {/* 背面:白卡 + 大号结论数字(预先转 180°,翻满后正读) */}
        <div
          style={{
            position: 'absolute',
            inset: 0,
            backfaceVisibility: 'hidden',
            transform: 'rotateY(180deg)',
            background: G.card,
            border: `2px solid ${G.border}`,
            borderRadius: 14,
            boxSizing: 'border-box',
            boxShadow: '0 2px 8px rgba(0,0,0,0.06)',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <span
            style={{
              fontFamily: 'Helvetica, Arial, sans-serif',
              fontWeight: 800,
              fontSize: 96,
              color: G.ink,
              letterSpacing: -2,
            }}
          >
            {RESULTS[i]}
          </span>
          <Sheen angle={angle} />
        </div>
      </div>
    </div>
  );
};

export const CardFlipReveal: React.FC = () => {
  const frame = useCurrentFrame();
  return (
    <div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', left: 120, top: 96 }}>
        <TitleBlock text="CARD FLIP REVEAL" size={54} />
      </div>
      {[0, 1, 2].map((i) => (
        <FlipCard key={i} i={i} frame={frame} />
      ))}
    </div>
  );
};


  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/transition/card-flip-reveal/CardFlipReveal.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
—

More in this category