{
  "slug": "bottom-push-stack-wipe",
  "title": "Bottom Push Stack Wipe",
  "tagline": "A chapter change by pushing from the bottom — the new scene, background and all, pushes up from the bottom edge, physically shoving the old scene off-screen; each chapter gets its own saturated background color.",
  "description": "A chapter change by pushing from the bottom — the new scene, background and all, pushes up from the bottom edge, physically shoving the old scene off-screen; ",
  "category": "transition",
  "tags": [
    "shotcraft",
    "remotion",
    "transition"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.72,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/bottom-push-stack-wipe/poster-8c154755870d.jpg",
    "video": "https://gimgs.net/motion/media/bottom-push-stack-wipe/video-595cc42c3254.mp4"
  },
  "variants": [
    {
      "key": "bottom-push-stack-wipe",
      "label": "bottom-push-stack-wipe",
      "description": "A chapter change by pushing from the bottom — the new scene, background and all, pushes up from the bottom edge, physically shoving the old scene off-screen; each chapter gets its own saturated background color.",
      "use": "",
      "video": "https://gimgs.net/motion/media/bottom-push-stack-wipe/video-595cc42c3254.mp4",
      "poster": "https://gimgs.net/motion/media/bottom-push-stack-wipe/poster-8c154755870d.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/bottom-push-stack-wipe.html",
  "json_url": "https://gimgs.net/motion/bottom-push-stack-wipe.json",
  "template_url": "https://gimgs.net/motion/bottom-push-stack-wipe/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/bottom-push-stack-wipe.md",
  "updated_at": "2026-09-24T07:39:45.535Z",
  "prompt": "## When to use\n\nThe chapter-change skeleton for multi-chapter product films (one selling point, one background color per chapter); segments needing a \"page-turn rhythm\" throughout.\n\n## Intention\n\nwipe-transitions is a boundary sweeping across while old and new pages stay still; page-turn is a cube flip. This card is a third kind of physicality: the new chapter shoves the old one out — two screens in rigid contact, moving at the same speed in the same direction, like a vending machine stacking product. The key is that the entire background, all in one piece, moves: the background + window card + decoration strip are fused into one solid slab pushed in from the bottom edge, so viewers read \"a new world\" rather than \"a new card.\" Heavy ease-out (fast then a hard stop) gives the push a satisfying thud on landing; a 40px shadow seam at the top edge is the evidence of physical contact between the two screens. With three chapters pushed in a row at full saturation, the chapter change itself becomes the film's metronome — the original slack film builds its entire skeleton on this one move.\n\n## Duration & energy\n\n- Duration: One push-in: 30f + ~1s in-chapter hold; demo triple push: 140f (~4.7s).\n- Energy: Medium\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\nwipe-transitions 是一条边界扫过、新旧页都不动；page-turn 是立方体\n翻转。本卡是第三种物性：**新章把旧章顶出去**——两屏刚性接触、同速\n同向位移，像自动售货机压栈。命门是\"连底色整屏走\"：底色+窗口卡+装饰\n条焊成一块整料从底边推入，观众读到\"换了个世界\"而不是\"换了张卡\"。\n重 ease-out（快进慢停）给推入一个\"哐\"的落位感；上缘 40px 接缝阴影\n是两屏物理接触的证据。连推三章且底色饱和度拉满，换章本身就成了全片\n的节拍器——slack 原片全片就靠这一招做骨架。\n\n## 动效核心\n- 每章一个整屏层：`translateY` 从 +1080 推到 0，旧章同步被顶到\n  −1080——两层位移严格同步同缓动，任何速差都读作\"滑动穿模\"\n- 推入缓动 = 重 ease-out `cubic-bezier(0.12, 0.9, 0.2, 1)`（快进\n  慢停），30f 完成；第 i 章的 y = (1−自己推入进度)×H − 下一章推入\n  进度×H，一条式子管进也管出\n- 新章上缘带 40px 渐变接缝阴影（黑 0.30→透明，压在旧章上），落定\n  后归零——\"顶出\"的物理接触感全靠它\n- 三章底色绿 #2bac76 / 蓝 #36c5f0 / 粉 #e01e5a（demo 取 slack 系\n  饱和色）；每章中央钉一张灰阶窗口卡 + 白色半透明装饰条\n- 推入间隔 hold ~1s（demo 18/55/92f 起推）；出画的层及时卸载\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 推入时长 | 30f，重 ease-out | <20f 读作闪切；线性匀速读作卷帘门，\"哐\"感全无 |\n| 章间 hold | ~37f（1.2s 左右） | 章内是内容表演区，<25f 观众没看清就被顶走 |\n| 接缝阴影 | 上缘 40px，黑 0.30 渐变 | 去掉后两屏像叠影滑动；>80px 读作新章自带黑边 |\n| 底色 | 每章一种饱和色，相邻章色相拉开 | 相邻章同色系则\"换章\"读作\"微调\"；灰阶章只能当第 0 章 |\n| 内容钉死 | 窗口卡/装饰条用章内绝对坐标 | 内容若相对视口定位就会\"漂\"在推入动画上，穿帮 |\n| 连推次数 | 2–4 章 | 1 次不成节奏；>4 次观众开始数底色，疲劳 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 与 wipe-transitions 分工：那卡新旧页都不动、一条几何边界扫过交接\n  （中性、哪儿都能用）；本卡两页都在动、刚性顶出（有物性、自带章节\n  节奏）——单次接缝用那卡，全片骨架用本卡\n- 与 shot-transitions 六式同层选型：本卡能量中档、方向固定向上，\n  需要能量落差大的接缝（如进 outro）让位给流白/黑场\n- 方向统一是骨架感的来源：全片所有章都从底边推，中途换成左推/顶推\n  节拍器就散了——要变方向就别当骨架用\n- 推入进行中两章同屏，都是满屏层——章内动画（卡片入场等）避开推入\n  的 30f 窗口，不然两章都在动，视线没有锚点\n- 实战底色用品牌色板的饱和档；底色饱和度不足时窗口卡\"钉在色底上\"\n  的从属关系读不出来，会退化成\"卡片各自换背景\"\n\n## 参考实现\ndemos/transition/bottom-push-stack-wipe/\n（BottomPushStackWipe.tsx）\n原片出处：slack-promo 22–27s\n\n## Reference implementation (Remotion)\n\n- demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.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/bottom-push-stack-wipe/BottomPushStackWipe.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/bottom-push-stack-wipe/BottomPushStackWipe.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.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": 142,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.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/bottom-push-stack-wipe/BottomPushStackWipe.tsx =====\n// bottom-push-stack-wipe —— slack-promo 22–27s\n// 换章手法：新场景连底色整屏从底边向上推入，把旧场景顶出画外，\n// 连推三章（三种饱和底色，每章中央钉一张灰阶窗口卡随底色走）。\n// 推入用重 ease-out（快进慢停）。\nimport React from 'react';\nimport { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { Card, G } from '../../_fixtures/Fixtures';\n\nconst H = 1080;\n\n// 章节定义：底色 + 卡片种子。第 0 章是起始灰阶场景。\nconst CHAPTERS = [\n  { color: G.bg, label: 0 },\n  { color: '#2bac76', label: 1 }, // 绿\n  { color: '#36c5f0', label: 2 }, // 蓝\n  { color: '#e01e5a', label: 3 }, // 粉红\n];\n\n// 每章推入的起始帧；约 32 帧完成一次推入，随后 hold\nconst PUSH_STARTS = [18, 55, 92];\nconst PUSH_DUR = 30;\n\nconst heavyEaseOut = Easing.bezier(0.12, 0.9, 0.2, 1); // 快进慢停\n\nconst ChapterScene: React.FC<{ chapter: number }> = ({ chapter }) => {\n  const c = CHAPTERS[chapter];\n  return (\n    <AbsoluteFill style={{ background: c.color, justifyContent: 'center', alignItems: 'center' }}>\n      {/* 底色上的淡装饰条，让\"底色也在动\"更可读 */}\n      {chapter > 0 && (\n        <>\n          <div style={{ position: 'absolute', top: 90, left: 120, width: 500, height: 26, borderRadius: 13, background: 'rgba(255,255,255,0.28)' }} />\n          <div style={{ position: 'absolute', bottom: 110, right: 140, width: 340, height: 26, borderRadius: 13, background: 'rgba(255,255,255,0.22)' }} />\n          <div style={{ position: 'absolute', top: 160, right: 220, width: 90, height: 90, borderRadius: '50%', background: 'rgba(255,255,255,0.18)' }} />\n        </>\n      )}\n      {/* 中央钉住的灰阶窗口卡 */}\n      <div style={{ boxShadow: '0 30px 80px rgba(0,0,0,0.28)', borderRadius: 18 }}>\n        <div style={{ width: 860, background: '#f2f2f0', borderRadius: '18px 18px 0 0', height: 52, display: 'flex', alignItems: 'center', gap: 10, padding: '0 22px', boxSizing: 'border-box', border: `2px solid ${G.border}`, borderBottom: 'none' }}>\n          {['#e0605a', '#e8b93e', '#67bb5a'].map((dot, i) => (\n            <div key={i} style={{ width: 16, height: 16, borderRadius: 8, background: dot }} />\n          ))}\n          <div style={{ marginLeft: 18, height: 14, width: 220, background: G.bar, borderRadius: 7 }} />\n        </div>\n        <Card w={860} h={430} seed={chapter + 2} style={{ borderRadius: '0 0 18px 18px', padding: 34 }} />\n      </div>\n    </AbsoluteFill>\n  );\n};\n\nexport const BottomPushStackWipe: React.FC = () => {\n  const frame = useCurrentFrame();\n  // 每章的推入进度\n  const progress = PUSH_STARTS.map((s) =>\n    interpolate(frame, [s, s + PUSH_DUR], [0, 1], {\n      extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: heavyEaseOut,\n    })\n  );\n  return (\n    <AbsoluteFill style={{ overflow: 'hidden', background: G.bg }}>\n      {CHAPTERS.map((_, i) => {\n        // 第 i 章的位移 = 自己被推入的进度 + 被后续章顶出的进度\n        const pushedIn = i === 0 ? 1 : progress[i - 1]; // 自己进入\n        const pushedOut = i < CHAPTERS.length - 1 ? progress[i] : 0; // 被下一章顶出\n        const y = (1 - pushedIn) * H - pushedOut * H;\n        if (y <= -H || y >= H) return null;\n        return (\n          <AbsoluteFill key={i} style={{ transform: `translateY(${y}px)` }}>\n            <ChapterScene chapter={i} />\n            {/* 推入时上缘接缝阴影，强化\"顶出\"的物理感 */}\n            {i > 0 && (\n              <div style={{ position: 'absolute', top: -40, left: 0, right: 0, height: 40, background: 'linear-gradient(to top, rgba(0,0,0,0.30), rgba(0,0,0,0))', opacity: pushedIn < 1 ? 1 : 0 }} />\n            )}\n          </AbsoluteFill>\n        );\n      })}\n    </AbsoluteFill>\n  );\n};\n\n\n"
}