{
  "id": "motion:motion-svg-line-draw",
  "slug": "motion-svg-line-draw",
  "kind": "motion",
  "title": {
    "ja": "SVGラインドロー",
    "en": "SVG line drawing"
  },
  "aiName": "SVG line drawing",
  "summary": {
    "ja": "線が描かれていく",
    "en": "Lines draw themselves as the page scrolls"
  },
  "number": 13,
  "group": "scroll-linked",
  "techniques": [
    "scroll-progress",
    "svg"
  ],
  "rules": [
    {
      "ja": "線は pathLength=1 で描き、描画の進みは --p だけで決める",
      "en": "Draw each line with pathLength=1 and drive the drawn length from --p alone"
    },
    {
      "ja": "スクロールを戻すと線も戻る",
      "en": "Scrolling back up undraws the lines"
    },
    {
      "ja": "動きを減らす設定では、描き終わった形を表示する",
      "en": "With reduced motion, show the lines fully drawn"
    }
  ],
  "reproduction": {
    "prompt": {
      "ja": "SVGラインドロー — SVG line drawing\n\n見出しの下に、3 本の SVG の曲線を描く。3 本は p の 0〜0.36、0.32〜0.68、0.64〜1.00 の区間で順に描かれ、隣とわずかに重なる。\n各線は 0% から 100% まで、スクロール量に合わせて左から右へ描かれる。\n線は accent の色、太さ 4px、端は丸くし、背景には rule の色で水平のグリッド線を 3 本引く。\n見出しと線は画面上端に固定し、300vh の区画を下へスクロールする間だけ描画が進む。上へ戻すと線も戻る。\nCSS の stroke-dasharray と stroke-dashoffset だけで実装し、ライブラリは使わない。\n\n外すと崩れる点:\n- 線は pathLength=1 で描き、描画の進みは --p だけで決める\n- スクロールを戻すと線も戻る\n- 動きを減らす設定では、描き終わった形を表示する\n\n動く実物（1 ファイルで完結する HTML）: https://design-vault.pages.dev/designs/motion-svg-line-draw/demo.html",
      "en": "SVG line drawing — SVG line drawing\n\nDraw three SVG curves under a heading. Each line is drawn from 0% to 100% as the scroll progress p moves through its own window: 0 to 0.36, 0.32 to 0.68, and 0.64 to 1.00, so neighbouring windows overlap slightly.\nThe lines move from left to right in step with the scroll amount.\nStrokes use the accent colour at 4px with round caps, over three horizontal grid lines in the rule colour.\nThe heading and the drawing stay fixed at the top of a 300vh section, and the drawing advances only while scrolling down; scrolling back up undraws the lines.\nImplement with CSS stroke-dasharray and stroke-dashoffset only, with no libraries.\n\nDo not drop:\n- Draw each line with pathLength=1 and drive the drawn length from --p alone\n- Scrolling back up undraws the lines\n- With reduced motion, show the lines fully drawn\n\nA working example (one self-contained HTML file): https://design-vault.pages.dev/designs/motion-svg-line-draw/demo.html"
    },
    "code": {
      "url": "https://design-vault.pages.dev/designs/motion-svg-line-draw/demo.html",
      "html": "<!doctype html>\n<html lang=\"ja\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>13 SVGラインドロー — SVG line drawing</title>\n<style>\n/* dv-motion-base:start */\n:root {\n  --ground: #FFFFFF;\n  --surface: #F3F5F8;\n  --ink: #14181F;\n  --muted: #5A6472;\n  --accent: #1F5FA8;\n  --rule: #D8DDE4;\n  --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--ground));\n  --accent-mid: color-mix(in srgb, var(--accent) 45%, var(--ground));\n  --shade: color-mix(in srgb, var(--ink) 14%, transparent);\n\n  --font: \"Zen Kaku Gothic New\", \"Hiragino Sans\", \"Noto Sans JP\", \"Yu Gothic\", system-ui, sans-serif;\n  --mono: ui-monospace, \"SF Mono\", Menlo, Consolas, monospace;\n\n  --t-label: 13px;\n  --t-body: 16px;\n  --t-lead: 20px;\n  --t-h2: 32px;\n  --t-h1: clamp(40px, 7vw, 72px);\n\n  --s1: 4px;\n  --s2: 8px;\n  --s3: 16px;\n  --s4: 24px;\n  --s5: 40px;\n  --s6: 64px;\n  --s7: 104px;\n\n  --radius: 12px;\n  --ease: cubic-bezier(.2, .7, .2, 1);\n  --dur: .7s;\n  --measure: 1080px;\n}\n*, *::before, *::after { box-sizing: border-box; }\nhtml { -webkit-text-size-adjust: 100%; }\nbody {\n  margin: 0;\n  background: var(--ground);\n  color: var(--ink);\n  font-family: var(--font);\n  font-size: var(--t-body);\n  line-height: 1.7;\n  font-feature-settings: \"palt\";\n  overflow-x: hidden;\n}\nh1, h2, h3, p, ul, ol, figure { margin: 0; padding: 0; }\nul, ol { list-style: none; }\n.dv-wrap { width: min(100% - 2 * var(--s4), var(--measure)); margin-inline: auto; }\n.dv-label {\n  min-height: 72vh;\n  display: grid;\n  align-content: center;\n  gap: var(--s3);\n  padding-block: var(--s6);\n  border-bottom: 1px solid var(--rule);\n}\n.dv-num { font-family: var(--mono); font-size: var(--t-lead); font-weight: 700; color: var(--accent); letter-spacing: .04em; }\n.dv-name { font-size: var(--t-h1); font-weight: 700; line-height: 1.15; letter-spacing: .01em; }\n.dv-ai { font-family: var(--mono); font-size: var(--t-lead); color: var(--muted); }\n.dv-desc { font-size: var(--t-lead); color: var(--ink); }\n.dv-hint { font-size: var(--t-label); color: var(--muted); letter-spacing: .12em; margin-top: var(--s4); }\n.dv-stage { padding-block: var(--s7); }\n.dv-h2 { font-size: var(--t-h2); font-weight: 700; line-height: 1.3; }\n.dv-lead { font-size: var(--t-lead); color: var(--muted); }\n.dv-eyebrow { font-family: var(--mono); font-size: var(--t-label); color: var(--accent); letter-spacing: .12em; text-transform: uppercase; }\n.dv-card { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s4); }\n.dv-ph {\n  border-radius: var(--radius);\n  background: linear-gradient(135deg, var(--accent-soft), var(--accent-mid) 60%, var(--accent));\n  aspect-ratio: 16 / 9;\n}\n.dv-ph--alt { background: linear-gradient(200deg, var(--surface), var(--accent-soft) 55%, var(--accent-mid)); }\n.dv-ph--ink { background: linear-gradient(160deg, var(--ink), color-mix(in srgb, var(--ink) 55%, var(--accent))); }\n.dv-end {\n  min-height: 60vh;\n  display: grid;\n  place-items: center;\n  border-top: 1px solid var(--rule);\n  color: var(--muted);\n  font-family: var(--mono);\n  font-size: var(--t-label);\n  letter-spacing: .12em;\n}\n/* dv-motion-base:end */\n\n/* ---- この演出だけの CSS。色は var(--…) か color-mix() のみ ---- */\n.draw-zone { height: 300vh; }\n.draw-stick { position: sticky; top: 0; height: 100vh; display: grid; place-items: center; }\n.draw-stick .dv-wrap { display: grid; gap: var(--s4); }\n.draw-svg { display: block; width: 100%; height: auto; overflow: visible; }\n.draw-grid { fill: none; stroke: var(--rule); stroke-width: 2; }\n.draw-line {\n  fill: none;\n  stroke: var(--accent);\n  stroke-width: 4;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  stroke-dasharray: 1;\n  stroke-dashoffset: calc(1 - clamp(0, (var(--p) - var(--from)) / var(--span), 1));\n}\n.draw-caption { font-family: var(--mono); font-size: var(--t-label); color: var(--muted); letter-spacing: .12em; }\n\n@media (prefers-reduced-motion: reduce) {\n  .draw-line { stroke-dashoffset: 0; }\n}\n</style>\n</head>\n<body data-motion-number=\"13\">\n<header class=\"dv-label\">\n  <div class=\"dv-wrap\">\n    <p class=\"dv-num\">13</p>\n    <h1 class=\"dv-name\">SVGラインドロー</h1>\n    <p class=\"dv-ai\">SVG line drawing</p>\n    <p class=\"dv-desc\">線が描かれていく</p>\n    <p class=\"dv-hint\">SCROLL ↓</p>\n  </div>\n</header>\n\n<main class=\"dv-stage\">\n  <section class=\"draw-zone\" data-progress=\"pin\">\n    <div class=\"draw-stick\">\n      <div class=\"dv-wrap\">\n        <h2 class=\"dv-h2\">見出しが入ります</h2>\n        <svg class=\"draw-svg\" viewBox=\"0 0 800 400\" aria-hidden=\"true\">\n          <path class=\"draw-grid\" d=\"M0 100H800M0 200H800M0 300H800\"/>\n          <path class=\"draw-line\" pathLength=\"1\" style=\"--from: 0; --span: .36\" d=\"M20 320C120 300 160 120 260 180S380 330 460 210S640 60 780 90\"/>\n          <path class=\"draw-line\" pathLength=\"1\" style=\"--from: .32; --span: .36\" d=\"M20 90C160 60 220 260 360 240S560 40 780 300\"/>\n          <path class=\"draw-line\" pathLength=\"1\" style=\"--from: .64; --span: .36\" d=\"M20 200C140 140 260 340 400 200S640 120 780 200\"/>\n        </svg>\n        <p class=\"draw-caption\">Section 1 — 本文のダミーです</p>\n      </div>\n    </div>\n  </section>\n</main>\n\n<footer class=\"dv-end\">END — 13 SVGラインドロー</footer>\n\n<script>\n/* dv-motion-helpers:start */\nconst dvReduce = matchMedia('(prefers-reduced-motion: reduce)').matches;\ndocument.documentElement.classList.add('js');\n\n/**\n * 画面に入った要素へ .is-in を付ける。\n * data-reveal=\"repeat\" の要素は画面を出たら外す（何度でも再生）。\n */\nfunction dvReveal(selector = '[data-reveal]', { threshold = 0.2, rootMargin = '0px 0px -8% 0px' } = {}) {\n  const els = [...document.querySelectorAll(selector)];\n  if (dvReduce || !('IntersectionObserver' in window)) {\n    els.forEach((el) => el.classList.add('is-in'));\n    return;\n  }\n  const io = new IntersectionObserver((entries) => {\n    for (const e of entries) {\n      if (e.isIntersecting) {\n        e.target.classList.add('is-in');\n        if (e.target.dataset.reveal !== 'repeat') io.unobserve(e.target);\n      } else if (e.target.dataset.reveal === 'repeat') {\n        e.target.classList.remove('is-in');\n      }\n    }\n  }, { threshold, rootMargin });\n  els.forEach((el) => io.observe(el));\n}\n\n/**\n * スクロール量を 0〜1 にして、各 [data-progress] 要素の CSS 変数 --p に書く。\n *   data-progress=\"through\" : 要素の上端が画面の下端に来たとき 0、下端が画面の上端を抜けたとき 1\n *   data-progress=\"pin\"     : 要素の上端が画面の上端に来たとき 0、下端が画面の下端に来たとき 1\n *                             （中に position: sticky の箱を持つ、背の高い区画用）\n *   data-progress=\"page\"    : ページ全体のスクロール量\n * onUpdate(el, p) を渡すと、値が変わるたびに呼ばれる。\n */\nfunction dvProgress(onUpdate) {\n  const els = [...document.querySelectorAll('[data-progress]')];\n  const clamp = (n) => Math.min(1, Math.max(0, n));\n  let queued = false;\n  const measure = () => {\n    queued = false;\n    const vh = window.innerHeight;\n    for (const el of els) {\n      const r = el.getBoundingClientRect();\n      let p;\n      if (el.dataset.progress === 'pin') p = r.height > vh ? -r.top / (r.height - vh) : 0;\n      else if (el.dataset.progress === 'page') {\n        const max = document.documentElement.scrollHeight - vh;\n        p = max > 0 ? window.scrollY / max : 0;\n      } else p = (vh - r.top) / (vh + r.height);\n      p = clamp(p);\n      el.style.setProperty('--p', p.toFixed(4));\n      if (onUpdate) onUpdate(el, p);\n    }\n  };\n  const request = () => {\n    if (!queued) { queued = true; requestAnimationFrame(measure); }\n  };\n  addEventListener('scroll', request, { passive: true });\n  addEventListener('resize', request);\n  measure();\n}\n/* dv-motion-helpers:end */\n\n/* ---- この演出だけの JS ---- */\ndvProgress();\n</script>\n</body>\n</html>\n"
    }
  },
  "license": "Original",
  "catalogVersion": 1,
  "designSchemaVersion": 2,
  "capabilities": [
    "live-preview",
    "copy-prompt",
    "get-code"
  ],
  "reusePolicy": "construction-reuse",
  "urls": {
    "ja": "https://design-vault.pages.dev/designs/motion-svg-line-draw",
    "en": "https://design-vault.pages.dev/en/designs/motion-svg-line-draw",
    "json": "https://design-vault.pages.dev/ai/assets/motion-svg-line-draw.json",
    "designJson": null
  }
}