{
  "slug": "glow-flyline-moves",
  "title": "Glow & flyline moves",
  "tagline": "Three dark-scene glow-and-flyline styles: glow-orb-ambient ambient glow noise, flyline-arc a connecting light arc, and orb-flyline-relay combining both with same-frame resonance.",
  "description": "Three dark-scene glow-and-flyline styles: glow-orb-ambient ambient glow noise, flyline-arc a connecting light arc, and orb-flyline-relay combining both with s",
  "category": "effects",
  "tags": [
    "shotcraft",
    "remotion",
    "effects",
    "data"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 5.06,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/glow-flyline-moves/poster-f4d169bbe635.jpg",
    "video": "https://gimgs.net/motion/media/glow-flyline-moves/video-760918b16074.mp4"
  },
  "variants": [
    {
      "key": "glow-orb-ambient",
      "label": "glow-orb-ambient · Ambient glow orbs",
      "description": "Three 500–700px radial glow orbs + blur(100px), drifting on a dual-sine path; card-edge glow is driven by the max of weighted orb distance [180,720]px → [1,0].",
      "use": "Backdrop for dark segments; gives cards a breathing feel",
      "video": "https://gimgs.net/motion/media/glow-flyline-moves/video-760918b16074.mp4",
      "poster": "https://gimgs.net/motion/media/glow-flyline-moves/poster-f4d169bbe635.jpg"
    },
    {
      "key": "flyline-arc",
      "label": "flyline-arc · Connecting flyline arc",
      "description": "A hand-drawn bezier sampled into 100 segments, growing over 22f out-cubic; a light head conditionally attaches and leads, with segment opacity fading by distance from the head; the landing point pulses along its outline, and can chain into a relay.",
      "use": "Data-flow direction / metric relationships; dashboard storytelling",
      "video": "https://gimgs.net/motion/media/glow-flyline-moves/video-1a1c272233fa.mp4",
      "poster": "https://gimgs.net/motion/media/glow-flyline-moves/poster-flyline-arc-e6ed95db6a02.jpg"
    },
    {
      "key": "orb-flyline-relay",
      "label": "orb-flyline-relay · Orb-flyline relay",
      "description": "The weld of A+B: the orb's surge and the card's pulse share the same landing frame, brightening by 1+1.6×surge, rising over 5f and dissipating over 15f.",
      "use": "The highlight segment of a dark scene; a three-card relay wrap-up",
      "video": "https://gimgs.net/motion/media/glow-flyline-moves/video-f2de9c14554f.mp4",
      "poster": "https://gimgs.net/motion/media/glow-flyline-moves/poster-orb-flyline-relay-ca5546dbfb69.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/glow-flyline-moves.html",
  "json_url": "https://gimgs.net/motion/glow-flyline-moves.json",
  "template_url": "https://gimgs.net/motion/glow-flyline-moves/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/glow-flyline-moves.md",
  "updated_at": "2026-09-24T07:40:00.424Z",
  "prompt": "## When to use\n\nThe atmosphere and data storytelling of the film's one dark segment: use A to lay down ambient noise, B to tell data-flow direction, C when the background needs to answer the foreground; a Linear-website feel.\n\n## Intention\n\nThis is the library's first card to live entirely in a **dark scene**. The white-background-can't-glow precedent, read in reverse, says: to play with light, go dark first. A is the space breathing — three heavily blurred glow orbs drift organically near black, and the central dark card's edges glow brighter as the nearest orb approaches, so the background is no longer dead; B is the verb of data — a glowing arc \"strikes\" from card A to card B, a bright leading point with a fading tail, the landing point pulsing along its outline, giving metric relationships a direction for the first time; C welds A and B together — the frame where a flyline lands makes the nearby orb surge brighter in the same beat, ambient layer and event layer **resonating on the same frame** rather than performing separately (the crux of the combo: separate the resonating frame and it degrades into two cards placed side by side).\n\n## Duration & energy\n\n- Duration: A: ~5s / B: ~4.7s / C: ~5.2s\n- Energy: A: Low (ambient-noise level) / B: Medium / C: Medium-high\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n这是库内第一张整卡住在**暗场**的卡。白底提亮不可见判例反过来读就是：\n要玩光，先去暗场。A 是空间的呼吸——近黑底三团重 blur 光斑有机漂移，\n中央深卡边缘随最近光斑靠近而泛光，背景不再是死的；B 是数据的动词——\n发光弧线从 A 卡\"打\"到 B 卡，亮点头领跑、渐隐光尾，落点描边脉冲点亮，\n指标关联第一次有了方向；C 是 A+B 的焊接——飞线落点帧邻近光斑同帧\n涨亮一拍，氛围层与事件层**同帧共振**，互相搭腔而非各演各的（组合命门：\n拆开共振帧就退化成两张卡并置）。\n\n## 三式选型\n| 式 | 做法 | 适用 |\n|----|------|------|\n| A glow-orb-ambient | 三团 500–700px radial 光斑 + blur(100px)，双正弦漂移；卡缘辉光按光斑距离 [180,720]px→[1,0] 加权取 max 驱动 | 暗场段落铺底；卡呼吸感 |\n| B flyline-arc | 手写 bezier 100 段采样，22f out-cubic 生长；光头条件挂载领跑，段 opacity 按离头距离渐隐；落点描边脉冲，可接力 | 数据流向/指标关联；dashboard 叙事 |\n| C orb-flyline-relay | A+B 焊接：光斑 surge 与卡脉冲共用落点帧，涨亮 1+1.6×surge、5f 起升 15f 消散 | 暗场高光段；三卡接力收束 |\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| A 光斑 | 峰值 opacity 0.32/0.22/0.18；漂移振幅合计 ≥240px、周期 92–134f 交叉不等 | 同周期漂移读作整体晃动不是\"有机\" |\n| A 卡缘辉光 | box-shadow 0→28px 白 0.25 | 无辉光光斑与卡互不认识 |\n| A 收敛 | 90–120f out-sine 收敛冻结（起始斜率≈0.94 近似连续），末 30f 真静止 | 硬停一帧读作卡死；缓动收敛才有真静止 |\n| B 线体 | 白线 4.5px + 9px 深色衬底；暗场版可去衬底 | 浅底无衬底白线直接消失（demo 判例） |\n| B 光头 | 白点 9px + halo，仅生长期条件挂载 | opacity 到 0 不算摘罩，永不真静止 |\n| B 脉冲 | 描边扩散 out-cubic 6f / 消散线性 12f | 白底禁提亮——用加深罩 0.16 替代 |\n| C 共振 | 落点帧同帧起 surge；卡 8f 亮到 1 + 描边脉冲残余 0.3 | 错 2f 以上共振读不出\"搭腔\" |\n| C 时间轴 | 动作 f98 前结束、光斑 f120 冻结，末 35f 真静止 | 氛围层最后冻，事件层先收 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 暗场段与纸墨浅底主视觉切换成本高——全片暗场段 ≤1 段，进出\n  用 shot-transitions 藏切族垫，别生切明暗\n- 光斑漂移末段**必须**缓动收敛到静止再冻结——匀速漂移直接 clamp\n  是一次可见的急刹（A 式 out-sine 收敛判例）\n- 实战品牌色版：光斑用品牌色对（如蓝紫），灰阶参数只保证明度关系，\n  色相对比要重调\n- B 接力线 ≥3 条时改用 C——纯飞线连打读作大屏模板，共振才有作者感\n- 声音：B/C 每次落点一声轻打点，A 无声（sound-design §4.5）\n\n## 参考实现\ndemos/effects/glow-flyline-moves/\n（FlylineArc.tsx / GlowOrbAmbient.tsx / OrbFlylineRelay.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/effects/glow-flyline-moves/FlylineArc.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/FlylineArc.tsx\n- demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx\n- demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/OrbFlylineRelay.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/effects/glow-flyline-moves/FlylineArc.tsx, demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx, demos/effects/glow-flyline-moves/OrbFlylineRelay.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/effects/glow-flyline-moves/FlylineArc.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/FlylineArc.tsx"
    },
    {
      "path": "demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx"
    },
    {
      "path": "demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/OrbFlylineRelay.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": 152,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/FlylineArc.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/glow-flyline-moves/OrbFlylineRelay.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/effects/glow-flyline-moves/FlylineArc.tsx =====\n// flyline-arc —— 飞线连接\n// FakeDashboard A 微暗化(brightness 0.92)，左上卡→右下卡一条贝塞尔弧线\n// \"打\"过去(22f, out-cubic 生长)，亮点头部领跑、亮头暗尾拖尾；到达帧目标卡\n// 3px+ ink 描边脉冲 + 加深脉冲(白底禁提亮)。随后第二条线接力打到上中卡。\n// 收尾真静止：全部动画在 f86 前结束，之后所有元素冻结。\nimport React, { useId } from 'react';\nimport { useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { FakeDashboard } from '../../_fixtures/Fixtures';\n\ntype Pt = { x: number; y: number };\n\n// 手写 cubic bezier 采样\nconst bez = (p0: Pt, p1: Pt, p2: Pt, p3: Pt, t: number): Pt => {\n  const u = 1 - t;\n  return {\n    x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,\n    y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y,\n  };\n};\n\nconst N = 100; // 采样段数\n\n// 卡片几何（FakeDashboard variant A 实测网格）\nconst CARD_LT = { x: 256, y: 108, w: 524, h: 454, cx: 518, cy: 335 }; // 左上\nconst CARD_RB = { x: 1360, y: 590, w: 524, h: 454, cx: 1622, cy: 817 }; // 右下\nconst CARD_TM = { x: 808, y: 108, w: 524, h: 454, cx: 1070, cy: 335 }; // 上中\n\n// 一条飞线：深色衬底 + 亮头暗尾白线分段 + 光头领跑（条件挂载）\nconst Flyline: React.FC<{\n  frame: number;\n  start: number; // 生长起始帧\n  haloId: string; // 光头径向渐变 ID（父组件按实例生成）\n  p0: Pt; p1: Pt; p2: Pt; p3: Pt;\n}> = ({ frame, start, haloId, p0, p1, p2, p3 }) => {\n  const DUR = 22;\n  if (frame < start) return null;\n  const e = interpolate(frame, [start, start + DUR], [0, 1], {\n    easing: Easing.out(Easing.cubic),\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n  const growing = frame < start + DUR;\n  // 到达后 10f 内尾部亮度线性抹匀 → 之后完全静止\n  const settle = interpolate(frame, [start + DUR, start + DUR + 10], [0, 1], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n\n  const pts: Pt[] = [];\n  const nDrawn = Math.max(2, Math.ceil(e * N) + 1);\n  for (let i = 0; i < nDrawn; i++) {\n    const t = Math.min((i / N), e);\n    pts.push(bez(p0, p1, p2, p3, t));\n  }\n  const head = bez(p0, p1, p2, p3, e);\n  pts[pts.length - 1] = head;\n\n  const underlay = pts.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ');\n\n  // 亮头暗尾：每段按\"离头距离\"给 opacity；到达后 settle 抹匀到 1\n  const segs = [];\n  for (let i = 0; i < pts.length - 1; i++) {\n    const tSeg = Math.min(i / N, e) / Math.max(e, 0.001); // 0 尾 → 1 头\n    const grad = 0.4 + 0.6 * tSeg * tSeg;\n    const op = grad + (1 - grad) * settle;\n    segs.push(\n      <line\n        key={i}\n        x1={pts[i].x} y1={pts[i].y} x2={pts[i + 1].x} y2={pts[i + 1].y}\n        stroke=\"#fafafa\" strokeWidth={4.5} strokeLinecap=\"round\" strokeOpacity={op}\n      />\n    );\n  }\n\n  return (\n    <g>\n      {/* 深色衬底：保证浅底可见 */}\n      <polyline\n        points={underlay} fill=\"none\" stroke=\"#2f2f2f\"\n        strokeWidth={9} strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeOpacity={0.92}\n      />\n      {segs}\n      {/* 光头：仅生长期挂载，摘罩即真静止 */}\n      {growing && (\n        <g>\n          <circle cx={head.x} cy={head.y} r={26} fill={`url(#${haloId})`} />\n          <circle cx={head.x} cy={head.y} r={9} fill=\"#ffffff\" stroke=\"#2f2f2f\" strokeWidth={3} />\n        </g>\n      )}\n    </g>\n  );\n};\n\n// 目标卡脉冲：3px+ ink 描边 + 加深罩(白底不许提亮) —— 条件挂载\nconst CardPulse: React.FC<{\n  frame: number;\n  at: number; // 触发帧\n  rect: { x: number; y: number; w: number; h: number };\n}> = ({ frame, at, rect }) => {\n  if (frame < at || frame > at + 18) return null;\n  // 扩散(起升)用 out-cubic 6f，消散用线性 12f\n  const amp =\n    frame <= at + 6\n      ? interpolate(frame, [at, at + 6], [0, 1], {\n          easing: Easing.out(Easing.cubic),\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n        })\n      : interpolate(frame, [at + 6, at + 18], [1, 0], {\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n        });\n  return (\n    <g>\n      <rect\n        x={rect.x + 2} y={rect.y + 2} width={rect.w - 4} height={rect.h - 4}\n        rx={14} fill={`rgba(0,0,0,${(0.16 * amp).toFixed(3)})`}\n      />\n      <rect\n        x={rect.x + 2} y={rect.y + 2} width={rect.w - 4} height={rect.h - 4}\n        rx={14} fill=\"none\" stroke=\"#2f2f2f\" strokeWidth={4} strokeOpacity={amp}\n      />\n    </g>\n  );\n};\n\nexport const FlylineArc: React.FC = () => {\n  const frame = useCurrentFrame();\n  // 渐变 ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const haloId = `headHalo-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;\n\n  // 时间轴：线1 生长 10–32f → 卡RB脉冲 32–50f；线2 生长 46–68f → 卡TM脉冲 68–86f\n  // f86 后全静止（140f 总长 → 静止 54f ≥ 35f）\n  const L1 = { p0: { x: CARD_LT.cx, y: CARD_LT.cy }, p1: { x: 818, y: 60 }, p2: { x: 1322, y: 380 }, p3: { x: CARD_RB.cx, y: CARD_RB.cy } };\n  const L2 = { p0: { x: CARD_RB.cx, y: CARD_RB.cy }, p1: { x: 1760, y: 560 }, p2: { x: 1360, y: 150 }, p3: { x: CARD_TM.cx, y: CARD_TM.cy } };\n\n  return (\n    <div style={{ width: 1920, height: 1080, position: 'relative', background: '#e0e0de' }}>\n      {/* 背景微暗化 0.92，常量、不动画 */}\n      <div style={{ filter: 'brightness(0.92)' }}>\n        <FakeDashboard variant=\"A\" />\n      </div>\n      <svg\n        width={1920} height={1080} viewBox=\"0 0 1920 1080\"\n        style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}\n      >\n        <defs>\n          <radialGradient id={haloId}>\n            <stop offset=\"0%\" stopColor=\"rgba(0,0,0,0.38)\" />\n            <stop offset=\"55%\" stopColor=\"rgba(0,0,0,0.18)\" />\n            <stop offset=\"100%\" stopColor=\"rgba(0,0,0,0)\" />\n          </radialGradient>\n        </defs>\n        <CardPulse frame={frame} at={32} rect={CARD_RB} />\n        <CardPulse frame={frame} at={68} rect={CARD_TM} />\n        <Flyline frame={frame} start={10} haloId={haloId} {...L1} />\n        <Flyline frame={frame} start={46} haloId={haloId} {...L2} />\n      </svg>\n    </div>\n  );\n};\n\n\n// ===== demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx =====\n// glow-orb-ambient｜暗场光斑呼吸\n// 近黑底上三团大光斑（radial-gradient 亮灰 + blur100）用 seed hash 驱动\n// 多正弦叠加做有机漂移；中央深色描边卡的边缘辉光随最近光斑距离呼吸。\n// 0–20f 光斑淡入，中段正常速度漂移，90–120f 缓动收敛到静止，末 30f 真静止。\nimport React from 'react';\nimport { AbsoluteFill, interpolate, useCurrentFrame, Easing } from 'remotion';\n\n// 库内标准伪随机（帧确定）\nconst h = (n: number) => {\n  const s = Math.sin(n * 127.3) * 43758.5453;\n  return s - Math.floor(s);\n};\n\nconst W = 1920;\nconst H = 1080;\nconst CX = W / 2;\nconst CY = H / 2;\n\ntype Orb = {\n  size: number;      // 直径 500–700\n  peak: number;      // 亮度峰值\n  bx: number; by: number; // 基准中心\n  p1: number; p2: number; // 两个正弦周期 90–140f\n  ax1: number; ax2: number; ay1: number; ay2: number; // 振幅（合计 ≥240px）\n  seed: number;\n};\n\nconst ORBS: Orb[] = [\n  { size: 680, peak: 0.32, bx: 600, by: 400, p1: 96, p2: 134, ax1: 170, ax2: 115, ay1: 160, ay2: 120, seed: 1 },\n  { size: 580, peak: 0.22, bx: 1360, by: 560, p1: 110, p2: 92, ax1: 160, ax2: 120, ay1: 175, ay2: 105, seed: 2 },\n  { size: 500, peak: 0.18, bx: 940, by: 860, p1: 128, p2: 98, ax1: 150, ax2: 125, ay1: 145, ay2: 118, seed: 3 },\n];\n\nconst TAU = Math.PI * 2;\n\nconst orbPos = (o: Orb, t: number) => {\n  const f1 = h(o.seed * 7 + 1) * TAU;\n  const f2 = h(o.seed * 7 + 2) * TAU;\n  const f3 = h(o.seed * 7 + 3) * TAU;\n  const f4 = h(o.seed * 7 + 4) * TAU;\n  const x = o.bx + o.ax1 * Math.sin((TAU * t) / o.p1 + f1) + o.ax2 * Math.sin((TAU * t) / o.p2 + f2);\n  const y = o.by + o.ay1 * Math.sin((TAU * t) / o.p2 + f3) + o.ay2 * Math.sin((TAU * t) / o.p1 + f4);\n  return { x, y };\n};\n\nexport const GlowOrbAmbient: React.FC = () => {\n  const f = useCurrentFrame();\n\n  // 有效时间：0–90f 匀速，90–120f 用 out-sine 减速收敛（起始斜率≈0.94，近似连续），\n  // f≥120 clamp 恒定 => 末 30f 所有位置/阴影完全静止。\n  const t =\n    f <= 90\n      ? f\n      : 90 +\n        interpolate(f, [90, 120], [0, 18], {\n          easing: Easing.out(Easing.sin),\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n        });\n\n  // 淡入 0–20f（在末 30f 之前早已结束）\n  const fadeIn = interpolate(f, [0, 20], [0, 1], {\n    easing: Easing.out(Easing.cubic),\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n\n  const positions = ORBS.map((o) => orbPos(o, t));\n\n  // 卡缘呼吸：最近光斑距离 -> 辉光强度（按光斑峰值加权取最大）\n  let glow = 0;\n  ORBS.forEach((o, i) => {\n    const d = Math.hypot(positions[i].x - CX, positions[i].y - CY);\n    const p = interpolate(d, [180, 720], [1, 0], {\n      extrapolateLeft: 'clamp',\n      extrapolateRight: 'clamp',\n    });\n    glow = Math.max(glow, p * (o.peak / 0.32));\n  });\n  const shadowBlur = 28 * glow;\n  const shadowSpread = 10 * glow;\n  const shadowAlpha = 0.25 * glow;\n\n  return (\n    <AbsoluteFill style={{ background: '#1d1d1b', overflow: 'hidden' }}>\n      {ORBS.map((o, i) => (\n        <div\n          key={i}\n          style={{\n            position: 'absolute',\n            left: positions[i].x - o.size / 2,\n            top: positions[i].y - o.size / 2,\n            width: o.size,\n            height: o.size,\n            borderRadius: '50%',\n            background: `radial-gradient(circle, rgba(232,232,228,${o.peak}) 0%, rgba(232,232,228,${o.peak * 0.5}) 42%, rgba(232,232,228,0) 70%)`,\n            filter: 'blur(100px)',\n            opacity: fadeIn,\n          }}\n        />\n      ))}\n      {/* 中央深色描边卡 */}\n      <div\n        style={{\n          position: 'absolute',\n          left: CX - 280,\n          top: CY - 165,\n          width: 560,\n          height: 330,\n          boxSizing: 'border-box',\n          background: '#262624',\n          border: '1.5px solid #6a6a68',\n          borderRadius: 16,\n          boxShadow: `0 0 ${shadowBlur}px ${shadowSpread}px rgba(255,255,255,${shadowAlpha})`,\n          padding: 32,\n          display: 'flex',\n          flexDirection: 'column',\n          gap: 16,\n        }}\n      >\n        <div style={{ height: 18, width: '55%', background: '#4a4a48', borderRadius: 9 }} />\n        <div style={{ height: 11, width: '82%', background: '#3a3a38', borderRadius: 6 }} />\n        <div style={{ height: 11, width: '68%', background: '#3a3a38', borderRadius: 6 }} />\n        <div style={{ marginTop: 'auto', display: 'flex', gap: 10, alignItems: 'center' }}>\n          <div style={{ width: 28, height: 28, borderRadius: 14, background: '#4a4a48' }} />\n          <div style={{ height: 11, width: 90, background: '#3a3a38', borderRadius: 6 }} />\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n\n// ===== demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx =====\n// orb-flyline-relay｜光斑飞线接力\n// 组合变异 = glow-orb-ambient + flyline-arc。\n// 近黑底（#1d1d1b）+ 两团 blur(100px) 灰亮光斑多正弦漂移当氛围层；\n// 三张深色描边卡三角布局、初始半暗(0.55)。飞线 A→B 落点帧：卡 B 亮起\n// (opacity→1 + 描边亮白脉冲) 且邻近光斑同帧涨亮一拍（组合共振证明点）；\n// 线二 B→C 接力，C 亮起收束。光斑 95–120f out-sine 减速收敛，\n// 全部动画 f120 前结束，末 35f 真静止。\nimport React, { useId } from 'react';\nimport { AbsoluteFill, interpolate, useCurrentFrame, Easing } from 'remotion';\n\n// 库内标准伪随机（帧确定）\nconst h = (n: number) => {\n  const s = Math.sin(n * 127.3) * 43758.5453;\n  return s - Math.floor(s);\n};\n\nconst TAU = Math.PI * 2;\n\ntype Pt = { x: number; y: number };\n\n// 手写 cubic bezier 采样\nconst bez = (p0: Pt, p1: Pt, p2: Pt, p3: Pt, t: number): Pt => {\n  const u = 1 - t;\n  return {\n    x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,\n    y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y,\n  };\n};\n\nconst N = 100;\n\n// ===== 布局：三张 420×250 深色卡呈三角 =====\nconst CARD_W = 420;\nconst CARD_H = 250;\nconst CARDS = {\n  A: { x: 230, y: 170 },   // 左上（center 440, 295）\n  B: { x: 1270, y: 300 },  // 右中（center 1480, 425）\n  C: { x: 700, y: 740 },   // 下中（center 910, 865）\n};\nconst center = (c: { x: number; y: number }) => ({ x: c.x + CARD_W / 2, y: c.y + CARD_H / 2 });\n\n// ===== 光斑（氛围层）=====\ntype Orb = {\n  size: number; peak: number;\n  bx: number; by: number;\n  p1: number; p2: number;\n  ax1: number; ax2: number; ay1: number; ay2: number;\n  seed: number;\n  surgeAt: number; // 与哪个落点帧共振\n};\nconst ORBS: Orb[] = [\n  // 邻近卡 B —— 线1落点(f42)同帧涨亮\n  { size: 720, peak: 0.26, bx: 1500, by: 330, p1: 104, p2: 138, ax1: 130, ax2: 95, ay1: 120, ay2: 92, seed: 1, surgeAt: 42 },\n  // 邻近卡 C —— 线2落点(f76)同帧涨亮\n  { size: 640, peak: 0.2, bx: 900, by: 830, p1: 122, p2: 94, ax1: 125, ax2: 88, ay1: 128, ay2: 90, seed: 2, surgeAt: 76 },\n];\n\nconst orbPos = (o: Orb, t: number) => {\n  const f1 = h(o.seed * 7 + 1) * TAU;\n  const f2 = h(o.seed * 7 + 2) * TAU;\n  const f3 = h(o.seed * 7 + 3) * TAU;\n  const f4 = h(o.seed * 7 + 4) * TAU;\n  const x = o.bx + o.ax1 * Math.sin((TAU * t) / o.p1 + f1) + o.ax2 * Math.sin((TAU * t) / o.p2 + f2);\n  const y = o.by + o.ay1 * Math.sin((TAU * t) / o.p2 + f3) + o.ay2 * Math.sin((TAU * t) / o.p1 + f4);\n  return { x, y };\n};\n\n// 涨亮一拍：起升 5f out-cubic，消散 15f 线性 → 落点帧 +20f 内结束\nconst surge = (frame: number, at: number) => {\n  if (frame < at || frame > at + 20) return 0;\n  return frame <= at + 5\n    ? interpolate(frame, [at, at + 5], [0, 1], {\n        easing: Easing.out(Easing.cubic),\n        extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n      })\n    : interpolate(frame, [at + 5, at + 20], [1, 0], {\n        extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n      });\n};\n\n// ===== 发光飞线：亮头领跑 + 渐隐尾，到达后整线线性消散并摘罩 =====\nconst Flyline: React.FC<{\n  frame: number;\n  start: number; // 生长起始帧\n  haloId: string; // 光头径向渐变 ID（父组件按实例生成）\n  p0: Pt; p1: Pt; p2: Pt; p3: Pt;\n}> = ({ frame, start, haloId, p0, p1, p2, p3 }) => {\n  const DUR = 24;      // 生长\n  const HOLD = 4;      // 到达后停一拍\n  const FADE = 14;     // 消散（线性，帧时间解耦）\n  if (frame < start || frame >= start + DUR + HOLD + FADE) return null; // 条件挂载=摘罩\n\n  const e = interpolate(frame, [start, start + DUR], [0, 1], {\n    easing: Easing.out(Easing.cubic),\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const growing = frame < start + DUR;\n  const fade = interpolate(frame, [start + DUR + HOLD, start + DUR + HOLD + FADE], [1, 0], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n\n  const pts: Pt[] = [];\n  const nDrawn = Math.max(2, Math.ceil(e * N) + 1);\n  for (let i = 0; i < nDrawn; i++) {\n    const t = Math.min(i / N, e);\n    pts.push(bez(p0, p1, p2, p3, t));\n  }\n  const head = bez(p0, p1, p2, p3, e);\n  pts[pts.length - 1] = head;\n\n  const poly = pts.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ');\n\n  // 亮头暗尾：段 opacity 随离头距离衰减（渐隐尾）\n  const segs = [];\n  for (let i = 0; i < pts.length - 1; i++) {\n    const tSeg = Math.min(i / N, e) / Math.max(e, 0.001); // 0 尾 → 1 头\n    const grad = 0.12 + 0.88 * tSeg * tSeg;\n    segs.push(\n      <line\n        key={i}\n        x1={pts[i].x} y1={pts[i].y} x2={pts[i + 1].x} y2={pts[i + 1].y}\n        stroke=\"#f4f4f0\" strokeWidth={5} strokeLinecap=\"round\"\n        strokeOpacity={grad * fade}\n      />\n    );\n  }\n\n  return (\n    <g>\n      {/* 宽幅低透明白 = 辉光衬底（暗底上可见） */}\n      <polyline\n        points={poly} fill=\"none\" stroke=\"#e8e8e4\"\n        strokeWidth={14} strokeLinecap=\"round\" strokeLinejoin=\"round\"\n        strokeOpacity={0.18 * fade}\n      />\n      {segs}\n      {/* 亮点头领跑：仅生长期挂载 */}\n      {growing && (\n        <g>\n          <circle cx={head.x} cy={head.y} r={34} fill={`url(#${haloId})`} />\n          <circle cx={head.x} cy={head.y} r={8} fill=\"#ffffff\" />\n        </g>\n      )}\n    </g>\n  );\n};\n\n// ===== 深色描边卡：半暗 → 落点帧亮起 + 描边亮白脉冲 =====\nconst DarkCard: React.FC<{\n  frame: number;\n  litAt: number; // 亮起帧（Infinity = 一直半暗；A 用 8 表示开场自亮）\n  x: number; y: number; seed: number;\n}> = ({ frame, litAt, x, y, seed }) => {\n  const lit = interpolate(frame, [litAt, litAt + 8], [0, 1], {\n    easing: Easing.out(Easing.cubic),\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const op = 0.55 + 0.45 * lit;\n  // 描边亮白脉冲：起升 6f out-cubic → 消散 16f 线性，残余 0.3 常量\n  const pulse =\n    frame < litAt\n      ? 0\n      : frame <= litAt + 6\n        ? interpolate(frame, [litAt, litAt + 6], [0, 1], {\n            easing: Easing.out(Easing.cubic),\n            extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n          })\n        : interpolate(frame, [litAt + 6, litAt + 22], [1, 0.3], {\n            extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n          });\n  const borderCol = `rgba(${Math.round(90 + 165 * pulse)},${Math.round(90 + 165 * pulse)},${Math.round(88 + 164 * pulse)},1)`;\n  const glow = pulse > 0 ? `0 0 ${24 * pulse}px ${6 * pulse}px rgba(255,255,255,${(0.3 * pulse).toFixed(3)})` : 'none';\n  const titleW = 45 + ((seed * 37) % 40);\n  return (\n    <div\n      style={{\n        position: 'absolute', left: x, top: y, width: CARD_W, height: CARD_H,\n        boxSizing: 'border-box', background: '#262624',\n        border: `1.5px solid ${borderCol}`, borderRadius: 14,\n        boxShadow: glow, opacity: op,\n        padding: 24, display: 'flex', flexDirection: 'column', gap: 12,\n      }}\n    >\n      <div style={{ height: 15, width: `${titleW}%`, background: '#4a4a48', borderRadius: 8 }} />\n      <div style={{ height: 10, width: '80%', background: '#383836', borderRadius: 5 }} />\n      <div style={{ height: 10, width: '62%', background: '#383836', borderRadius: 5 }} />\n      <div style={{ marginTop: 'auto', display: 'flex', gap: 9, alignItems: 'center' }}>\n        <div style={{ width: 24, height: 24, borderRadius: 12, background: '#4a4a48' }} />\n        <div style={{ height: 10, width: 78, background: '#383836', borderRadius: 5 }} />\n      </div>\n    </div>\n  );\n};\n\nexport const OrbFlylineRelay: React.FC = () => {\n  const frame = useCurrentFrame();\n  // 渐变 ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const haloId = `orbHeadHalo-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;\n\n  // 光斑有效时间：0–95f 匀速漂移，95–120f out-sine 减速收敛，f≥120 恒定 → 末 35f 真静止\n  const t =\n    frame <= 95\n      ? frame\n      : 95 +\n        interpolate(frame, [95, 120], [0, 15], {\n          easing: Easing.out(Easing.sin),\n          extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n        });\n\n  // 光斑淡入 0–18f\n  const fadeIn = interpolate(frame, [0, 18], [0, 1], {\n    easing: Easing.out(Easing.cubic),\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n\n  // 时间轴：卡A f8 自亮(发起) → 线1 f18–42 A→B → f42 卡B亮+光斑1涨 →\n  // 线2 f52–76 B→C → f76 卡C亮+光斑2涨 → 全部动画 f98 前结束，光斑 f120 冻结\n  const cA = center(CARDS.A);\n  const cB = center(CARDS.B);\n  const cC = center(CARDS.C);\n  const L1 = { p0: cA, p1: { x: 820, y: 90 }, p2: { x: 1300, y: 180 }, p3: cB };\n  const L2 = { p0: cB, p1: { x: 1620, y: 820 }, p2: { x: 1240, y: 1000 }, p3: cC };\n\n  return (\n    <AbsoluteFill style={{ background: '#1d1d1b', overflow: 'hidden' }}>\n      {/* 氛围层：两团大 blur 光斑，落点帧同帧涨亮（组合共振） */}\n      {ORBS.map((o, i) => {\n        const pos = orbPos(o, t);\n        const s = surge(frame, o.surgeAt);\n        const a = Math.min(0.85, o.peak * (1 + 1.6 * s));\n        return (\n          <div\n            key={i}\n            style={{\n              position: 'absolute',\n              left: pos.x - o.size / 2,\n              top: pos.y - o.size / 2,\n              width: o.size,\n              height: o.size,\n              borderRadius: '50%',\n              background: `radial-gradient(circle, rgba(234,234,230,${a.toFixed(3)}) 0%, rgba(234,234,230,${(a * 0.5).toFixed(3)}) 42%, rgba(234,234,230,0) 70%)`,\n              filter: 'blur(100px)',\n              opacity: fadeIn,\n            }}\n          />\n        );\n      })}\n\n      {/* 三张深色描边卡：A 开场自亮发起，B/C 随落点亮起 */}\n      <DarkCard frame={frame} litAt={8} x={CARDS.A.x} y={CARDS.A.y} seed={1} />\n      <DarkCard frame={frame} litAt={42} x={CARDS.B.x} y={CARDS.B.y} seed={2} />\n      <DarkCard frame={frame} litAt={76} x={CARDS.C.x} y={CARDS.C.y} seed={3} />\n\n      {/* 飞线接力层 */}\n      <svg\n        width={1920} height={1080} viewBox=\"0 0 1920 1080\"\n        style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}\n      >\n        <defs>\n          <radialGradient id={haloId}>\n            <stop offset=\"0%\" stopColor=\"rgba(255,255,255,0.55)\" />\n            <stop offset=\"55%\" stopColor=\"rgba(255,255,255,0.22)\" />\n            <stop offset=\"100%\" stopColor=\"rgba(255,255,255,0)\" />\n          </radialGradient>\n        </defs>\n        <Flyline frame={frame} start={18} haloId={haloId} {...L1} />\n        <Flyline frame={frame} start={52} haloId={haloId} {...L2} />\n      </svg>\n    </AbsoluteFill>\n  );\n};\n\n\n"
}