{
  "id": "scroll-reveal",
  "type": "motion",
  "status": "published",
  "name": {
    "zh": "滚动显现",
    "en": "Scroll Reveal"
  },
  "aliases": [
    "滚动显现",
    "滚动淡入",
    "进入视口动画",
    "滑到那里才出现",
    "滚动出现",
    "scroll into view animation"
  ],
  "category": [
    "Motion",
    "Scroll"
  ],
  "description": {
    "zh": "元素滚动进入视口时才播放淡入、上浮或滑入动画，之前保持隐藏或占位。 把\"一次性呈现全部内容\"变成\"随阅读节奏逐段揭示\"，让长页面的信息 更有秩序感。通常由 IntersectionObserver 触发，只播一次。",
    "en": "Elements stay hidden until they enter the viewport, then play a fade, rise or slide-in — once. It replaces \"dump everything at once\" with content revealed at the pace of reading, giving long pages a sense of order. Usually triggered by IntersectionObserver."
  },
  "use_when": [
    {
      "zh": "长落地页的分区内容逐段呈现",
      "en": "Long landing pages revealing sections one by one"
    },
    {
      "zh": "文章、案例展示等线性阅读内容",
      "en": "Linear reading — articles, case studies, showcases"
    },
    {
      "zh": "需要引导视线自上而下移动",
      "en": "When the eye should be led top-to-bottom"
    }
  ],
  "avoid_when": [
    {
      "zh": "用户要找的东西被藏进动画（迟到即伤害）",
      "en": "Content users came for — late reveal hurts"
    },
    {
      "zh": "表单、价格等决策关键信息",
      "en": "Decision-critical info — forms, pricing"
    },
    {
      "zh": "整页元素全部滚动显现（等待感堆积）",
      "en": "Revealing everything on the page — waiting fatigue"
    }
  ],
  "tags": [
    "animation",
    "scroll",
    "viewport",
    "entrance"
  ],
  "variants": [
    {
      "id": "fade-up",
      "name": {
        "zh": "淡入上浮",
        "en": "Fade up"
      },
      "note": {
        "zh": "opacity 0→1 + translateY(24px→0)，最常用",
        "en": "opacity 0→1 + translateY(24px→0) — the default"
      }
    },
    {
      "id": "slide-in",
      "name": {
        "zh": "侧向滑入",
        "en": "Slide in"
      },
      "note": {
        "zh": "从左右滑入，适合图文交错布局",
        "en": "Enter from a side — suits alternating image/text layouts"
      }
    },
    {
      "id": "scale-in",
      "name": {
        "zh": "缩放浮现",
        "en": "Scale in"
      },
      "note": {
        "zh": "从 0.92 放大到 1，卡片与图片适用",
        "en": "0.92→1 scale — good for cards and images"
      }
    }
  ],
  "related": [
    {
      "id": "lazy-loading",
      "relation": "used_with"
    },
    {
      "id": "infinite-scroll",
      "relation": "used_with"
    },
    {
      "id": "text-reveal",
      "relation": "similar_to"
    },
    {
      "id": "stagger-reveal",
      "relation": "similar_to"
    }
  ],
  "demo": {
    "component": "scroll-reveal",
    "modes": [
      "default"
    ],
    "motionControls": [
      "slow"
    ]
  },
  "implementation": {
    "css": [
      "IntersectionObserver",
      "opacity",
      "transform: translateY",
      "transition"
    ],
    "notes": {
      "zh": "IntersectionObserver 观察目标元素，进入视口（threshold 0.15～0.25）时 加 .revealed 类触发 transition：opacity 0→1、translateY(24px)→0， 600ms ease-out，只触发一次后 unobserve。动画时长乘以一个可调速度变量。 初始隐藏状态会导致无 JS 时不显示，务必提供 no-js 回退； 尊重 prefers-reduced-motion（直接显示）。",
      "en": "Observe targets with IntersectionObserver (threshold 0.15–0.25); on entry add a .revealed class driving `transition: opacity 0→1, translateY(24px)→0` over 600ms ease-out, then unobserve. Scale durations by a speed variable. The hidden initial state breaks no-JS rendering — always ship a fallback; respect prefers-reduced-motion (show immediately)."
    }
  },
  "sources": [
    {
      "name": "MDN — Intersection Observer API",
      "url": "https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver"
    }
  ],
  "prompts": {
    "basic": {
      "zh": "给页面的各个区块添加滚动显现：进入视口时淡入上浮。",
      "en": "Add scroll reveal to page sections — fade up as they enter the viewport."
    },
    "design": {
      "zh": "区块进入视口下沿 80% 高度时淡入并上浮 24px，600ms ease-out，只播一次； 同屏多个元素按 80ms 间隔错峰出现。",
      "en": "Sections fade up 24px when crossing 80% of viewport height, 600ms ease-out, once only; siblings on screen stagger 80ms apart."
    },
    "implementation": {
      "zh": "IntersectionObserver（rootMargin: '0px 0px -15% 0px'）进入即加 .revealed 并 unobserve；CSS：.reveal{opacity:0;transform:translateY(24px)} .revealed{opacity:1;transform:none;transition:all .6s ease-out}。 无 JS 回退：<noscript> 或默认显示。尊重 prefers-reduced-motion。",
      "en": "IntersectionObserver (rootMargin: '0px 0px -15% 0px') adds .revealed and unobserves; CSS: `.reveal{opacity:0;transform:translateY(24px)}` `.revealed{opacity:1;transform:none;transition:all .6s ease-out}`. Ship a no-JS fallback (noscript or visible-by-default). Respect prefers-reduced-motion."
    },
    "agent": {
      "zh": "为项目落地页的分区内容添加滚动显现动画。\n\n先检查是否已有 reveal/observer 工具，复用而非新写。\n要求：\n- 进入视口淡入上浮 24px，600ms，只播一次\n- 同屏元素 80ms 错峰\n- 无 JS 或 SSR 首屏内容不被隐藏\n- 尊重 prefers-reduced-motion\n- 不引入新依赖\n完成后运行现有检查，说明修改文件。",
      "en": "Add scroll-reveal animations to the landing page's sections.\n\nCheck for an existing reveal/observer utility first; reuse it.\nRequirements:\n- Fade up 24px on viewport entry, 600ms, once only\n- 80ms stagger for on-screen siblings\n- No-JS and SSR above-the-fold content never hidden\n- Respect prefers-reduced-motion\n- No new dependencies\nRun existing checks when done and list modified files."
    }
  }
}