Motion · Entrada de UI

List reveal with drift

A 6-item vertical menu settles into place in sequence at a 0.09 stagger with a light overshoot on landing, while the whole list container drifts linearly upward throughout — two unrelated layers of motion.

1920×108016:930 fps4.1s · 123 quadrosremotionLicença: Apache-2.0#shotcraft#remotion#ui-entrance

Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador

When to use

Entrances for navigation/sidebars/settings panels; any UI segment that should feel "self-assembling", also works as a low-energy backdrop under narration.

Intention

A list fading in item by item is one of the easiest motions to make feel dead — each item stops moving and the frame sits completely still between items. This card's solution is to add a slow drift on the container that's unrelated to the item-by-item entrance — locally items are "finding their place", globally the frame is "breathing". The audience reads every item clearly, while not a single frame from start to finish is pinned dead still.

Duration & energy

  • Duration: About 3.6s (108f@30fps)
  • Energy: Low (a steady pulse, no peak; kept alive by the drift)

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

意图

列表逐项淡入是最容易做得死板的一类动效:每项动完就停,画面在两项之间是完全静止的。 这张卡的解法是给容器加一层与逐项入场无关的慢速漂移——局部在"找位",整体在"呼吸"。 观众读得清每一项,同时画面从头到尾没有一帧是钉死的。

动效核心

  • 两层运动完全解耦:容器 list.style.transform = translateY(lerp(t, 16, -16)) 只吃全局 t,

是贯穿全片的线性漂移;每项自己吃 seg(...) 的局部窗口,互不参与对方的计算

  • 逐项入场:seg(t, 0.06+i*0.09, +0.24, E.outBack),stagger 0.09(约 10f)比常规

UI stagger 慢一档——这是"读得完"的间隔,不是"快速铺开"的间隔

  • 找位动作是 scale 主导:scale(0.78 + p*0.22) 配 translateY(14→0),位移只有 14px,

观众感知到的是"从小长到实",而不是"从下面飞上来"

  • E.outBack 的过冲让 scale 短暂越过 1(约 1.002 量级的软过冲)——量很小,作用是把

落位的那一帧从"停"变成"扣住"

  • 透明度 min(1, p*2.2):行程走到 45% 就已全实,避免半透明的列表项和背景卡片色

(#161a26 + 1px #262c40 描边)糊在一起

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 项数 | 6(Dashboard…Sign out) | 末项窗口 0.51→0.75,尾部留 0.25 静置给观众收尾;>8 项必须压 stagger,否则末项吃到收尾 | | stagger | 0.09/项(≈10f) | 10f 是"逐项读得完";压到 0.04 变成一次铺开(另一种手法),拉到 0.15 以上节拍会散 | | 单项行程 | 窗 0.24(≈26f) | 行程比 stagger 长 2.6 倍 → 相邻三项同时在动,队列是连绵的;两者相等会变成一项一项硬接 | | 起始 scale | 0.78 | 0.78 是"已经能认出是个条目"的尺度;<0.5 读作从零弹出,和 pop 类手法混淆 | | 起始位移 | 14px | 位移必须远小于行高(9px gap + 约 33px 项高),否则项与项之间穿插 | | 过冲 | E.outBack | 这里的过冲要小到几乎读不出来;换 spring 或加大 back 系数,6 项连续抖会显得廉价 | | 容器漂移 | lerp(t, 16, -16),全程线性 | 总位移 32px 摊在 3.6s 上(约 0.3px/f)——刚好在"感觉画面活着"和"看得出在动"之间;>60px 会与逐项位移量级相当,两层运动混为一件事 | | 透明度斜率 | min(1, p*2.2) | 2.2 决定多早变实;降到 1 会看到半透明项叠在背景上,深色主题下尤其脏 |

已知坑

  • 容器漂移是线性且贯穿全程,t=1 时停在 -16px 而不是 0——这个动效末帧不回位。

要循环播放或与后一镜对齐位置,得把漂移换成 seg(t, 0, 1, ...) 之外的收束曲线, 或在末段补一个反向缓收

  • 项数变化必须重算 stagger:末项起点 0.06+(n-1)*0.09,10 项时起点已到 0.87,行程会

被 t=1 截断,最后一项永远进不来

  • 文案没设 white-space:nowrap,容器宽度写死 240px——中文或更长的英文标签会换行,

行高一变整列节奏全乱

  • 漂移方向(向上)与逐项位移方向(14px→0,也是向上)同向。这是有意的(同向叠加更

顺),但两者量级必须保持 2 倍以上差距,否则读作"一起往上飘"而非两层

  • 图标是 hue 色环渐变方块的占位(6 个色相区分条目),落地要换成真实图标;条目底色

#161a26 属深色主题,浅色界面下描边和阴影都要重调

参考实现

demos/ui-entrance/list-reveal/ (ListReveal.tsx)

Reference implementation (Remotion)

  • demos/ui-entrance/list-reveal/ListReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/list-reveal/ListReveal.tsx

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

Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.

  1. Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
  2. Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
  3. Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/list-reveal.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/ui-entrance/list-reveal/ListReveal.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("list-reveal")

Prompt

Use the video-shotcraft shot recipe "List reveal with drift" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/list-reveal.json
Raw TSX: https://gimgs.net/motion/list-reveal/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/ui-entrance/list-reveal/ListReveal.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 4.1s

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

2,800 chars · Apache-2.0Abrir template bruto ↗
// ===== demos/ui-entrance/list-reveal/ListReveal.tsx =====
// list-reveal — List Reveal 菜单逐项找位(motion-lab 定稿转原生 Remotion)
// 垂直菜单列表项依次 scale 找位入场(outBack 轻微过冲),同时整个列表容器
// 全程线性缓慢上移——"整体漂移"与"逐项入场"两层运动分离叠加。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React from 'react';
import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';

export const LIST_REVEAL_DURATION = 108; // 3600ms @30fps

const LABELS = ['Dashboard', 'Projects', 'Analytics', 'Messages', 'Settings', 'Sign out'];
const HUES = [225, 250, 275, 300, 210, 340];

export const ListReveal: React.FC = () => {
  const t = useT();
  return (
    <DesignStage bg="#0a0b10">
      {/* 居中容器:flex 撑起列表垂直水平居中 */}
      <div
        style={{
          position: 'absolute',
          inset: 0,
          background: '#0a0b10',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {/* 整体漂移层:全程线性缓慢上移 */}
        <div
          style={{
            position: 'relative',
            width: 240,
            display: 'flex',
            flexDirection: 'column',
            gap: 9,
            // -0.5px:原样片浏览器 flex 居中落在整数位(列表高 255 → 顶部 7.0),
            // Remotion 渲染落在 7.5,补回半个设计像素对齐原片
            transform: `translateY(${lerp(t, 16, -16) - 0.5}px)`,
          }}
        >
          {LABELS.map((s, i) => {
            // 逐项入场:outBack 轻微过冲找位
            const p = seg(t, 0.06 + i * 0.09, 0.06 + i * 0.09 + 0.24, E.outBack);
            return (
              <div
                key={i}
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: 11,
                  padding: '9px 13px',
                  borderRadius: 10,
                  background: '#161a26',
                  border: '1px solid #262c40',
                  opacity: Math.min(1, p * 2.2),
                  transform: `scale(${0.78 + Math.max(0, p) * 0.22}) translateY(${lerp(Math.max(0, p), 14, 0)}px)`,
                }}
              >
                <div
                  style={{
                    width: 15,
                    height: 15,
                    borderRadius: 5,
                    flex: 'none',
                    background: `linear-gradient(140deg,hsl(${HUES[i]},75%,64%),hsl(${HUES[i]},70%,46%))`,
                  }}
                />
                <div
                  style={{
                    fontFamily: '-apple-system,sans-serif',
                    fontWeight: 500,
                    fontSize: 13,
                    lineHeight: 1,
                    color: '#c6cde2',
                  }}
                >
                  {s}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </DesignStage>
  );
};


  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/ui-entrance/list-reveal/ListReveal.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
—

Mais nesta categoria