{
  "slug": "crane-rise-reveal",
  "title": "Crane Rise Reveal",
  "tagline": "A crane-arm pull-up reveal — the opening shoves in close on one row of data, then the camera decelerates upward and pulls back as row after row floods in until the full dashboard fills the frame.",
  "description": "A crane-arm pull-up reveal — the opening shoves in close on one row of data, then the camera decelerates upward and pulls back as row after row floods in unti",
  "category": "intro",
  "tags": [
    "shotcraft",
    "remotion",
    "intro",
    "opening",
    "camera",
    "data"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 5.06,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/crane-rise-reveal/poster-a237b5741bfe.jpg",
    "video": "https://gimgs.net/motion/media/crane-rise-reveal/video-74b7f0e71753.mp4"
  },
  "variants": [
    {
      "key": "crane-rise-reveal",
      "label": "crane-rise-reveal",
      "description": "A crane-arm pull-up reveal — the opening shoves in close on one row of data, then the camera decelerates upward and pulls back as row after row floods in until the full dashboard fills the frame.",
      "use": "",
      "video": "https://gimgs.net/motion/media/crane-rise-reveal/video-74b7f0e71753.mp4",
      "poster": "https://gimgs.net/motion/media/crane-rise-reveal/poster-a237b5741bfe.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/crane-rise-reveal.html",
  "json_url": "https://gimgs.net/motion/crane-rise-reveal.json",
  "template_url": "https://gimgs.net/motion/crane-rise-reveal/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/crane-rise-reveal.md",
  "updated_at": "2026-09-24T07:39:52.507Z",
  "prompt": "## When to use\n\nA \"from detail to the whole picture\" establishing open; the inverse of drone-dive-landing (whole → single point dive).\n\n## Intention\n\nThe library's establishing-open shots already include drone-dive-landing — plunging from a god's-eye view into a hero close-up, \"whole → focus.\" This card mirrors it: \"focus → whole\" — first push in tight on one real row of data so viewers see \"what this is,\" then crane-lift upward and back as row after row of content floods in, until the whole product fills the frame — \"the row you just saw is only one cell of this entire wall.\" Suited to openers selling product scale/content richness; use only one direction per film if both cards appear.\n\n## Duration & energy\n\n- Duration: 5s (close-up hold 20f + rise 100f + full-frame hold 30f).\n- Energy: Medium-high (a continuous one-directional move, no impact beat).\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n开场定场库内已有 drone-dive-landing——从上帝视角砸进 hero 特写，\n\"全局→焦点\"。本卡是它的镜像：\"焦点→全局\"——先怼在一行真实数据上\n让观众看清\"这是什么\"，再升降臂式拉升后退，一排排内容涌入画面，\n最后整面产品铺满——\"你看到的这一行，只是这一面墙的一格\"。\n适合以产品体量/内容丰富度为卖点的开场；两卡同片只用一个方向。\n\n## 动效核心\n- transform-origin 固定左上，联动公式 `translate = 屏幕中心 − 对准点×scale`；\n  对准点沿 (底行中心)→(页面中心) 与 scale 3.2→1.0 **共用同一条\n  Easing.out(quad) 进度**——一条 p = 一台相机一次机动（同 C 式判例）\n- 减速曲线（先快后慢）是\"升降臂\"的手感：起步有劲、临顶缓停\n- 逐行脉冲：相机可见区上缘 `visTop = fy − 540/s` 首次越过某行顶边时，\n  该行深灰脉冲一拍（4f 起 18f 落，G.ink 叠层 opacity 0→0.22→0）——\n  \"涌入\"的可读性来源；触发帧逐帧解出，与运镜严格同步\n- 白底上\"提亮\"不可见，**加深才可感**（本批实渲判例：brightness 提亮\n  在白卡上等于没做，改深色叠层脉冲）\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 起点特写 | scale 3.2，对准最底行（一行占满画幅） | hold ≥20f 让\"这是一行数据\"可读再起飞 |\n| 拉升 | 100f Easing.out(quad)，scale 3.2→1.0 | 快于 80f 行脉冲追不上；慢于 130f 中段拖 |\n| 行脉冲 | 越线触发，0→0.22→0 深色叠层 22f | 脉冲跟不上相机（固定间隔触发）立刻穿帮 |\n| 收尾 | 满幅后真静止 ≥30f | R1；满幅帧即标准构图，不再补推 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 起点特写 = 一行内容被放大 3.2 倍盯着看，真实素材先过\n  审美准则 Q2 的高分辨率栅格化技法，低倍截图开场即糊\n- 快速拉升段可包 CameraMotionBlur（参数见 deck-deal-flyin 运动模糊行），\n  但只包前半程快速段——临顶慢速段包了会抹软文字（轮 #8 判例）\n- 与 drone-dive-landing 同片不并用（一升一降互为镜像，观众读作同一招）；\n  与 crane 语义最近的 tilt-reveal 未入库，别混称\n\n## 参考实现\ndemos/opening/crane-rise-reveal/\n（CraneRiseReveal.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/opening/crane-rise-reveal/CraneRiseReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/crane-rise-reveal/CraneRiseReveal.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/opening/crane-rise-reveal/CraneRiseReveal.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/opening/crane-rise-reveal/CraneRiseReveal.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/crane-rise-reveal/CraneRiseReveal.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": 152,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/crane-rise-reveal/CraneRiseReveal.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/opening/crane-rise-reveal/CraneRiseReveal.tsx =====\n// 升降臂拉升揭示（crane-rise-reveal）——crane shot。\n// 世界 = FakeDashboard(B) 五行列表。相机 transform-origin 左上，联动公式：\n// translate = 屏幕中心 - 对准点*scale（对准点始终落在屏幕中心）。\n// 帧 0–20 hold 在底行特写（scale 3.2，对准第 5 行图标+长条）；\n// 帧 20–120 scale 3.2→1 + 对准点 (520,958)→(960,540)，Easing.out(quad) 减速升起；\n// 视野上缘每越过一行顶边，该行深色脉冲一拍（4f 起 18f 落）读作\"涌入\"；帧 120–150 满幅真静止。\nimport React from 'react';\nimport { useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { G, FakeDashboard } from '../../_fixtures/Fixtures';\n\nconst HOLD = 20; // 开场特写 hold\nconst MOVE_END = 120; // 运镜结束，此后真静止\nconst ease = Easing.out(Easing.quad);\n\n// FakeDashboard(B) 行几何：侧栏 220 + 内容 padding 36，header 72，5 行 gap 20\nconst ROW_LEFT = 220 + 36;\nconst ROW_W = 1920 - ROW_LEFT - 36;\nconst ROW_H = (1080 - 72 - 72 - 4 * 20) / 5; // 171.2\nconst rowTop = (i: number) => 72 + 36 + i * (ROW_H + 20);\n\nconst F0 = { x: 520, y: rowTop(4) + ROW_H / 2 }; // 起点对准最底行（图标+标题条区域）\nconst F1 = { x: 960, y: 540 }; // 终点对准整页中心\nconst S0 = 3.2;\n\nconst camAt = (frame: number) => {\n  const p = Math.min(1, Math.max(0, (frame - HOLD) / (MOVE_END - HOLD)));\n  const e = ease(p);\n  const s = S0 + (1 - S0) * e;\n  const fx = F0.x + (F1.x - F0.x) * e;\n  const fy = F0.y + (F1.y - F0.y) * e;\n  return { s, tx: 960 - fx * s, ty: 540 - fy * s, visTop: fy - 540 / s };\n};\n\n// 每行脉冲触发帧：视野上缘首次越过该行顶边（底行开场已在画内 → 运动一起步即触发）\nconst triggers = Array.from({ length: 5 }, (_, i) => {\n  for (let f = HOLD; f <= MOVE_END; f++) {\n    if (camAt(f).visTop <= rowTop(i) + 1) return f;\n  }\n  return MOVE_END;\n});\n\nexport const CraneRiseReveal: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { s, tx, ty } = camAt(frame);\n\n  return (\n    <div\n      style={{\n        width: 1920,\n        height: 1080,\n        background: G.bg,\n        overflow: 'hidden',\n        position: 'relative',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          width: 1920,\n          height: 1080,\n          transformOrigin: '0 0',\n          transform: `translate(${tx}px, ${ty}px) scale(${s})`,\n        }}\n      >\n        <FakeDashboard variant=\"B\" />\n        {triggers.map((t, i) => {\n          const op = interpolate(frame, [t, t + 4, t + 22], [0, 0.22, 0], {\n            extrapolateLeft: 'clamp',\n            extrapolateRight: 'clamp',\n          });\n          if (op <= 0.001) return null;\n          return (\n            <div\n              key={i}\n              style={{\n                position: 'absolute',\n                left: ROW_LEFT,\n                top: rowTop(i),\n                width: ROW_W,\n                height: ROW_H,\n                borderRadius: 14,\n                background: G.ink,\n                opacity: op,\n              }}\n            />\n          );\n        })}\n      </div>\n    </div>\n  );\n};\n\n\n"
}