{
  "slug": "print-texture-transitions",
  "title": "Print texture transitions",
  "tagline": "A print-textured transition: ink-bleed-reveal, where feathered bleeding edges soak in and consume the old scene.",
  "description": "A print-textured transition: ink-bleed-reveal, where feathered bleeding edges soak in and consume the old scene.",
  "category": "transition",
  "tags": [
    "shotcraft",
    "remotion",
    "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/print-texture-transitions/poster-11c36a22ebf3.jpg",
    "video": "https://gimgs.net/motion/media/print-texture-transitions/video-8237a62c8ec5.mp4"
  },
  "variants": [
    {
      "key": "ink-bleed-reveal",
      "label": "ink-bleed-reveal · Ink-bleed reveal",
      "description": "A print-textured transition — ink-bleed-reveal, where feathered bleeding edges soak into and consume the old scene.",
      "use": "Old scene → new scene replacement; a handwritten, narrative tone",
      "video": "https://gimgs.net/motion/media/print-texture-transitions/video-8237a62c8ec5.mp4",
      "poster": "https://gimgs.net/motion/media/print-texture-transitions/poster-11c36a22ebf3.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/print-texture-transitions.html",
  "json_url": "https://gimgs.net/motion/print-texture-transitions.json",
  "template_url": "https://gimgs.net/motion/print-texture-transitions/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/print-texture-transitions.md",
  "updated_at": "2026-09-24T07:40:15.478Z",
  "prompt": "## When to use\n\nA paper-and-ink-aesthetic seam for scene changes; a third family alongside the shot-transitions six and the transition-travel three — a \"medium development\"-type transition\n\n## Intention\n\nThe transition vocabulary already has hand-off (the shot-transitions six) and travel (transition-travel), both spatial/optical metaphors. This style opens a third family: the **medium metaphor** — the new image doesn't \"arrive,\" it's \"printed in,\" drawing directly on the library's paper-and-ink physical imagination: like a drop of ink landing on rice paper and bleeding out to consume the old scene, organic and hand-written in feel. A natural fit for films with a paper/ink tonality, and another \"medium family\" seam alongside light-leak-burn (accent-color light leak).\n\n## Duration & energy\n\n- Duration: 4–4.5s (bleed 55–80f + static hold ≥30f)\n- Energy: Medium (gradual development, no impact beat)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n转场语言已有交棒（shot-transitions 六式）和穿越（transition-travel）\n，都是空间/光学隐喻。本式开第三族：**介质隐喻**——新画面不是\"到来\"\n而是\"印染\"出来的，直接调用库内纸墨审美的物理想象：像一滴墨落在\n宣纸上洇开吃掉旧景，有机、书写感。天然亲和 paper/ink 调性的片子，\n是 light-leak-burn（强调色漏光）之外的另一个\"介质系\"接缝选择。\n\n## 单式选型\n| 式 | 做法 | 适用 |\n|----|------|------|\n| B ink-bleed-reveal 墨渗揭示 | 新页透过须状渗边的有机圆形遮罩从落墨点洇开，快慢不匀吃掉旧景 | 旧景→新景替换；书写感、叙事调性 |\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 渗边 | SVG mask 白圆挂 `feTurbulence(fractalNoise, baseFrequency 0.02, octaves 3, seed 固定)` + `feDisplacementMap scale 60→160 随帧涨` | filter 只揉遮罩形状、内容始终清晰；seed 必须写死（确定性） |\n| 洇开 | 半径 [20,98f]→[0,1450px] Easing.out(quad)，乘 `1+0.08·sin(0.32f)·env` 快慢不匀扰动（env 末 20f 衰减归零） | 匀速圆扩张读作普通 iris——扰动即\"墨的脾气\" |\n| 收尾摘罩 | 洇满后（帧 ~100）摘掉 SVG mask 直接铺新景 | feTurbulence 有亚像素抖动，不摘则结尾永不真静止（本批实渲判例） |\n| 收尾 | 新景就位后真静止 ≥30f | R1 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 一缝一式规矩同 shot-transitions：介质转场不与白闪/震屏叠加；\n  本式与 light-leak-burn 介质系同片合计 ≤2 次，多了片子读作印刷厂宣传\n- 洇开中段旧景内容会透过噪声孔洞碎片式短暂露出——读作墨渍肌理\n  属可接受有机效果，但旧景若是人脸/logo 等高辨识元素会读作故障，避开\n- 慢转场（55–80f 动作段），高节奏连打段落别用——那里去 E 式甩镜\n\n## 参考实现\ndemos/transition/print-texture-transitions/\n（InkBleedReveal.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/transition/print-texture-transitions/InkBleedReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/print-texture-transitions/InkBleedReveal.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/transition/print-texture-transitions/InkBleedReveal.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/transition/print-texture-transitions/InkBleedReveal.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/print-texture-transitions/InkBleedReveal.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/transition/print-texture-transitions/InkBleedReveal.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/transition/print-texture-transitions/InkBleedReveal.tsx =====\n// 墨渗揭示（ink-bleed-reveal）——水墨转场（轨道遮罩法）。\n// 旧景：G.bg 纸底 + 居中 TitleBlock \"BEFORE\"；新景 FakeDashboard(A) 放进\n// SVG <foreignObject>，套 <mask>：中心偏左上 (800,420) 的白圆当轨道遮罩。\n// 圆本身套 feTurbulence(baseFrequency 0.02, octaves 3, seed 7 固定) +\n// feDisplacementMap（scale 60→160 随帧涨）造须状渗边——filter 只作用在\n// mask 形状上，新景内容始终清晰。帧 0–20 hold 旧景；帧 20–98 半径\n// 0→1450（Easing.out(quad)）再叠 ±8% 低频正弦扰动（帧 78–98 扰动衰减到 0，\n// 洇满全屏）；帧 100–130 摘掉 mask 直接铺新景，真静止 30f。\nimport React, { useId } from 'react';\nimport { useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';\n\nexport const InkBleedReveal: React.FC = () => {\n  const frame = useCurrentFrame();\n  // 滤镜/mask ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');\n  const bleedId = `inkBleed-${uid}`;\n  const maskId = `inkMask-${uid}`;\n\n  // 墨滴落点：画面中心偏左上\n  const cx = 800;\n  const cy = 420;\n\n  // 基础半径：帧 20–98，0 → 1450px（最远角 ~1300px + 渗边位移余量 150px）\n  const baseR = interpolate(frame, [20, 98], [0, 1450], {\n    easing: Easing.out(Easing.quad),\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n\n  // ±8% 低频正弦扰动 = 快慢不匀的洇开；帧 78–98 幅度衰减到 0，保证吃满后能真静止\n  const wobbleEnv = interpolate(frame, [78, 98], [1, 0], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n  const r = Math.max(0, baseR * (1 + 0.08 * Math.sin(frame * 0.32) * wobbleEnv));\n\n  // 渗边发散度：displacement scale 60 → 160（边缘越洇越散、指尖分叉越长）\n  const dispScale = interpolate(frame, [20, 98], [60, 160], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n\n  // 帧 100 起 mask 已全白：摘掉 SVG 直接铺新景，确保结尾像素级真静止\n  const settled = frame >= 100;\n\n  return (\n    <div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>\n      {/* 旧景：纸底 + BEFORE 标题 */}\n      <div style={{ position: 'absolute', inset: 0, background: G.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n        <TitleBlock text=\"BEFORE\" size={120} />\n      </div>\n\n      {settled ? (\n        <div style={{ position: 'absolute', inset: 0 }}>\n          <FakeDashboard variant=\"A\" />\n        </div>\n      ) : (\n        <svg\n          width={1920}\n          height={1080}\n          viewBox=\"0 0 1920 1080\"\n          style={{ position: 'absolute', inset: 0, display: 'block' }}\n        >\n          <defs>\n            {/* filter 只挂在 mask 的圆上——揉的是遮罩边，不是画面内容 */}\n            <filter id={bleedId} x=\"-40%\" y=\"-40%\" width=\"180%\" height=\"180%\">\n              <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.02\" numOctaves=\"3\" seed=\"7\" result=\"noise\" />\n              <feDisplacementMap in=\"SourceGraphic\" in2=\"noise\" scale={dispScale} xChannelSelector=\"R\" yChannelSelector=\"G\" />\n            </filter>\n            <mask id={maskId} maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"1920\" height=\"1080\">\n              <rect x=\"0\" y=\"0\" width=\"1920\" height=\"1080\" fill=\"black\" />\n              {r > 0.5 && <circle cx={cx} cy={cy} r={r} fill=\"white\" filter={`url(#${bleedId})`} />}\n            </mask>\n          </defs>\n          <g mask={`url(#${maskId})`}>\n            <foreignObject x=\"0\" y=\"0\" width=\"1920\" height=\"1080\">\n              <FakeDashboard variant=\"A\" />\n            </foreignObject>\n          </g>\n        </svg>\n      )}\n    </div>\n  );\n};\n\n\n"
}