MOTION PALETTE
← すべてのデモ

Orbit Path

ScrollAI Image / SVG / GSAP / ScrollTrigger

フルスクリーンで開く ↗

枠の中でスクロール・クリックして動きを試せます。端末指定は実寸表示です。幅が足りない場合は横にスクロールできます。

再現プロンプト

架空の配送サービス「TANGENT」の1ページ。AI生成した雨夜の俯瞰都市マップと流れる夜霧の上を、ドローンがSVGルートに沿ってgetPointAtLengthでスクロール追従。倉庫・仕分けハブ・住宅街のシネマティックな経由地カットを巡り、最終地点では質感ある荷箱3フレームが発光しながらクロスフェード開封する。 配色・構成・動きの数値をまとめた指示書です。 お使いのAIに貼り、素材やお店の情報を伝えて実装してください。仕上がりは利用するAIと素材によって変わります。

Build a single self-contained HTML file (inline CSS + JS, GSAP and ScrollTrigger loaded from CDN) for a one-page site for a fictional last-mile logistics brand called "TANGENT" ("Last-Mile Network"). The entire page is read as one continuous delivery route: a single SVG curve runs the full height of the document, a small delivery-drone icon rides that curve as the page scrolls, and every section is a stop along the same line. The page scrolls with the normal window scrollbar (no inner scroll container needed in the exported file).

## Art direction
- Background #100E17 (near-black plum), card/panel surfaces #1B1826, single accent #F472B6 (pink) used for the route line, the drone, waypoint markers and the CTA border, foreground text #F5F1FA, muted body copy #9C93A8.
- Typography: uppercase eyebrows at 0.26em letter-spacing, headings 700 weight clamp(1.4rem,3.2vw,2.1rem) in body sections and clamp(1.9rem,5.2vw,3.2rem) in the hero, body copy 0.9–1rem at the muted color.
- A single sticky, full-viewport AI-generated aerial city plate sits behind every section: rainy night, river, warehouse district, highway interchange, dense blocks and a residential quarter. Apply a restrained dark veil so the pink SVG route and pale copy stay legible while city detail remains visible; section backgrounds are translucent rather than opaque.

## Imagery (AI-generated cinematic plates)
Use eight local images in one unified cinematic premium-logistics style: near-black plum/deep navy, muted violet rain, sparse amber practical light, and no text/logos. The opaque city plate is a 3:2 near-top-down aerial view containing river, freight yards, interchange and homes, without any baked-in route line. Three 16:9 waypoint vignettes show (1) a rainy pickup warehouse with shelves/conveyor/plain parcels, (2) an automated sorting hub with arc-shaped branching belts, and (3) a residential porch handoff by drone. Each shipping card places its matching vignette above the eyebrow and copy. The footer uses three alpha WebP product frames of the same charcoal box on the same square canvas: fully closed, opened with tissue visible, then fully open with a concentrated gold glow and rose-gold inner parcel. A sparse alpha overlay of blue-violet mist strands and tiny light particles drifts above the city plate.

## Ambient mist motion and power gate
Size the transparent mist overlay to 108% with -4% offsets. Create one paused GSAP yoyo tween from xPercent -2.8/yPercent -0.5/opacity 0.38 to xPercent 2.8/yPercent 0.7/opacity 0.7 over 28 seconds with sine.inOut. A visibility ScrollTrigger plays it only while the demo root is on screen and pauses it immediately off screen. Disable the loop for prefers-reduced-motion.

## The route: geometry and coordinate system
Define six named control points as fractions of the page's total content width/height (xFrac, yFrac): start (0.78, 0.05) → waypoint "Pickup" (0.66, 0.22) → waypoint "Sorting Hub" (0.86, 0.42) → a plain bend point with no marker (0.60, 0.58) → waypoint "Last-Mile Handoff" (0.88, 0.76) → final "Delivered" point (0.72, 0.94). On page load and on every resize (ResizeObserver on the content element), convert these fractions into real pixel coordinates using the content element's actual measured width/height, then build the path's "d" attribute by converting the point list into cubic Beziers with the Catmull-Rom-to-Bezier formula (tension = 1/6): for point i, control1 = p[i] + (p[i+1]-p[i-1])/6, control2 = p[i+1] - (p[i+2]-p[i])/6, drawn as a "C" command per segment — this makes the curve pass exactly through every control point with smooth, S-shaped transitions. Set the SVG's viewBox to "0 0 {contentWidth} {contentHeight}" (one viewBox unit = one CSS pixel) so that SVGPathElement.getPointAtLength() returns coordinates that can be used directly as translate() values with no further transform math. Render two overlapping copies of the same path: a faint dashed "preview" (stroke rgba(244,114,182,0.22), width 2, dash pattern "1 9") showing the whole route up front, and a solid "travelled" copy (stroke #F472B6, width 2.5, drop-shadow glow) whose stroke-dasharray equals the path's total length and whose stroke-dashoffset is driven by scroll.

## Signature 1: the drone follows the path
A single window-wide ScrollTrigger (trigger = the content element, start "top top", end "bottom bottom", scrub: true, no smoothing) drives everything. On every onUpdate, take the raw scroll progress (0–1) and remap it through a piecewise-linear function into a path-length progress (0–1) — see "Waypoint pauses" below. Multiply that path progress by the path's total length to get the target length, call getPointAtLength(length) for the drone's position, and call getPointAtLength(length + eps) with eps = max(1px, totalLength × 0.002) for a look-ahead point; the drone's heading angle = atan2(dy, dx) of those two points in degrees. Position the drone with 'transform: translate(x px, y px) translate(-50%, -50%) rotate(angle deg)', written directly via a DOM ref on every tick (never React state) so the update stays cheap. The drone icon itself is a small (30×30) line-art quadcopter: an X of two crossing struts with a small ring rotor at each of the four tips, a solid parcel square at the center, a smaller outlined parcel hanging just below it, and a small filled triangle on the right edge acting as the "nose" so the rotation is visually legible.

## Signature 2: the travelled route fills in
Every onUpdate also sets the "travelled" path's stroke-dashoffset = totalLength × (1 − pathProgress). Because dasharray equals the full length, this reveals the accent-colored stroke exactly from the route's start up to the drone's current position, and un-reveals it symmetrically when the user scrolls back up — a literal, always-accurate progress line, never a fixed-duration draw-on.

## Signature 3: waypoint pauses and shipping cards
Three named stops — Pickup, Sorting Hub, Last-Mile Handoff — each own a scroll-progress "pause window": Pickup 0.18–0.24, Sorting Hub 0.40–0.46, Last-Mile Handoff 0.74–0.80 (of the whole page's scroll range). The scroll→path remap is built from an ordered list of {scrollProgress, pathProgress} keyframes: {0,0}, {0.18,0.2}, {0.24,0.2}, {0.40,0.4}, {0.46,0.4}, {0.74,0.8}, {0.80,0.8}, {0.95,1}, {1,1} — ordinary linear interpolation between consecutive keyframes; because each pause window's start/end keyframes share the same pathProgress value, the interpolation naturally produces a plateau (the drone visibly holds still while the user keeps scrolling through that window), with no special-case branching required. Each waypoint has a dashed-border shipping-label card (background #1B1826, 1px dashed rgba(244,114,182,0.45) border, radius 10px) positioned at its route point, offset left or right of the marker. The card begins with its matching 16:9 cinematic vignette, followed by an eyebrow, bold headline and body copy. It is hidden by default (opacity 0, clip-path inset(0 0 100% 0)) and becomes visible — opacity 1, clip-path inset(0 0 0% 0), scale 0.92→1 — whenever scroll progress is inside its pause window extended by 0.03 in both directions. Every route point also has a small dot marker that permanently brightens as the drone passes it.

## Mouse/touch reaction
Hovering (or tapping, on touch) any shipping-label card also forces it open regardless of scroll position — a second CSS class, independent of the scroll-driven one, so either condition alone is enough to reveal the card. This lets a visitor preview any stop without having to scroll to its exact pause window.

## Signature 4: delivery — the parcel opens
The footer's hero object is a 300px responsive square containing three absolutely stacked alpha-WebP frames of the same premium charcoal parcel: closed, open with tissue, and fully open around a bright golden interior. Once scroll progress passes 0.95, compute local progress = clamp((scroll − 0.95) / 0.05). Crossfade closed→half over local 0–0.5, then half→open over roughly local 0.45–1, writing opacity directly through refs inside the same scrub update. The final frame is the brightest and receives a soft CSS radial halo behind the cutout. Scrolling backward reverses every crossfade exactly; do not use a time-based autoplay.

## Section content
1. Hero (100vh): eyebrow "TANGENT LOGISTICS · LAST-MILE NETWORK", H1 "Every parcel plots its own path.", a body paragraph about live tracking, a bouncing "↓ follow the route" hint.
2. "How it moves": short copy about the route line being the literal tracking status, no separate dashboard.
3. "The Hub" (Sorting Hub waypoint lives here): copy about automated re-sorting by neighborhood.
4. "Coverage": a static, non-animated 2-column stat grid (no count-up) — FLEET "140 drones + 60 vans", METRO AREAS "12 cities", DOCK TO DOOR "38 minutes avg.", OPERATES "Rain or shine".
5. "The Last Mile" (Handoff waypoint lives here): copy about drone handoff in under 90 seconds.
6. Footer: the opening parcel box, H2 "Delivered. Route complete.", a CTA button "Track My Parcel →" (1px accent border, transparent fill, hover fills solid accent with dark text, translateY(-2px)).
All non-route text content reveals via a shared clip-path wipe from below ('inset(0% 0% 100% 0%)' → 'inset(0% 0% 0% 0%)', opacity 0→1, 0.75s power2.out, 0.1s stagger, triggered at "top 78%", reversible) — never a y-offset fade.

## Performance & accessibility
- The whole route layer (svg + markers + drone + cards) is a single absolutely-positioned div stretching to the content element's full natural height via inset: 0, sitting before the section elements in the DOM so it paints behind them (every section has position: relative, establishing normal stacking by DOM order) and carries pointer-events: none except on the cards themselves.
- Route path/text writes are all direct DOM/ref mutations inside one ScrollTrigger's onUpdate — no per-frame React re-renders.
- Respect prefers-reduced-motion: reduce by skipping the drone's heading rotation (it still translates along the path, just without spinning), disabling the hero hint's bounce keyframe, and shortening/softening the section reveal (0.35s opacity fade, no clip-path, 0.03s stagger) — the core scroll-driven route mechanic stays active either way since it only ever moves in direct response to the user's own scrolling, never autoplays.
- All route/drone graphics are aria-hidden; every waypoint's real information (eyebrow, title, copy) is ordinary readable text inside its card, and the stat grid, headings and CTA remain plain accessible DOM at all times.

## Action flow and content

Implement the following local planning experience for the primary CTA, in the visual language of the page. Use a real button opening a labelled modal dialog. Choosing a pricing card must preselect that exact plan. A navigation link may move to another section; a planning CTA must lead to its review flow.

Use this content for the dialog, including the option details and notes:

{
  "slug": "orbit-path",
  "brand": "TANGENT",
  "accent": "#2d695e",
  "title": "Follow a sample parcel",
  "tracking": true,
  "intro": "One parcel, from pickup to the final mile. Use TG-2048 to inspect the delivery history and expected arrival window.",
  "facts": [
    [
      "Tracking reference",
      "TG-2048"
    ],
    [
      "Service",
      "Local delivery · standard parcel"
    ],
    [
      "Updates",
      "Illustrative events for this sample route"
    ]
  ],
  "notes": [
    "This is a tracking demonstration. Entering a real tracking number will not contact a carrier or retrieve personal delivery information."
  ]
}

- Explain before input that this is a fictional brand sample: choices can be reviewed and saved, with no external submission, booking, payment or registration. Use each brand's language. Do not launch a mail client or present an installer for a fictional product.
- Give every option group a legend, inputs explicit labels and errors linked descriptions. Validate options against the allowed values, requested dates as actual dates from today onward, and free text as 10–2,000 trimmed characters. Blank spaces alone must fail. Tracking accepts the sample reference TG-2048 only.
- Focus the first invalid field and preserve every entered value. On success, show the selected option WITH its full detail, requested date or brief, relevant facts and notes. An edit action returns to the preserved form. Do not claim a real reservation, purchase or account has succeeded.
- Save the complete review as UTF-8 text. A browser download request is not proof of a file being written. On download failure, retain the full text in a labelled, selectable textarea and offer retry. Do not show a false success state.
- Constrain modal content to the viewport with internal scrolling. Support keyboard and touch, trap focus while open, close on Escape and restore focus to the exact initiating control. Keep these operations available with motion disabled.
- If a separate project-contact CTA is present, let it collect a project brief and review it locally; a product selection CTA must retain the product choice. Verify both flows independently.

Sample tracking history: 09:10 pickup at Tangent Hub, 10:35 sorting at Riverside, 11:20 out for delivery, expected 12:00–14:00. Mark the entire history as a sample.