FlyingPosters
flying-postersMoves poster images through a perspective field controlled by dragging.
Usage
Basic usage
Pass in an array of poster image addresses, connected end to end in an infinite loop; the default is automatic scrolling, and the scroll wheel/drag can be used to scroll manually.
Scroll wheel / drag and scroll
<div
className="relative h-80 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 270)" }}
>
<FlyingPosters items={posters} className="absolute inset-0" />
</div>Folding and twisting strength
distortion Controls the folding range of the poster flying when scrolling. The larger the fold, the more exaggerated it is (recommendations 1–6).
Scroll wheel / drag and scroll
<FlyingPosters items={posters} distortion={5} className="absolute inset-0" />Wide angle perspective
cameraFov Increase the perspective, and the arc of the posters flying in and out becomes more obvious; cameraZ Zoom out to see more posters.
Scroll wheel / drag and scroll
<FlyingPosters
items={posters}
cameraFov={70}
cameraZ={26}
className="absolute inset-0"
/>Turn off automatic scrolling
autoScroll={false} Only manual scrolling (wheel/drag) remains.
Scroll wheel / drag and scroll
<FlyingPosters items={posters} autoScroll={false} className="absolute inset-0" />When to use
Use FlyingPosters for an immersive corridor of same-ratio posters or covers, such as a landing-page hero or gallery entrance. Choose it for true 3D folding and perspective depth. Use TiltedCard for a 2D card that tilts on hover, or ScrollStack for scroll-pinned stacking cards.
Import
import { FlyingPosters } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| items | string[] | [] | Poster image URLs joined into an infinite loop. Same-ratio images are recommended; an empty array renders an empty canvas safely. |
| planeWidth | number | 320 | Poster plane width in world units. |
| planeHeight | number | 320 | Poster plane height in world units. |
| distortion | number | 3 | Scroll-driven folding strength. Values from 1 to 6 are recommended; 0 is nearly flat translation. |
| scrollEase | number | 0.01 | Scroll lerp factor from 0 to 1. Smaller values create longer, heavier inertia. |
| cameraFov | number | 45 | Perspective camera field of view in degrees. |
| cameraZ | number | 20 | Camera distance on the Z axis. Larger values reveal more posters. |
| autoScroll | boolean | true | Slowly scrolls without input; forced off under reduced motion. |
| autoScrollSpeed | number | 0.6 | Auto-scroll speed in world units per second. |
| className | string | — | Class name forwarded to the root. |
| style | CSSProperties | — | Inline styles forwarded to the root. |
Slots
| Slot | Type | Description |
|---|---|---|
| fallback | ReactNode | Content centered in the static layer when reduced motion is enabled or WebGL is unavailable. |
Usage notes
- The root must have measurable dimensions, such as
absolute inset-0inside a fixed-height positioned parent. - WebGL is client-only. ogl is lazy-loaded with SSR and StrictMode safeguards; unavailable WebGL and reduced motion use the static
fallbacklayer. - [[webgl-canvas-loseContext-poisons-strictmode-remount]]: do not cache and reuse the component's canvas across StrictMode remounts, because a lost context permanently invalidates that canvas.
- [[mcp-browser-busy-launch-isolated-chromium-via-executablepath]] / [[recharts-headless-screenshot-blank-clippath-animation-starved]]: a headless capture may stop before the WebGL entrance advances and appear blank. Verify in a real browser or enable reduced motion.
Related
Table · Book3D · ProTable · PricingTable · JsonViewer · EditableTable
Playground
Scroll wheel / drag and scroll
<div className="relative h-80 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 270)" }}>
<FlyingPosters
items={posters}
distortion={3}
scrollEase={0.05}
cameraFov={45}
autoScroll={true}
className="absolute inset-0"
/>
</div>