{
  "slug": "chip-lift-to-user-pill",
  "title": "Chip Lift to User Pill",
  "tagline": "A target chip in a grid hard-cuts to inverted black-on-white in 3 frames, other chips fade and shrink out staggered by Manhattan distance to it; the black chip's left edge anchors and grows into a pill, typing out a name letter by letter and lighting a green dot, then a 1px line connects it to a circular badge.",
  "description": "A target chip in a grid hard-cuts to inverted black-on-white in 3 frames, other chips fade and shrink out staggered by Manhattan distance to it; the black chi",
  "category": "interaction",
  "tags": [
    "shotcraft",
    "remotion",
    "interaction"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 5.5,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/chip-lift-to-user-pill/poster-5ee2dd22db47.jpg",
    "video": "https://gimgs.net/motion/media/chip-lift-to-user-pill/video-54cf5f918e77.mp4"
  },
  "variants": [
    {
      "key": "chip-lift-to-user-pill",
      "label": "chip-lift-to-user-pill",
      "description": "A target chip in a grid hard-cuts to inverted black-on-white in 3 frames, other chips fade and shrink out staggered by Manhattan distance to it; the black chip's left edge anchors and grows into a pill, typing out a name letter by letter and lighting a green dot, then a 1px line connects it to a circular badge.",
      "use": "",
      "video": "https://gimgs.net/motion/media/chip-lift-to-user-pill/video-54cf5f918e77.mp4",
      "poster": "https://gimgs.net/motion/media/chip-lift-to-user-pill/poster-5ee2dd22db47.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/chip-lift-to-user-pill.html",
  "json_url": "https://gimgs.net/motion/chip-lift-to-user-pill.json",
  "template_url": "https://gimgs.net/motion/chip-lift-to-user-pill/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/chip-lift-to-user-pill.md",
  "updated_at": "2026-09-24T07:39:50.000Z",
  "prompt": "## When to use\n\nThe \"pick and expand this one from a pile of candidates\" interaction flow; feature demos for collaboration/address-book/recipient-style products; a select-to-detail transition.\n\n## Intention\n\nGive \"selecting\" two completely different textures: the instant of selection is hard (3-frame stepped inversion, like real UI's `:active`), and everything after is soft (the pill growing, letters typing in, the green dot lighting up). Other chips fade out staggered by distance, telling viewers \"attention is converging from here\" — ordering by distance matters more than ordering by time, because it gives the causality a spatial anchor.\n\n## Duration & energy\n\n- Duration: About 5.0s (150f@30fps).\n- Energy: Medium (the moment of selection is a hard burst, everything after is an unhurried grow-and-type).\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n让\"选中\"这件事有**两段完全不同的质感**：选中瞬间是硬的（3 帧台阶化反色，\n像真实 UI 的 `:active`），选中之后是软的（药丸生长、逐字打字、绿点点亮）。\n其余 chip 按距离交错淡出，是在告诉观众\"注意力从这里开始收拢\"——\n距离排序比时间排序重要，因为它给出了空间上的因果。\n\n## 动效核心\n- 4×3 网格（chip 40×24、间距 10/9），目标位于第 2 列第 2 行，\n  选中前先 `appendChild` 提到最上层\n- **反色硬切**：`a = seg(0.04→0.085, linear)` 再台阶化成\n  `a<0.34 ? 0 : a<0.67 ? 0.5 : 1`——只有 3 个取值，得到 3 帧硬切感；\n  背景 `#fff→INK`、描边 `LINE→INK`、文字 `TXT→#fff` 同步跳变\n- **距离交错淡出**：其余 chip 按曼哈顿距离 `|Δcol|+|Δrow|`，\n  `d0 = 0.10 + dist*0.022`，各 0.075 走完（outQuad），\n  opacity 1→0 + `scale 1→0.9`\n- **左缘锚定生长**：`g = seg(0.26→0.44, outCubic)` 驱动 width 40→190px，\n  `left` 不变——生长方向单一，读作\"展开\"而非\"移动\"\n- 人名逐字：以 `g` 为自变量（不是 t），字符 `0.18 + i*0.062` 起、\n  各 0.05 走完，`translateY 2→0`；原 2 字母标签在 `g*5` 内先撤掉\n- 绿点 `#35D07F` + `0 0 8px` 辉光，在 `g` 的 0.85→1 段 outBack 弹出，\n  `left = w − 15` 始终吸在药丸右端\n- 连接线 `0.47→0.57` outQuad 从 0 拉到 90px → 徽标 `0.56→0.63`\n  scale 0.8→1 淡入 → 字幕 `0.6→0.66` 出现并逐词加深到 0.92\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 反色时长 | t 0.04→0.085，台阶化成 3 档 | **不要给它缓动**：连续插值读作\"渐变高亮\"，台阶才读作按下 |\n| 距离间隔 | 0.022 / 曼哈顿距离 | 按 index 排序也能错峰，但按距离才有\"从选中点扩散\"的空间因果 |\n| 余项退场 | 各 0.075、opacity→0 + scale→0.9 | 只淡出不缩小会显得\"被擦掉\"；缩到 0.9 才像退到后面 |\n| 药丸生长 | width 40→190px，t 0.26→0.44 outCubic，左缘锚定 | 双向生长（居中扩张）会把它读成弹窗，不是同一个 chip 长大 |\n| 逐字打字 | 以 g 为自变量，间隔 0.062、各 0.05 | 挂在 g 上而不是 t 上：改生长时长时打字节奏自动跟随 |\n| 绿点 | 7px + 0 0 8px 辉光，g 的 0.85→1 段 outBack | 必须在生长收尾才亮：早了就成了\"已在线\"，不是\"刚接上\" |\n| 连接线 | 0→90px，t 0.47→0.57 outQuad | 线要在药丸完全定型后才起笔，否则读作药丸在被拽 |\n| 徽标 | 26px，0.56→0.63 outCubic，scale 0.8→1 | 与连接线抵达重叠 0.01，才有\"线到即物到\"的交棒 |\n\n## 已知坑\n- 目标 chip 必须在动画前提到 DOM 最上层，否则生长时被右侧 chip 盖住\n- 药丸内的人名容器 `left:13px` 是按 24px 高的 chip 配的内边距；\n  改 chip 高度要同改，否则文字贴边\n- 绿点的 `left = w − 15` 每帧重算——写死坐标后一改 PW1 就飞出药丸\n- 连接线与徽标的坐标全部基于 `PX + PW1`（药丸终态右缘），\n  改生长终值必须同改这两处\n- 占位内容：12 个双字母标签 + 人名 \"Casey Doe\" + 字幕\n  \"Starting with Casey\"，落地全部替换；人名字符数变化会改变打字总时长\n  （间隔 0.062 × 字符数必须留在 g 的 1.0 以内）\n\n## 参考实现\ndemos/interaction/chip-lift-to-user-pill/\n（ChipLiftToUserPill.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.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/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.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/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.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": 165,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.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/interaction/chip-lift-to-user-pill/ChipLiftToUserPill.tsx =====\n// chip-lift-to-user-pill — Chip Lift 选中 chip 长成人名药丸（motion-lab 定稿转原生 Remotion）\n// 网格里的目标 chip 先 3 帧硬切反色成黑底白字，其余 chip 按到它的距离交错淡出并缩到 0.9；\n// 黑 chip 保持左缘不动向右生长成药丸，内部逐字打出人名并点亮绿点，再拉一条 1px 连接线\n// 接到圆形徽标，最后走逐词加深字幕。\n// 设计坐标 480×270（DesignStage 等比放大），440×240 定尺画布居中排版。\nimport React from 'react';\nimport { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';\n\nexport const CHIP_LIFT_TO_USER_PILL_DURATION = 150; // 5000ms @30fps\n\n// ---- 本卡共享量（浅灰瑞士极简系配色 / 字体；BG/DIM/h2r 取自 motion-lab/fx/b09.js 同批定义） ----\nconst SANS = '-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif';\nconst BG = '#F1F1F3'; // 页面浅灰\nconst INK = '#0B0B0C'; // 纯黑\nconst TXT = '#111111'; // 正文黑\nconst DIM = '#C9C9CE'; // 浅灰占位字\nconst LINE = '#E6E6EA'; // 描边\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\nconst h2r = (h: string) => [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];\n// 颜色插值：mix(p,'#C9C9CE','#111')\nconst mix = (p: number, a: string, b: string) => {\n  const A = h2r(a), B = h2r(b), q = clamp01(p);\n  return `rgb(${Math.round(A[0] + (B[0] - A[0]) * q)},${Math.round(A[1] + (B[1] - A[1]) * q)},${Math.round(A[2] + (B[2] - A[2]) * q)})`;\n};\n\n// ---- 网格参数（与 effect.js 完全一致） ----\nconst COLS = 4, ROWS = 3, CW = 40, CH = 24, GX = 10, GY = 9, GX0 = 8, GY0 = 70;\nconst TC = 1, TR = 1; // 目标 chip 的列/行\nconst LABELS = ['JD', 'MK', 'CD', 'RL', 'AV', 'TP', 'KN', 'BW', 'CE', 'HR', 'LM', 'DQ'];\n// 目标 chip 左上角坐标（左缘锚定，生长时不动）\nconst TX = GX0 + TC * (CW + GX); // 58\nconst TY = GY0 + TR * (CH + GY); // 103\n// 其余 chip：位置 + 到目标的曼哈顿距离（交错淡出用）\nconst OTHERS = Array.from({ length: ROWS * COLS }, (_, i) => {\n  const r = Math.floor(i / COLS), c = i % COLS;\n  return {\n    x: GX0 + c * (CW + GX),\n    y: GY0 + r * (CH + GY),\n    label: LABELS[i],\n    dist: Math.abs(c - TC) + Math.abs(r - TR),\n    isT: c === TC && r === TR,\n  };\n}).filter((o) => !o.isT);\n\nconst PW0 = CW, PW1 = 190; // 药丸生长的起止宽度\nconst NAME_CHARS = 'Casey Doe'.split(''); // 药丸内逐字打出的人名\n\n// 结尾字幕（逐词加深语法，只用到 show + inn 两态）\nconst CAP_WORDS = 'Starting with Casey'.split(' ');\nconst CAP_ST = 0.78 / CAP_WORDS.length;\nconst CAP_WIN = CAP_ST * 1.5;\n\n// 徽标尺寸（白底圆 + 四角星）\nconst BADGE_SIZE = 26;\nconst BADGE_SVG = Number((BADGE_SIZE * 0.52).toFixed(1)); // 13.5\n\nexport const ChipLiftToUserPill: React.FC = () => {\n  const t = useT();\n\n  // A 反色硬切（3 帧感）：进度台阶化到 0 / 0.5 / 1 → 硬切质感\n  const a = seg(t, 0.04, 0.085, E.linear);\n  const aq = a < 0.34 ? 0 : a < 0.67 ? 0.5 : 1;\n\n  // C 药丸从左缘生长 + 逐字 + 绿点\n  const g = seg(t, 0.26, 0.44, E.outCubic);\n  const w = lerp(g, PW0, PW1);\n  const dq = seg(g, 0.85, 1, E.outBack);\n\n  // D 连接线 → 徽标 → 字幕\n  const cw = seg(t, 0.47, 0.57, E.outQuad);\n  const bp = seg(t, 0.56, 0.63, E.outCubic);\n  const capShow = seg(t, 0.6, 0.66);\n  const capInn = seg(t, 0.6, 0.92);\n\n  return (\n    <DesignStage bg={BG}>\n      {/* 页面 + 440×240 定尺画布（居中） */}\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          background: BG,\n          overflow: 'hidden',\n          fontFamily: SANS,\n          WebkitFontSmoothing: 'antialiased',\n        }}\n      >\n        <div style={{ position: 'absolute', left: '50%', top: '50%', width: 440, height: 240, margin: '-120px 0 0 -220px' }}>\n          {/* B 其余 chip：按到目标的曼哈顿距离交错淡出 + scale .9 */}\n          {OTHERS.map((o, i) => {\n            const d0 = 0.10 + o.dist * 0.022;\n            const p = seg(t, d0, d0 + 0.075, E.outQuad);\n            return (\n              <div\n                key={i}\n                style={{\n                  position: 'absolute',\n                  left: o.x,\n                  top: o.y,\n                  width: CW,\n                  height: CH,\n                  borderRadius: CH / 2,\n                  background: '#fff',\n                  border: `1px solid ${LINE}`,\n                  display: 'flex',\n                  alignItems: 'center',\n                  boxSizing: 'border-box',\n                  overflow: 'hidden',\n                  boxShadow: '0 1px 3px rgba(0,0,0,.04)',\n                  opacity: 1 - p,\n                  transform: `scale(${lerp(p, 1, 0.9)})`,\n                }}\n              >\n                <div\n                  style={{\n                    position: 'absolute',\n                    left: 0,\n                    top: 0,\n                    width: CW,\n                    height: CH,\n                    display: 'flex',\n                    alignItems: 'center',\n                    justifyContent: 'center',\n                    font: `600 10.5px/1 ${SANS}`,\n                    letterSpacing: '.6px',\n                    color: TXT,\n                  }}\n                >\n                  {o.label}\n                </div>\n              </div>\n            );\n          })}\n\n          {/* 目标 chip（最上层）：反色硬切 → 左缘锚定向右生长成药丸 */}\n          <div\n            style={{\n              position: 'absolute',\n              left: TX,\n              top: TY,\n              width: w,\n              height: CH,\n              borderRadius: CH / 2,\n              background: mix(aq, '#ffffff', INK),\n              border: `1px solid ${mix(aq, LINE, INK)}`,\n              display: 'flex',\n              alignItems: 'center',\n              boxSizing: 'border-box',\n              overflow: 'hidden',\n              boxShadow: '0 1px 3px rgba(0,0,0,.04)',\n            }}\n          >\n            {/* 原缩写标签：反色后随生长淡出 */}\n            <div\n              style={{\n                position: 'absolute',\n                left: 0,\n                top: 0,\n                width: CW,\n                height: CH,\n                display: 'flex',\n                alignItems: 'center',\n                justifyContent: 'center',\n                font: `600 10.5px/1 ${SANS}`,\n                letterSpacing: '.6px',\n                color: mix(aq, TXT, '#ffffff'),\n                opacity: 1 - clamp01(g * 5),\n              }}\n            >\n              CD\n            </div>\n            {/* 人名逐字打出（stagger 跑在生长进度 g 上） */}\n            <div\n              style={{\n                position: 'absolute',\n                left: 13,\n                top: 0,\n                height: CH,\n                display: 'flex',\n                alignItems: 'center',\n                whiteSpace: 'nowrap',\n              }}\n            >\n              {NAME_CHARS.map((ch, i) => {\n                const p = seg(g, 0.18 + i * 0.062, 0.18 + i * 0.062 + 0.05, E.outQuad);\n                return (\n                  <span\n                    key={i}\n                    style={{\n                      font: `600 11px/1 ${SANS}`,\n                      color: '#fff',\n                      opacity: p,\n                      whiteSpace: 'pre',\n                      letterSpacing: '.2px',\n                      transform: `translateY(${lerp(p, 2, 0)}px)`,\n                      display: 'inline-block',\n                    }}\n                  >\n                    {ch}\n                  </span>\n                );\n              })}\n            </div>\n            {/* 在线绿点：outBack 弹出，钉在药丸右缘内侧 */}\n            <div\n              style={{\n                position: 'absolute',\n                top: (CH - 7) / 2,\n                left: w - 15,\n                width: 7,\n                height: 7,\n                borderRadius: '50%',\n                background: '#35D07F',\n                boxShadow: '0 0 8px rgba(53,208,127,.6)',\n                opacity: clamp01(dq * 2),\n                transform: `scale(${dq})`,\n              }}\n            />\n          </div>\n\n          {/* 1px 连接线：从药丸右缘拉到徽标 */}\n          <div\n            style={{\n              position: 'absolute',\n              left: TX + PW1,\n              top: TY + CH / 2,\n              height: 1,\n              width: `${(cw * 90).toFixed(1)}px`,\n              background: TXT,\n            }}\n          />\n\n          {/* AI 徽标（白底圆 + 四角星） */}\n          <div\n            style={{\n              position: 'absolute',\n              width: BADGE_SIZE,\n              height: BADGE_SIZE,\n              borderRadius: '50%',\n              background: '#fff',\n              border: `1px solid ${LINE}`,\n              boxSizing: 'border-box',\n              boxShadow: '0 2px 10px rgba(0,0,0,.07)',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n              left: TX + PW1 + 90 - 2,\n              top: TY + CH / 2 - 13,\n              opacity: bp,\n              transform: `scale(${lerp(bp, 0.8, 1)})`,\n            }}\n          >\n            <svg width={BADGE_SVG} height={BADGE_SVG} viewBox=\"0 0 24 24\">\n              <path d=\"M12 0.8 L14.3 9.7 L23.2 12 L14.3 14.3 L12 23.2 L9.7 14.3 L0.8 12 L9.7 9.7 Z\" fill={TXT} />\n            </svg>\n          </div>\n\n          {/* 结尾字幕：逐词加深（浅灰占位 → 黑），整行透明度随 show 淡入 */}\n          <div\n            style={{\n              position: 'absolute',\n              display: 'flex',\n              alignItems: 'baseline',\n              whiteSpace: 'nowrap',\n              left: TX + PW1 - 18,\n              top: TY + CH + 34,\n              opacity: capShow,\n            }}\n          >\n            {CAP_WORDS.map((wd, i) => {\n              const q = clamp01((capInn - i * CAP_ST) / CAP_WIN);\n              return (\n                <span\n                  key={i}\n                  style={{\n                    font: `600 13px/1.25 ${SANS}`,\n                    color: mix(q, DIM, TXT),\n                    letterSpacing: (-0.03 * (1 - q)).toFixed(4) + 'em',\n                    marginRight: i === CAP_WORDS.length - 1 ? 0 : 4.5,\n                  }}\n                >\n                  {wd}\n                </span>\n              );\n            })}\n          </div>\n        </div>\n      </div>\n    </DesignStage>\n  );\n};\n\n\n"
}