{
  "slug": "brand-frame-snap",
  "title": "Brand Frame Snap",
  "tagline": "A brand-color frame grammar — a thick solid frame grows before the content to enclose the whole screen, with the screen recording dropping in; on mode switch the frame hard-flips color on the same frame as the layout inside swaps.",
  "description": "A brand-color frame grammar — a thick solid frame grows before the content to enclose the whole screen, with the screen recording dropping in; on mode switch ",
  "category": "effects",
  "tags": [
    "shotcraft",
    "remotion",
    "effects",
    "transition"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.39,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/brand-frame-snap/poster-c8cc4da001f2.jpg",
    "video": "https://gimgs.net/motion/media/brand-frame-snap/video-b1011d7d5d1e.mp4"
  },
  "variants": [
    {
      "key": "brand-frame-snap",
      "label": "brand-frame-snap",
      "description": "A brand-color frame grammar — a thick solid frame grows before the content to enclose the whole screen, with the screen recording dropping in; on mode switch the frame hard-flips color on the same frame as the layout inside swaps.",
      "use": "",
      "video": "https://gimgs.net/motion/media/brand-frame-snap/video-b1011d7d5d1e.mp4",
      "poster": "https://gimgs.net/motion/media/brand-frame-snap/poster-c8cc4da001f2.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/brand-frame-snap.html",
  "json_url": "https://gimgs.net/motion/brand-frame-snap.json",
  "template_url": "https://gimgs.net/motion/brand-frame-snap/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/brand-frame-snap.md",
  "updated_at": "2026-09-24T07:39:46.170Z",
  "prompt": "## When to use\n\nA whole-film wrapper for dual-mode/dual-chapter product films (blue = mode A, green = mode B color coding); brand-wrapping real screen-recording footage.\n\n## Intention\n\nRaw screen recording filling the frame has no brand presence, and slapping a logo watermark on it feels cheap. This card \"mats\" the recording with a thick brand-color frame: the frame appears before the content (frame first, footage second, for ritual weight), and afterward the frame color becomes a semantic code across the film — blue for Design, green for Dev Mode. On a chapter switch, the whole frame hard-flips color within a single frame, no gradient — a hard color cut is the visual equivalent of a \"gear shift\" sound effect; the layout inside switches A→B and the corner badge/label text change on the same frame, and all three landing together nails \"mode switched.\" Division of labor with theme-switch-moves: that one changes the UI's own skin (with a sweeping boundary), this one changes the \"frame\" wrapper layer (same-frame, no boundary) — the semantics are mode/chapter switch, not theme switch.\n\n## Duration & energy\n\n- Duration: One color flip ~4.3s (130f); the frame itself can stay on screen the whole film.\n- Energy: Medium (the color-flip instant is high, the rest is a quiet wrapper layer).\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n真实录屏直接铺满屏没有品牌存在感，加 logo 水印又廉价。本卡用一圈\n粗品牌色画框把录屏\"装裱\"起来：画框**先于内容出现**（先立画框再放\n画，仪式感），之后画框颜色成为全片的语义编码——蓝=Design、绿=Dev\nMode。章节切换时整圈画框一帧内硬翻色，无渐变，颜色硬切就是\"换挡\"\n声效的视觉等价物；窗内布局同帧 A→B、角标与徽标同步换字，三处同帧\n共振把\"模式切换\"砸实。与 theme-switch-moves 分工：那换的是 UI 本体\n肤色（有扫过边界），本卡换的是\"画框\"这个包装层（同帧无边界），\n语义是模式/章节切换不是主题切换。\n\n## 动效核心\n- 画框用 4 条实体色带（非 border 属性），厚度 0→44px ease-out 18f\n  长出；翻色 = 同一帧换 background，帧确定\n- 录屏窗口 spring(damping 16) 从下方 560px + scale 0.82→1 落进框内\n  （帧 14 起，画框长出中段——框未成形窗先到会读作框追窗）\n- 翻色帧三件事同帧：画框换色 + 窗内布局 A→B 硬切 + 框带角标/窗口\n  徽标换字（DESIGN→DEV MODE）\n- 翻色瞬间叠 2 帧白闪脉冲（opacity 0.55 起阶梯衰减）+ 画框厚度阻尼\n  弹跳（exp(-0.22t)·cos(0.9t)·10px），给\"换挡\"打击感\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 画框厚度 | 44px（1080p） | 太薄读作描边不读作\"装裱\"；>60px 开始吃内容区 |\n| 画框生长 | 18f ease-out，先于窗口 14f | 画框必须先立——内容先到画框后包读作补救 |\n| 窗口落位 | spring(damping 16, stiffness 110)，y 560→0 + scale 0.82→1 | 落进\"框内\"是语义关键，落点必须完全在框带内侧 |\n| 翻色时刻 | demo 78f；停一拍（窗口落定后 ≥30f）再翻 | 落定没坐稳就翻，观众还没建立\"蓝=当前模式\"的编码 |\n| 白闪脉冲 | 2–3f，0.55→0.19 阶梯 | 帮眼睛登记\"这一帧变了\"；>4f 抢了 cel-flash 的活 |\n| 厚度弹跳 | exp 衰减 cos，初幅 10px | 纯硬切略干；弹跳是\"挡位卡进去\"的机械回馈 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 翻色必须真同帧：画框、窗内布局、角标文字三者错开哪怕 2f，\"换挡\"就散成三个小动画\n- 颜色编码要全片守约：蓝框段落里不许出现绿色主 UI 元素，编码被稀释后翻色就只是换色\n- 画框是包装层不是主角：全片翻色 ≤2 次，翻多了读作霓虹灯（P4 克制原则）\n- 两个模式的品牌色对比度要够（demo 蓝 #3E7BFA / 绿 #1BC47D）；同明度邻近色翻了等于没翻\n- 声音：翻色帧钉一声干脆的机械 click/换挡声，白闪与声同帧；画框生长段可静\n\n## 参考实现\ndemos/effects/brand-frame-snap/\n（BrandFrameSnap.tsx）\n原片出处：figma-devmode 0:28–0:32 + 0:43–0:47\n\n## Reference implementation (Remotion)\n\n- demos/effects/brand-frame-snap/BrandFrameSnap.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/brand-frame-snap/BrandFrameSnap.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/brand-frame-snap/BrandFrameSnap.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/brand-frame-snap/BrandFrameSnap.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/brand-frame-snap/BrandFrameSnap.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": 132,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/brand-frame-snap/BrandFrameSnap.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/brand-frame-snap/BrandFrameSnap.tsx =====\n// brand-frame-snap —— figma-devmode 0:28–0:32 (brand-frame-snap) + 0:43–0:47 (frame-color-flip)\n// 一圈粗品牌色画框先于内容出现包住全屏 → 灰阶\"录屏窗口\"在框内落位 →\n// 停一拍 → 画框整圈蓝→绿同帧硬翻色，窗口内容同帧换布局。\n// 一个 borderColor 完成章节导航/状态提示/品牌露出。\nimport React from 'react';\nimport { useCurrentFrame, spring, interpolate } from 'remotion';\nimport { G, FakeDashboard } from '../../_fixtures/Fixtures';\n\nconst FPS = 30;\nconst FIGMA_BLUE = '#3E7BFA';\nconst DEV_GREEN = '#1BC47D';\nconst FLIP_FRAME = 78; // 同帧硬翻色时刻\n\nconst clamp01 = (t: number) => Math.min(1, Math.max(0, t));\nconst easeOut = (t: number) => 1 - Math.pow(1 - t, 3);\n\nexport const BrandFrameSnap: React.FC = () => {\n  const f = useCurrentFrame();\n  const mode: 'design' | 'dev' = f < FLIP_FRAME ? 'design' : 'dev';\n  const frameColor = mode === 'design' ? FIGMA_BLUE : DEV_GREEN;\n\n  // 1) 画框先登场：厚度从 0 长到 44px（ease-out，前 18 帧）\n  const frameGrow = easeOut(clamp01(f / 18));\n  const frameW = 44 * frameGrow;\n\n  // 2) 窗口在框内落位：从下方 + 略缩，弹簧弹入（帧 14 起）\n  const drop = spring({ frame: f - 14, fps: FPS, config: { damping: 16, stiffness: 110, mass: 1 } });\n  const winY = interpolate(drop, [0, 1], [560, 0]);\n  const winS = interpolate(drop, [0, 1], [0.82, 1]);\n  const winO = interpolate(drop, [0, 0.25], [0, 1], { extrapolateRight: 'clamp' });\n\n  // 3) 翻色瞬间给 2 帧白闪脉冲 + 画框轻微厚度弹跳，强化\"换挡\"\n  const sinceFlip = f - FLIP_FRAME;\n  const flash = sinceFlip >= 0 && sinceFlip < 3 ? 0.55 - sinceFlip * 0.18 : 0;\n  const snapPulse = sinceFlip >= 0 ? Math.exp(-sinceFlip * 0.22) * Math.cos(sinceFlip * 0.9) * 10 : 0;\n\n  // 模式标签\n  const label = mode === 'design' ? 'DESIGN' : 'DEV MODE';\n\n  return (\n    <div style={{ width: 1920, height: 1080, background: '#161618', position: 'relative', overflow: 'hidden' }}>\n      {/* 框内内容区 */}\n      <div style={{\n        position: 'absolute', inset: frameW + snapPulse, background: G.bg,\n        overflow: 'hidden', borderRadius: 8,\n      }}>\n        {/* 录屏窗口（带标题栏的窗口卡）落位 */}\n        <div style={{\n          position: 'absolute', left: '50%', top: '50%',\n          width: 1560, height: 830,\n          transform: `translate(-50%, -50%) translateY(${winY}px) scale(${winS})`,\n          opacity: winO,\n          borderRadius: 16, overflow: 'hidden',\n          boxShadow: '0 24px 70px rgba(0,0,0,0.28)',\n          border: `2px solid ${G.border}`, background: G.panel,\n        }}>\n          {/* 窗口标题栏 */}\n          <div style={{\n            height: 52, background: '#e9e9e7', borderBottom: `2px solid ${G.line}`,\n            display: 'flex', alignItems: 'center', gap: 10, padding: '0 22px', boxSizing: 'border-box',\n          }}>\n            {[0, 1, 2].map((i) => (\n              <div key={i} style={{ width: 16, height: 16, borderRadius: 8, background: G.bar }} />\n            ))}\n            <div style={{ marginLeft: 18, height: 12, width: 260, background: G.line, borderRadius: 6 }} />\n            {/* 模式徽标：随画框同帧换色换字 */}\n            <div style={{\n              marginLeft: 'auto', background: frameColor, color: '#fff',\n              fontFamily: 'Helvetica, Arial, sans-serif', fontWeight: 800, fontSize: 15,\n              letterSpacing: 1.5, padding: '6px 16px', borderRadius: 8,\n              opacity: winO,\n            }}>\n              {label}\n            </div>\n          </div>\n          {/* 窗口内容：翻色同帧换布局 A→B */}\n          <div style={{ transform: 'scale(0.81)', transformOrigin: '0 0', width: 1920, height: 1080 }}>\n            <FakeDashboard variant={mode === 'design' ? 'A' : 'B'} />\n          </div>\n        </div>\n      </div>\n\n      {/* 品牌色画框：用 4 条实体边而非 border，翻色是纯 background 同帧硬切 */}\n      {([\n        { left: 0, top: 0, right: 0, height: frameW + snapPulse },\n        { left: 0, bottom: 0, right: 0, height: frameW + snapPulse },\n        { left: 0, top: 0, bottom: 0, width: frameW + snapPulse },\n        { right: 0, top: 0, bottom: 0, width: frameW + snapPulse },\n      ] as React.CSSProperties[]).map((pos, i) => (\n        <div key={i} style={{ position: 'absolute', background: frameColor, ...pos }} />\n      ))}\n\n      {/* 画框上的模式角标（左上，嵌在框带里） */}\n      <div style={{\n        position: 'absolute', left: 70, top: 0, height: frameW + snapPulse,\n        display: 'flex', alignItems: 'center',\n        fontFamily: 'Helvetica, Arial, sans-serif', fontWeight: 800,\n        fontSize: 22, letterSpacing: 3, color: '#ffffff',\n        opacity: frameGrow,\n      }}>\n        {label}\n      </div>\n\n      {/* 翻色白闪脉冲 */}\n      {flash > 0 && (\n        <div style={{ position: 'absolute', inset: 0, background: '#ffffff', opacity: flash }} />\n      )}\n    </div>\n  );\n};\n\n\n"
}