{
  "slug": "marker-underline-title",
  "title": "Marker underline title",
  "tagline": "After the headline settles, a marker-pen underline draws fast left to right beneath a keyword — a widening stroke, a rough edge, a slight upward slant hugging the letters' baseline.",
  "description": "After the headline settles, a marker-pen underline draws fast left to right beneath a keyword — a widening stroke, a rough edge, a slight upward slant hugging",
  "category": "typography",
  "tags": [
    "shotcraft",
    "remotion",
    "typography"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 3.39,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/marker-underline-title/poster-c06a63b18ea4.jpg",
    "video": "https://gimgs.net/motion/media/marker-underline-title/video-7cb860738192.mp4"
  },
  "variants": [
    {
      "key": "marker-underline-title",
      "label": "marker-underline-title · Marker underline title",
      "description": "After the headline settles, a marker-pen underline draws fast left to right beneath a keyword — a widening stroke, a rough edge, a slight upward slant hugging the letters' baseline.",
      "use": "",
      "video": "https://gimgs.net/motion/media/marker-underline-title/video-7cb860738192.mp4",
      "poster": "https://gimgs.net/motion/media/marker-underline-title/poster-c06a63b18ea4.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/marker-underline-title.html",
  "json_url": "https://gimgs.net/motion/marker-underline-title.json",
  "template_url": "https://gimgs.net/motion/marker-underline-title/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/marker-underline-title.md",
  "updated_at": "2026-09-24T07:40:06.840Z",
  "prompt": "## When to use\n\nEmphasizing a single keyword in a headline (new/free/AI…); a hand-drawn, human-touch brand tone; annotation-style emphasis in body copy.\n\n## Intention\n\nA sudden hand-drawn marker stroke inside a printed headline uses the material contrast to pull one word out of the layout — like someone circling a point with a real pen on a poster. Three crux points: **fast** (one continuous stroke over 8–12f; slower reads as a loading bar), **close** (hugging the letters' baseline; farther away reads as a divider, not emphasis), and **follows the slant** (an italic word's underline must slant slightly up from left to right — drawing it the wrong direction is the most common and most obviously fake mistake).\n\n## Duration & energy\n\n- Duration: Starts 4–8f after the headline settles, the stroke draws over 8–12f, total 1–1.5s\n- Energy: Low (one stroke of emphasis, doesn't steal the headline's spotlight)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n打印体标题里突然出现一笔手绘马克笔，用材质反差把一个词从\n排版里拎出来——像人拿笔在海报上圈重点。命门有三：**快**\n（8–12f 一笔呵成，慢了读作加载条）、**近**（贴着字底，离远了\n读作分隔线不是强调）、**跟字势**（斜体词的划线必须左低右高\n微上斜——斜向画反是最容易犯且一眼假的错，判例见已知坑）。\n\n## 动效核心\n- SVG path 描画（strokeDashoffset 或裁剪窗），从左到右 8–12f，\n  ease-out 微收尾（起笔快收笔略缓＝手劲）\n- 笔形：路径变宽（中段最宽）、边缘毛糙（轻微噪声扰动/粗糙笔刷\n  贴图），首尾略细＝起笔收笔\n- 位置：贴字底（bottom 偏移 ≈ -0.1em），中轴微上斜跟随斜体字势\n- 时机：标题完全落定后 +4~8f 起笔——划线是\"看完标题后的强调\"，\n  与标题同动就抢戏\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 划线时长 | 10f（原片实测节奏；14f 版被裁偏慢） | >14f 读作进度条；<6f 看不见笔迹方向 |\n| 字底距离 | ≈-0.1em（从 -40 收到 -20@1080p 才贴住字势） | 离远读作分隔线；碰到字底降部又乱 |\n| 斜向 | 左低右高微上斜（跟斜体） | **画反（左高右低）一眼假**——返工判例 |\n| 笔宽 | 中段 ~0.12em，首尾 ~0.6x | 等宽读作机器线；差异>2x 读作书法 |\n| 毛糙度 | 边缘轻噪声 | 太糙读作故障；全平滑读作 CSS border |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 与 draw-svg-trace 撞领域（有意单列为两卡）：那张是通用\n  SVG 描画（图标/插画/任意线稿）；本卡专做**文字强调**的马克笔\n  质感（变宽+毛糙+跟字势），做图形描画去用那张\n- 斜向判例：v1 左高右低被比对环节抓出——划线斜向必须跟字势，\n  正体字则水平微抖\n- 残余差距：原片（notion-ai）笔头飞白颗粒感更细碎，实战可加\n  更细的笔刷噪点\n\n## 参考实现\ndemos/typography/marker-underline-title/\n（MarkerUnderlineTitle.tsx）\n原片出处：notion-ai.mp4\n\n## Reference implementation (Remotion)\n\n- demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/marker-underline-title/MarkerUnderlineTitle.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/typography/marker-underline-title/MarkerUnderlineTitle.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/typography/marker-underline-title/MarkerUnderlineTitle.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/marker-underline-title/MarkerUnderlineTitle.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": 102,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/marker-underline-title/MarkerUnderlineTitle.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/typography/marker-underline-title/MarkerUnderlineTitle.tsx =====\n// marker-underline-title —— 白底大标题落定后，斜体 \"new\" 下方一道\n// 马克笔下划线从左到右描画（粗细变化/端头圆润/微歪/边缘毛糙）。\n// 对标 notion-ai.mp4 2.3–3.6s。与库内 draw-svg-trace 撞车，本版做马克笔质感。\nimport React, { useId } from 'react';\nimport { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion';\n\nconst mulberry32 = (a: number) => () => {\n  let t = (a += 0x6d2b79f5);\n  t = Math.imul(t ^ (t >>> 15), t | 1);\n  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n};\n\n// 马克笔笔画：中轴微歪的路径 + 变宽轮廓，一次生成多边形\nconst buildStroke = (len: number, seed: number) => {\n  const rand = mulberry32(seed);\n  const N = 40;\n  const top: string[] = [];\n  const bot: string[] = [];\n  // 预生成微歪偏移（低频）与毛糙（高频）\n  const wob = Array.from({ length: N + 1 }, () => rand() - 0.5);\n  for (let i = 0; i <= N; i++) {\n    const t = i / N;\n    const x = t * len;\n    // 中轴：跟随斜体微上斜（左低右高，对照截图）+ 缓波\n    const mid = 19 - t * 9 + Math.sin(t * Math.PI * 1.6 + 0.4) * 2.6 + wob[i] * 1.6;\n    // 宽度：起笔略细→中段饱满→收笔收尖，加高频毛糙\n    const wBase = 14 + Math.sin(t * Math.PI) * 6 - Math.max(0, t - 0.86) * 46;\n    const w = Math.max(2.2, wBase + wob[i] * 3);\n    top.push(`${x.toFixed(1)},${(mid - w / 2).toFixed(1)}`);\n    bot.push(`${x.toFixed(1)},${(mid + w / 2).toFixed(1)}`);\n  }\n  return `M${top.join('L')}L${bot.reverse().join('L')}Z`;\n};\n\nexport const MarkerUnderlineTitle: React.FC = () => {\n  const frame = useCurrentFrame();\n  // clipPath ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const revealId = `reveal-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;\n  const LEN = 252;\n\n  // 标题落定：整块从下方 30px 弹入（ease-out），24 帧内完成\n  const enter = interpolate(frame, [0, 22], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const eo = 1 - Math.pow(1 - enter, 3);\n  const titleY = (1 - eo) * 36;\n  const titleOp = Math.min(1, enter * 1.6);\n\n  // 下划线：标题落定停一拍后描画，10 帧从左到右（提速一档），ease-out\n  const draw = interpolate(frame, [32, 42], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const drawE = 1 - Math.pow(1 - draw, 2.2);\n  const path = buildStroke(LEN, 77);\n\n  return (\n    <AbsoluteFill style={{ background: '#f4f4f2', alignItems: 'center', justifyContent: 'center' }}>\n      <div style={{\n        opacity: titleOp, transform: `translateY(${titleY}px)`,\n        fontFamily: '-apple-system, \"Helvetica Neue\", Arial, sans-serif',\n        fontWeight: 700, fontSize: 118, color: '#191919',\n        textAlign: 'center', lineHeight: 1.12, letterSpacing: '-0.02em',\n      }}>\n        <div>\n          Meet the{' '}\n          <span style={{ fontStyle: 'italic', position: 'relative', display: 'inline-block' }}>\n            new\n            {/* 马克笔下划线：clip 从左到右揭示，保留笔形本身的粗细变化 */}\n            <svg\n              width={LEN} height={44} viewBox={`0 0 ${LEN} 44`}\n              style={{ position: 'absolute', left: -12, bottom: -20, overflow: 'visible' }}\n            >\n              <defs>\n                <clipPath id={revealId}>\n                  <rect x={0} y={-20} width={drawE * (LEN + 6)} height={60} />\n                </clipPath>\n              </defs>\n              {draw > 0 && (\n                <path d={path} fill=\"#111111\" clipPath={`url(#${revealId})`} />\n              )}\n            </svg>\n          </span>\n        </div>\n        <div>Notion AI</div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n\n"
}