# Picker carousel feature cycle

A mobile-style vertical picker — the focus pill stays still while content scrolls through it, snapping to a full stop each time.

- Category: interaction · Tags: shotcraft, remotion, interaction
- Spec: 1920×1080 @ 30 fps · 4.1s (16:9)
- License: Apache-2.0 · Tier: free
- JSON: https://gimgs.net/motion/picker-carousel-feature-cycle.json
- Kind: remotion
- Source (TSX): https://gimgs.net/motion/picker-carousel-feature-cycle/template.html
- Repo: https://github.com/Vincentwei1021/video-shotcraft
- Credits: video-shotcraft (Vincent Wei) — https://github.com/Vincentwei1021/video-shotcraft
- Page: https://gimgs.net/motion/picker-carousel-feature-cycle.html

## Params

_No params._

Default params (base64url for `?p=`): `e30`

## Prompt

## When to use

A shot that reads off feature/scene names one by one; a "pick one" interaction demo; a picker-control showcase for mobile products

## Intention

Give the physical feel of "selecting" something: the focus frame is fixed, it's the content that scrolls and gets snapped into it. Each item must **actually go still for a few frames** after stopping — continuous scrolling reads as a loading animation, only a pause reads as "this item is now selected." Distance-based decay (opacity + font size + grayscale all fading together with distance from center) is the only way to keep the viewer's eye locked on the middle row.

## Duration & energy

- Duration: About 3.6s (108f@30fps)
- Energy: Medium (every snap is a beat, five beats advancing at a steady pace)

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

## 意图
把"选择"这件事的物理感做出来：焦点框是固定的，是内容在滚动并被吸住。
每项停下后必须**真的静止几帧**——连续滚动读作 loading 动画，停顿才读作
"这一项被选中了"。距离衰减（透明度 + 字号 + 灰度三通道同时衰减）是让
观众视线始终锁在中间那一行的唯一手段。

## 动效核心
- 视口 300×170px（5 行 × rowH 34），焦点药丸固定在第 3 行位置
  （`top: rowH*2`），白底 + 1px `#E3E3E6` 描边 + `0 2px 8px rgba(0,0,0,.06)`
- 全程 `t 0.05→0.95` 匀速切成 STEPS=5 步；步内位移
  `outQuint(min(1, local/(1−HOLD)))`，HOLD=5/14 的尾段完全不动
  （每步≈0.65s = 走位≈0.42s + 静止≈0.23s）
- 列表整体 `translateY(rowH*2 − pos*rowH)`——移动的是内容，药丸零位移
- 距离衰减（`d = |i − pos|`）分三段：opacity `d≤1` 时 1→0.55、
  `1<d≤2` 时 0.55→0.18；fontSize 17→14px（按 `d/2`）；
  color `d<0.5` 用 INK、`d<1.5` 用 MID、更远用 `#B9B9BE`
- 行内图标 opacity `max(0, 1 − d*1.6)`——只有焦点行看得见图标
- 落定后药丸 `scaleY 1 + sin(min(1, land/0.6)·π)*0.06` 呼吸一下
  （`land` 只在 HOLD 段内 >0，即吸附完成才呼吸）
- 视口上下各 26% 的 PAPER→透明渐变遮罩，让进出项自然消隐

## 参数表
| 参数 | 典型值 | 调节手感 |
|------|--------|----------|
| 行高/可见行数 | rowH 34px × 5 行，焦点在第 3 行 | 可见行数必须是奇数，否则焦点行不在视觉正中 |
| 步数 | STEPS=5，窗口 t 0.05→0.95 匀速 | 5 拍是 3.6s 的舒适上限；7 拍每拍就压到 0.46s 读不清 |
| HOLD | 5/14 ≈ 0.357（每步尾段静止≈0.23s） | **命门**：HOLD 归零就变成匀速滚动，完全丢掉"吸附"语义 |
| 吸附缓动 | outQuint（作用在 local/(1−HOLD) 上） | outQuint 的急减速就是"被磁铁吸住"的手感，outCubic 太软 |
| opacity 衰减 | 1 → 0.55（d≤1）→ 0.18（d≤2） | 相邻行留 0.55 是让观众感知"上下还有内容"；降到 0.3 就孤立了 |
| fontSize 衰减 | 17→14px，按 min(1, d/2) | 字号差是第二重景深；只做透明度会显得扁 |
| 图标可见性 | max(0, 1 − d·1.6) | 只有焦点行带图标，是"当前项"的额外标记 |
| 药丸呼吸 | scaleY 1→1.06→1，落定后 0.6·HOLD 内走完 | 0.06 已到上限；再大就读作药丸自己在动，抢了内容的戏 |

## 已知坑
- 药丸必须是 **`translateY` 零位移的固定层**，`scaleY` 呼吸也不能带
  位移，否则焦点框漂移，"内容穿过焦点"的语义立刻失效
- 距离衰减写在 `fontSize` 上会触发逐帧重排（行高固定所以布局不跳），
  行高若改成 auto 就会抖；rowH 必须写死
- 渐变遮罩的颜色要与背景 PAPER 完全一致，差一档就露出边界带
- 左外侧 26×22 的方形 AI 徽标用 `margin:-11px 0 0 -186px` 相对画面中心
  定位；改视口宽度必须同改 186px
- ITEMS 是 7 条占位功能名（"Data Cleanup" 等），落地换真实功能名时
  字长变化会影响居中观感——单行超过 300px 就要缩字号而不是换行

## 参考实现
demos/interaction/picker-carousel-feature-cycle/
（PickerCarouselFeatureCycle.tsx）

## Reference implementation (Remotion)

- demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx

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


## Flow

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/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.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.

## Render

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.

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