{
  "slug": "card-flip-reveal",
  "title": "Card Flip Reveal",
  "tagline": "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.",
  "description": "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",
  "category": "transition",
  "tags": [
    "shotcraft",
    "remotion",
    "transition",
    "data"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.89,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/card-flip-reveal/poster-3081c6bf8979.jpg",
    "video": "https://gimgs.net/motion/media/card-flip-reveal/video-35cfcec9c16a.mp4"
  },
  "variants": [
    {
      "key": "card-flip-reveal",
      "label": "card-flip-reveal",
      "description": "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.",
      "use": "",
      "video": "https://gimgs.net/motion/media/card-flip-reveal/video-35cfcec9c16a.mp4",
      "poster": "https://gimgs.net/motion/media/card-flip-reveal/poster-3081c6bf8979.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/card-flip-reveal.html",
  "json_url": "https://gimgs.net/motion/card-flip-reveal.json",
  "template_url": "https://gimgs.net/motion/card-flip-reveal/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/card-flip-reveal.md",
  "updated_at": "2026-09-24T07:39:47.363Z",
  "prompt": "## When to use\n\nThe \"feature → result\" paired narrative: a row of feature cards each flip to reveal their own metric/result; an element-level transition.\n\n## Intention\n\nThe 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.\"\n\n## Duration & energy\n\n- Duration: Single-card flip 26f, three cards staggered by 10f, full run ~4.9s (with hold).\n- Energy: Medium-high\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n库内\"翻\"字辈已有 wall-reveal-moves B（整墙波浪翻面，讲入场）和\nsplit-flap（字符翻牌，讲文字）。本卡是**语义翻面**：一张卡的正反两面\n是一对因果——正面是功能界面，背面是它带来的数字结论。翻面动作本身\n就是\"所以呢？\"的回答，Apple bento 段落的标准语法。逐张错峰 10f 扫过\n一排，三张卡三个数字节奏读作\"成果连报\"。\n\n## 动效核心\n- perspective 1200px + transformStyle preserve-3d；双面各自\n  backfaceVisibility hidden，背面预转 rotateY(180°)\n- 翻转 18f Easing.bezier(0.55,0,0.3,1) 冲到 192°（12° 过冲，原案 8°\n  不可感已加码），再 8f Easing.out(poly(5)) 回落 180°\n- **侧棱高光**：linear-gradient 位置随角度 35°→145° 从卡左外扫到右外，\n  强度 sin 包络在 90°（最薄瞬间）达峰 0.32 加深灰——白底用加深不用\n  提亮（判例）；高光与角度联动才读作\"光扫过转动的面\"\n- 错峰 delay 10f/卡；全部翻完真静止 ≥40f\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 翻转时长 | 18f + 8f 回弹 | <14f 看不到侧棱瞬间；>28f 读作慢慢转 |\n| 过冲 | 192°→180°（12°） | 无过冲落定读作生硬停表 |\n| 高光峰值 | 0.32 加深灰 @90° | 峰值错开 90° 读作贴图错误 |\n| 错峰 | 10f/卡，≤4 卡 | 同时翻读作整墙翻面（那是 wall-reveal B 的活）|\n| 背面内容 | 大号数字（90px 级）+ 一行小标签 | 背面塞完整 UI 读不完——翻面后只有一拍注意力 |\n| 收尾 | 末卡落定真静止 ≥40f | R1 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 与 wall-reveal-moves B 式（网格波浪翻面）同技术根不同语义：那是\n  入场（背面灰卡→正面 UI），这是揭示（正面 UI→背面结论）——同片\n  两者都用时翻转方向要区分（入场 rotateX、揭示 rotateY）\n- 正反两面必须语义成对（界面→它的成果）；翻出无关内容，翻面就\n  只是花哨的换图\n- 真实素材正面用截图时注意 backface 渲染开销，三卡以上分 Sequence\n- 声音候选位：每卡翻停帧一声轻\"啪\"（sound-design §4.5），三连啪\n  节奏感翻倍\n\n## 参考实现\ndemos/transition/card-flip-reveal/\n（CardFlipReveal.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/transition/card-flip-reveal/CardFlipReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/card-flip-reveal/CardFlipReveal.tsx\n\nThe component imports shared fixtures from `demos/_fixtures` and helpers from `assets/lib` in the repo; SKILL.md there documents the render workflow.\n",
  "flow": [
    {
      "step": "Read the recipe",
      "note": "Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points."
    },
    {
      "step": "Get the code",
      "tool": "git",
      "cmd": "git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install"
    },
    {
      "step": "Register + render",
      "tool": "remotion",
      "cmd": "npx remotion render <CompositionId> out.mp4   # component: demos/transition/card-flip-reveal/CardFlipReveal.tsx"
    },
    {
      "step": "Or re-implement",
      "note": "Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing."
    }
  ],
  "files": [
    {
      "path": "demos/transition/card-flip-reveal/CardFlipReveal.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/card-flip-reveal/CardFlipReveal.tsx"
    }
  ],
  "contract": "React/Remotion composition (TSX) from the open-source video-shotcraft library (Apache-2.0). It is not a browser-playable template: clone the repo, register the component in a Remotion project and render with `npx remotion render`. The recipe (prompt) describes timing, easing and parameters so you can also re-implement the shot in any engine.",
  "render": {
    "engine": "remotion",
    "frames": 147,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/card-flip-reveal/CardFlipReveal.tsx"
    ],
    "note": "Clone https://github.com/Vincentwei1021/video-shotcraft (npm install). The component lives under demos/{category}/{name}/ and imports shared fixtures from demos/_fixtures and helpers from assets/lib. Copy it (with those imports) into a Remotion project, register a <Composition> with the template's width/height/fps/duration in Root.tsx, then `npx remotion render <CompositionId> out.mp4`. SKILL.md in the repo documents the full Claude Code / Codex workflow. The prompt below is the shot recipe (timing, easing, parameter table, pitfalls) — enough to re-implement the shot in HTML/WAAPI or another engine if you prefer."
  },
  "source": "// ===== demos/transition/card-flip-reveal/CardFlipReveal.tsx =====\n// 功能卡 3D 翻面揭示（card-flip-reveal）——Apple bento 翻转段。\n// 横排 3 张占位卡逐张错峰沿 Y 轴翻 180°（perspective 1200px，双面结构\n// backface-visibility hidden），背面白卡中央大号结论数字。翻转先加速后\n// 弹性落定（末端过冲 +12° 回 180°）；翻到侧棱（90°）附近闪过一道随角度\n// 移动的加深灰高光带（白底用加深而非提亮）。\n// 关键帧（卡 i 起点 = 18 + i*10，i = 0/1/2）：\n//   0–18 hold → 卡0: 18–36 翻至 192° → 36–44 回弹落 180° →\n//   卡1: 28–46–54，卡2: 38–56–64 → 64–145 三卡全静止（81f ≥ 40f）。\nimport React from 'react';\nimport { useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { G, Card, TitleBlock } from '../../_fixtures/Fixtures';\n\nconst CW = 440;\nconst CH = 300;\nconst GAP = 60;\nconst X0 = (1920 - (CW * 3 + GAP * 2)) / 2; // 240\nconst Y = (1080 - CH) / 2; // 390\nconst FLIP_START = 18;\nconst STAGGER = 10;\nconst FLIP_DUR = 18;\nconst SETTLE = 8;\nconst OVERSHOOT = 12; // 末端过冲角度（原案 8°，肉眼存疑加码到 12°）\n\nconst RESULTS = ['4.9×', '−38%', '99.9%'];\n\n// 卡 i 在帧 f 的翻转角：0 → 192（先加速后减速）→ 180（弹性落定），帧确定\nconst angleAt = (f: number, i: number): number => {\n  const s = FLIP_START + i * STAGGER;\n  if (f < s + FLIP_DUR) {\n    return interpolate(f, [s, s + FLIP_DUR], [0, 180 + OVERSHOOT], {\n      extrapolateLeft: 'clamp',\n      extrapolateRight: 'clamp',\n      easing: Easing.bezier(0.55, 0, 0.3, 1),\n    });\n  }\n  return interpolate(f, [s + FLIP_DUR, s + FLIP_DUR + SETTLE], [180 + OVERSHOOT, 180], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.poly(5)),\n  });\n};\n\n// 随角度移动的加深高光带：位置从卡左外扫到右外，强度在 90°（侧棱）达峰\nconst Sheen: React.FC<{ angle: number }> = ({ angle }) => {\n  const pos = interpolate(angle, [35, 145], [-25, 115], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n  const op = Math.max(0, 1 - Math.abs(angle - 90) / 55);\n  if (op <= 0.004) return null;\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        inset: 0,\n        borderRadius: 14,\n        pointerEvents: 'none',\n        opacity: op,\n        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}%)`,\n      }}\n    />\n  );\n};\n\nconst FlipCard: React.FC<{ i: number; frame: number }> = ({ i, frame }) => {\n  const angle = angleAt(frame, i);\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        left: X0 + i * (CW + GAP),\n        top: Y,\n        width: CW,\n        height: CH,\n        perspective: 1200,\n      }}\n    >\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          position: 'relative',\n          transformStyle: 'preserve-3d',\n          transform: `rotateY(${angle}deg)`,\n        }}\n      >\n        {/* 正面：占位卡 */}\n        <div style={{ position: 'absolute', inset: 0, backfaceVisibility: 'hidden' }}>\n          <Card w={CW} h={CH} seed={i + 1} />\n          <Sheen angle={angle} />\n        </div>\n        {/* 背面：白卡 + 大号结论数字（预先转 180°，翻满后正读） */}\n        <div\n          style={{\n            position: 'absolute',\n            inset: 0,\n            backfaceVisibility: 'hidden',\n            transform: 'rotateY(180deg)',\n            background: G.card,\n            border: `2px solid ${G.border}`,\n            borderRadius: 14,\n            boxSizing: 'border-box',\n            boxShadow: '0 2px 8px rgba(0,0,0,0.06)',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'center',\n          }}\n        >\n          <span\n            style={{\n              fontFamily: 'Helvetica, Arial, sans-serif',\n              fontWeight: 800,\n              fontSize: 96,\n              color: G.ink,\n              letterSpacing: -2,\n            }}\n          >\n            {RESULTS[i]}\n          </span>\n          <Sheen angle={angle} />\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport const CardFlipReveal: React.FC = () => {\n  const frame = useCurrentFrame();\n  return (\n    <div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>\n      <div style={{ position: 'absolute', left: 120, top: 96 }}>\n        <TitleBlock text=\"CARD FLIP REVEAL\" size={54} />\n      </div>\n      {[0, 1, 2].map((i) => (\n        <FlipCard key={i} i={i} frame={frame} />\n      ))}\n    </div>\n  );\n};\n\n\n"
}