Motion · Transition

Bottom Push Stack Wipe

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.

1920×108016:930 fps4.72s · 142 framesremotionLicense: Apache-2.0#shotcraft#remotion#transition

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

When to use

The chapter-change skeleton for multi-chapter product films (one selling point, one background color per chapter); segments needing a "page-turn rhythm" throughout.

Intention

wipe-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.

Duration & energy

  • Duration: One push-in: 30f + ~1s in-chapter hold; demo triple push: 140f (~4.7s).
  • Energy: Medium

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

意图

wipe-transitions 是一条边界扫过、新旧页都不动;page-turn 是立方体 翻转。本卡是第三种物性:新章把旧章顶出去——两屏刚性接触、同速 同向位移,像自动售货机压栈。命门是"连底色整屏走":底色+窗口卡+装饰 条焊成一块整料从底边推入,观众读到"换了个世界"而不是"换了张卡"。 重 ease-out(快进慢停)给推入一个"哐"的落位感;上缘 40px 接缝阴影 是两屏物理接触的证据。连推三章且底色饱和度拉满,换章本身就成了全片 的节拍器——slack 原片全片就靠这一招做骨架。

动效核心

  • 每章一个整屏层:translateY 从 +1080 推到 0,旧章同步被顶到

−1080——两层位移严格同步同缓动,任何速差都读作"滑动穿模"

  • 推入缓动 = 重 ease-out cubic-bezier(0.12, 0.9, 0.2, 1)(快进

慢停),30f 完成;第 i 章的 y = (1−自己推入进度)×H − 下一章推入 进度×H,一条式子管进也管出

  • 新章上缘带 40px 渐变接缝阴影(黑 0.30→透明,压在旧章上),落定

后归零——"顶出"的物理接触感全靠它

  • 三章底色绿 #2bac76 / 蓝 #36c5f0 / 粉 #e01e5a(demo 取 slack 系

饱和色);每章中央钉一张灰阶窗口卡 + 白色半透明装饰条

  • 推入间隔 hold ~1s(demo 18/55/92f 起推);出画的层及时卸载

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 推入时长 | 30f,重 ease-out | <20f 读作闪切;线性匀速读作卷帘门,"哐"感全无 | | 章间 hold | ~37f(1.2s 左右) | 章内是内容表演区,<25f 观众没看清就被顶走 | | 接缝阴影 | 上缘 40px,黑 0.30 渐变 | 去掉后两屏像叠影滑动;>80px 读作新章自带黑边 | | 底色 | 每章一种饱和色,相邻章色相拉开 | 相邻章同色系则"换章"读作"微调";灰阶章只能当第 0 章 | | 内容钉死 | 窗口卡/装饰条用章内绝对坐标 | 内容若相对视口定位就会"漂"在推入动画上,穿帮 | | 连推次数 | 2–4 章 | 1 次不成节奏;>4 次观众开始数底色,疲劳 |

已知坑

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

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

  • 与 wipe-transitions 分工:那卡新旧页都不动、一条几何边界扫过交接

(中性、哪儿都能用);本卡两页都在动、刚性顶出(有物性、自带章节 节奏)——单次接缝用那卡,全片骨架用本卡

  • 与 shot-transitions 六式同层选型:本卡能量中档、方向固定向上,

需要能量落差大的接缝(如进 outro)让位给流白/黑场

  • 方向统一是骨架感的来源:全片所有章都从底边推,中途换成左推/顶推

节拍器就散了——要变方向就别当骨架用

  • 推入进行中两章同屏,都是满屏层——章内动画(卡片入场等)避开推入

的 30f 窗口,不然两章都在动,视线没有锚点

  • 实战底色用品牌色板的饱和档;底色饱和度不足时窗口卡"钉在色底上"

的从属关系读不出来,会退化成"卡片各自换背景"

参考实现

demos/transition/bottom-push-stack-wipe/ (BottomPushStackWipe.tsx) 原片出处:slack-promo 22–27s

Reference implementation (Remotion)

  • demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.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/bottom-push-stack-wipe.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("bottom-push-stack-wipe")

Prompt

Use the video-shotcraft shot recipe "Bottom Push Stack Wipe" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/bottom-push-stack-wipe.json
Raw TSX: https://gimgs.net/motion/bottom-push-stack-wipe/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 4.72s

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

3,490 chars · Apache-2.0Open raw template ↗
// ===== demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx =====
// bottom-push-stack-wipe —— slack-promo 22–27s
// 换章手法:新场景连底色整屏从底边向上推入,把旧场景顶出画外,
// 连推三章(三种饱和底色,每章中央钉一张灰阶窗口卡随底色走)。
// 推入用重 ease-out(快进慢停)。
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';
import { Card, G } from '../../_fixtures/Fixtures';

const H = 1080;

// 章节定义:底色 + 卡片种子。第 0 章是起始灰阶场景。
const CHAPTERS = [
  { color: G.bg, label: 0 },
  { color: '#2bac76', label: 1 }, // 绿
  { color: '#36c5f0', label: 2 }, // 蓝
  { color: '#e01e5a', label: 3 }, // 粉红
];

// 每章推入的起始帧;约 32 帧完成一次推入,随后 hold
const PUSH_STARTS = [18, 55, 92];
const PUSH_DUR = 30;

const heavyEaseOut = Easing.bezier(0.12, 0.9, 0.2, 1); // 快进慢停

const ChapterScene: React.FC<{ chapter: number }> = ({ chapter }) => {
  const c = CHAPTERS[chapter];
  return (
    <AbsoluteFill style={{ background: c.color, justifyContent: 'center', alignItems: 'center' }}>
      {/* 底色上的淡装饰条,让"底色也在动"更可读 */}
      {chapter > 0 && (
        <>
          <div style={{ position: 'absolute', top: 90, left: 120, width: 500, height: 26, borderRadius: 13, background: 'rgba(255,255,255,0.28)' }} />
          <div style={{ position: 'absolute', bottom: 110, right: 140, width: 340, height: 26, borderRadius: 13, background: 'rgba(255,255,255,0.22)' }} />
          <div style={{ position: 'absolute', top: 160, right: 220, width: 90, height: 90, borderRadius: '50%', background: 'rgba(255,255,255,0.18)' }} />
        </>
      )}
      {/* 中央钉住的灰阶窗口卡 */}
      <div style={{ boxShadow: '0 30px 80px rgba(0,0,0,0.28)', borderRadius: 18 }}>
        <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' }}>
          {['#e0605a', '#e8b93e', '#67bb5a'].map((dot, i) => (
            <div key={i} style={{ width: 16, height: 16, borderRadius: 8, background: dot }} />
          ))}
          <div style={{ marginLeft: 18, height: 14, width: 220, background: G.bar, borderRadius: 7 }} />
        </div>
        <Card w={860} h={430} seed={chapter + 2} style={{ borderRadius: '0 0 18px 18px', padding: 34 }} />
      </div>
    </AbsoluteFill>
  );
};

export const BottomPushStackWipe: React.FC = () => {
  const frame = useCurrentFrame();
  // 每章的推入进度
  const progress = PUSH_STARTS.map((s) =>
    interpolate(frame, [s, s + PUSH_DUR], [0, 1], {
      extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: heavyEaseOut,
    })
  );
  return (
    <AbsoluteFill style={{ overflow: 'hidden', background: G.bg }}>
      {CHAPTERS.map((_, i) => {
        // 第 i 章的位移 = 自己被推入的进度 + 被后续章顶出的进度
        const pushedIn = i === 0 ? 1 : progress[i - 1]; // 自己进入
        const pushedOut = i < CHAPTERS.length - 1 ? progress[i] : 0; // 被下一章顶出
        const y = (1 - pushedIn) * H - pushedOut * H;
        if (y <= -H || y >= H) return null;
        return (
          <AbsoluteFill key={i} style={{ transform: `translateY(${y}px)` }}>
            <ChapterScene chapter={i} />
            {/* 推入时上缘接缝阴影,强化"顶出"的物理感 */}
            {i > 0 && (
              <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 }} />
            )}
          </AbsoluteFill>
        );
      })}
    </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/transition/bottom-push-stack-wipe/BottomPushStackWipe.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