@fxshelf/card-orbit
Version:
Path-constrained 3D card orbit carousel for React and Vue — rise, arc, exit. No Framer Motion / GSAP. Copy-as-source friendly.
1 lines • 19.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/core/path.ts","../src/core/createOrbit.ts","../src/react/CardOrbit.tsx","../src/react/CardOrbitMobile.tsx"],"names":["useState","useRef","useEffect","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["export const RISE = 280\nexport const ARC = (Math.PI / 2) * 90\nexport const EXIT = 240\nexport const PATH_LEN = RISE + ARC + EXIT\nexport const F = RISE / PATH_LEN\nexport const G = (RISE + ARC) / PATH_LEN\n\nexport const DEFAULT_ACTIVE_WINDOW = 0.72\nexport const DEFAULT_AUTO_SPEED = 45e-6\nexport const DEFAULT_DRAG_SENSITIVITY = 8e-4\nexport const DEFAULT_PERSPECTIVE = 3400\n\nexport type PathPoint = {\n x: number\n y: number\n depth: number\n fade: number\n local: number\n active: boolean\n}\n\nexport function samplePath(local: number, active: boolean): PathPoint {\n const fade = active\n ? Math.min(\n Math.min(1, Math.max(0, local / 0.06)),\n Math.min(1, Math.max(0, (1 - local) / 0.08)),\n )\n : 0\n\n const depth = active\n ? Math.sin(\n (local < F ? local / (2 * F) : 0.5 + (local - F) / (2 * (1 - F))) * Math.PI,\n )\n : 0\n\n let x: number\n let y: number\n\n if (local < F) {\n x = -10\n y = -300 + (local / F) * RISE\n } else if (local < G) {\n const ang = (180 + ((local - F) / (G - F)) * 90) * (Math.PI / 180)\n x = 80 + 90 * Math.cos(ang)\n y = -20 - 90 * Math.sin(ang)\n } else {\n x = 80 + ((local - G) / (1 - G)) * EXIT\n y = 70\n }\n\n return { x, y, depth, fade, local, active }\n}\n\nexport function cardTransformStyle(point: PathPoint): {\n zIndex: number\n opacity: number\n transform: string\n pointerEvents: 'auto' | 'none'\n} {\n const { x, y, depth, fade, local, active } = point\n\n return {\n zIndex: Math.round(20 + 80 * depth),\n opacity: active ? fade * (0.4 + 0.6 * depth) : 0,\n transform: [\n `translate3d(calc(-50% + ${x}%), calc(-50% + ${y}%), ${-1000 + 1280 * depth}px)`,\n `rotateX(${4 - 2 * depth}deg)`,\n `rotateY(${-3 + 6 * local}deg)`,\n 'rotateZ(0deg)',\n `scale(${0.44 + 0.74 * depth})`,\n ].join(' '),\n pointerEvents: depth > 0.72 ? 'auto' : 'none',\n }\n}\n","import { DEFAULT_AUTO_SPEED, DEFAULT_DRAG_SENSITIVITY } from './path'\n\nexport type CreateOrbitOptions = {\n autoSpeed?: number\n dragSensitivity?: number\n onProgress: (progress: number) => void\n}\n\nexport type OrbitController = {\n attach(el: HTMLElement): void\n detach(): void\n setOptions(partial: {\n autoSpeed?: number\n dragSensitivity?: number\n }): void\n destroy(): void\n getProgress(): number\n}\n\nexport function createOrbit(options: CreateOrbitOptions): OrbitController {\n let autoSpeed = options.autoSpeed ?? DEFAULT_AUTO_SPEED\n let dragSensitivity = options.dragSensitivity ?? DEFAULT_DRAG_SENSITIVITY\n const onProgress = options.onProgress\n\n let progress = 0\n let dragging = false\n let lastX: number | null = null\n let lastY: number | null = null\n let frame = 0\n let last = 0\n let el: HTMLElement | null = null\n let running = false\n\n const emit = () => onProgress(progress)\n\n const onPointerDown = (e: PointerEvent) => {\n lastX = e.clientX\n lastY = e.clientY\n dragging = true\n el?.setPointerCapture(e.pointerId)\n }\n\n const onPointerMove = (e: PointerEvent) => {\n if (!dragging || lastX === null || lastY === null) return\n const dx = e.clientX - lastX\n const dy = e.clientY - lastY\n const delta = dy + 0.25 * dx\n progress = (progress + dragSensitivity * delta + 1) % 1\n emit()\n lastX = e.clientX\n lastY = e.clientY\n }\n\n const endDrag = (e: PointerEvent) => {\n lastX = null\n lastY = null\n dragging = false\n if (el?.hasPointerCapture?.(e.pointerId)) {\n el.releasePointerCapture(e.pointerId)\n }\n }\n\n const tick = (now: number) => {\n const dt = now - last\n last = now\n if (!dragging) {\n progress = (progress + autoSpeed * dt) % 1\n emit()\n }\n frame = requestAnimationFrame(tick)\n }\n\n const startLoop = () => {\n if (running) return\n running = true\n last = performance.now()\n frame = requestAnimationFrame(tick)\n }\n\n const stopLoop = () => {\n if (!running) return\n running = false\n cancelAnimationFrame(frame)\n frame = 0\n }\n\n const detach = () => {\n if (!el) return\n el.removeEventListener('pointerdown', onPointerDown)\n el.removeEventListener('pointermove', onPointerMove)\n el.removeEventListener('pointerup', endDrag)\n el.removeEventListener('pointercancel', endDrag)\n el.removeEventListener('lostpointercapture', endDrag)\n el = null\n }\n\n return {\n attach(target) {\n detach()\n el = target\n el.addEventListener('pointerdown', onPointerDown)\n el.addEventListener('pointermove', onPointerMove)\n el.addEventListener('pointerup', endDrag)\n el.addEventListener('pointercancel', endDrag)\n el.addEventListener('lostpointercapture', endDrag)\n startLoop()\n },\n detach,\n setOptions(partial) {\n if (partial.autoSpeed !== undefined) autoSpeed = partial.autoSpeed\n if (partial.dragSensitivity !== undefined) {\n dragSensitivity = partial.dragSensitivity\n }\n },\n destroy() {\n stopLoop()\n detach()\n dragging = false\n lastX = null\n lastY = null\n },\n getProgress() {\n return progress\n },\n }\n}\n","'use client'\n\nimport {\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n} from 'react'\nimport { createOrbit } from '../core/createOrbit'\nimport {\n DEFAULT_ACTIVE_WINDOW,\n DEFAULT_AUTO_SPEED,\n DEFAULT_DRAG_SENSITIVITY,\n DEFAULT_PERSPECTIVE,\n cardTransformStyle,\n samplePath,\n} from '../core/path'\nimport '../core/styles.css'\n\nexport type CardOrbitProps = {\n /** Image URLs to place on the path (order = phase offset). */\n images: string[]\n /** Optional alt text per image; falls back to `Card ${i + 1}`. */\n alts?: string[]\n className?: string\n style?: CSSProperties\n stageClassName?: string\n cardClassName?: string\n imageClassName?: string\n /** Auto-advance rate when not dragging. Default `45e-6`. */\n autoSpeed?: number\n /** Pointer scrub sensitivity. Default `8e-4`. */\n dragSensitivity?: number\n /** Visible phase window in `(0, 1)`. Default `0.72`. */\n activeWindow?: number\n /** CSS perspective in px. Default `3400`. */\n perspective?: number\n ariaLabel?: string\n /**\n * Hide below `lg` (1024px) with built-in CSS.\n * Pair with `CardOrbitMobile` for narrow screens. Default `true`.\n */\n desktopOnly?: boolean\n}\n\nexport function CardOrbit({\n images,\n alts,\n className,\n style,\n stageClassName,\n cardClassName,\n imageClassName,\n autoSpeed = DEFAULT_AUTO_SPEED,\n dragSensitivity = DEFAULT_DRAG_SENSITIVITY,\n activeWindow = DEFAULT_ACTIVE_WINDOW,\n perspective = DEFAULT_PERSPECTIVE,\n ariaLabel = 'Card orbit carousel',\n desktopOnly = true,\n}: CardOrbitProps) {\n const [progress, setProgress] = useState(0)\n const rootRef = useRef<HTMLDivElement>(null)\n const orbitRef = useRef<ReturnType<typeof createOrbit> | null>(null)\n\n const hasImages = images.length > 0\n\n useEffect(() => {\n if (!hasImages) return\n const node = rootRef.current\n if (!node) return\n\n const orbit = createOrbit({\n onProgress: setProgress,\n })\n orbitRef.current = orbit\n orbit.attach(node)\n return () => {\n orbit.destroy()\n orbitRef.current = null\n }\n }, [hasImages])\n\n useEffect(() => {\n orbitRef.current?.setOptions({ autoSpeed, dragSensitivity })\n }, [autoSpeed, dragSensitivity])\n\n if (!hasImages) return null\n\n const rootClass = [\n 'card-orbit',\n desktopOnly ? 'card-orbit--desktop-only' : '',\n className ?? '',\n ]\n .filter(Boolean)\n .join(' ')\n\n return (\n <div\n ref={rootRef}\n className={rootClass}\n style={{ perspective: `${perspective}px`, ...style }}\n aria-label={ariaLabel}\n >\n <div\n className={['card-orbit__stage', stageClassName].filter(Boolean).join(' ')}\n >\n {images.map((src, index) => {\n const phase = (index / images.length + progress) % 1\n const active = phase > 0 && phase < activeWindow\n const local = active ? phase / activeWindow : 0\n const point = samplePath(local, active)\n\n return (\n <figure\n key={`${src}-${index}`}\n className={['card-orbit__card', cardClassName].filter(Boolean).join(' ')}\n style={cardTransformStyle(point)}\n >\n <img\n src={src}\n alt={alts?.[index] ?? `Card ${index + 1}`}\n width={1200}\n height={780}\n className={['card-orbit__image', imageClassName].filter(Boolean).join(' ')}\n draggable={false}\n decoding=\"async\"\n />\n </figure>\n )\n })}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\nimport '../core/styles.css'\n\nexport type CardOrbitMobileProps = {\n images: string[]\n alts?: string[]\n className?: string\n style?: CSSProperties\n trackClassName?: string\n cardClassName?: string\n imageClassName?: string\n /**\n * Hide at `lg` and up (pair with desktop `CardOrbit`).\n * Default `true`.\n */\n hideOnDesktop?: boolean\n /** Marquee duration in seconds. Default `28`. */\n durationSec?: number\n}\n\nexport function CardOrbitMobile({\n images,\n alts,\n className,\n style,\n trackClassName,\n cardClassName,\n imageClassName,\n hideOnDesktop = true,\n durationSec = 28,\n}: CardOrbitMobileProps) {\n if (images.length === 0) return null\n\n const loop = [...images, ...images]\n\n const rootClass = [\n 'card-orbit-mobile',\n hideOnDesktop ? 'card-orbit-mobile--desktop-hide' : '',\n className ?? '',\n ]\n .filter(Boolean)\n .join(' ')\n\n return (\n <div className={rootClass} style={style}>\n <div\n className={['card-orbit-mobile__track', trackClassName].filter(Boolean).join(' ')}\n style={{ animationDuration: `${durationSec}s` }}\n >\n {loop.map((src, i) => (\n <figure\n key={`${src}-${i}`}\n className={['card-orbit-mobile__card', cardClassName].filter(Boolean).join(' ')}\n >\n <img\n src={src}\n alt={alts?.[i % images.length] ?? ''}\n className={['card-orbit-mobile__image', imageClassName]\n .filter(Boolean)\n .join(' ')}\n draggable={false}\n />\n </figure>\n ))}\n </div>\n </div>\n )\n}\n"]}