clean: remove .pi/ config from calvana repo — lives in pi-vs-claude-code now
- Removed all .pi/ (agents, themes, extensions, skills, observatory) - Removed CLAUDE.md (belongs in parent pi repo) - Added .pi/ and CLAUDE.md to .gitignore - Added .pi/ to pledge-now-pay-later/.gitignore
This commit is contained in:
@@ -115,104 +115,52 @@ const GALLERY_IMAGES = [
|
||||
{ src: "/images/brand/youth-01-workshop.jpg", alt: "Youth workshop in full swing" },
|
||||
]
|
||||
|
||||
/* Slide directions: [incoming start offset, outgoing end offset] as CSS translate values */
|
||||
const SLIDES: [string, string][] = [
|
||||
["translateX(100%)", "translateX(-100%)"], // from right
|
||||
["translateX(-100%)", "translateX(100%)"], // from left
|
||||
["translateY(100%)", "translateY(-100%)"], // from bottom
|
||||
["translateY(-100%)", "translateY(100%)"], // from top
|
||||
]
|
||||
|
||||
const DURATION = 420 // ms — snappy
|
||||
const INTERVAL = 3500
|
||||
const SLIDE_DIR: [string, string] = ["translateX(100%)", "translateX(-100%)"] // slide from right
|
||||
const SLIDE_SPEED = 150 // ms — fast snap-slide
|
||||
const SLIDE_INTERVAL = 3500
|
||||
|
||||
function GallerySlideshow() {
|
||||
const [idx, setIdx] = useState(0)
|
||||
const [prevIdx, setPrevIdx] = useState(-1)
|
||||
const [phase, setPhase] = useState<"idle" | "prep" | "go">("idle")
|
||||
const dirRef = useRef(SLIDES[0])
|
||||
const dirRef = useRef(SLIDE_DIR)
|
||||
const inRef = useRef<HTMLDivElement>(null)
|
||||
const outRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Pick random direction and kick off transition
|
||||
const advance = useCallback(() => {
|
||||
dirRef.current = SLIDES[Math.floor(Math.random() * SLIDES.length)]
|
||||
setIdx((i) => {
|
||||
setPrevIdx(i)
|
||||
return (i + 1) % GALLERY_IMAGES.length
|
||||
})
|
||||
setPhase("prep") // frame 1: position incoming off-screen
|
||||
setIdx((i) => { setPrevIdx(i); return (i + 1) % GALLERY_IMAGES.length })
|
||||
setPhase("prep")
|
||||
}, [])
|
||||
|
||||
// Two-frame trick: prep → go
|
||||
useEffect(() => {
|
||||
if (phase === "prep") {
|
||||
// Position incoming element at its start offset (no transition)
|
||||
const el = inRef.current
|
||||
if (el) {
|
||||
el.style.transition = "none"
|
||||
el.style.transform = dirRef.current[0]
|
||||
}
|
||||
const oel = outRef.current
|
||||
if (oel) {
|
||||
oel.style.transition = "none"
|
||||
oel.style.transform = "translate(0,0)"
|
||||
}
|
||||
// Next frame: enable transition and snap to final position
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => setPhase("go"))
|
||||
})
|
||||
if (inRef.current) { inRef.current.style.transition = "none"; inRef.current.style.transform = dirRef.current[0] }
|
||||
if (outRef.current) { outRef.current.style.transition = "none"; outRef.current.style.transform = "translate(0,0)" }
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => setPhase("go")))
|
||||
}
|
||||
if (phase === "go") {
|
||||
const ease = `transform ${DURATION}ms cubic-bezier(0.25, 1, 0.5, 1)`
|
||||
const el = inRef.current
|
||||
if (el) {
|
||||
el.style.transition = ease
|
||||
el.style.transform = "translate(0,0)"
|
||||
}
|
||||
const oel = outRef.current
|
||||
if (oel) {
|
||||
oel.style.transition = ease
|
||||
oel.style.transform = dirRef.current[1]
|
||||
}
|
||||
const t = setTimeout(() => {
|
||||
setPrevIdx(-1)
|
||||
setPhase("idle")
|
||||
}, DURATION + 20)
|
||||
return () => clearTimeout(t)
|
||||
const t = `transform ${SLIDE_SPEED}ms cubic-bezier(0.16, 1, 0.3, 1)`
|
||||
if (inRef.current) { inRef.current.style.transition = t; inRef.current.style.transform = "translate(0,0)" }
|
||||
if (outRef.current) { outRef.current.style.transition = t; outRef.current.style.transform = dirRef.current[1] }
|
||||
const timer = setTimeout(() => { setPrevIdx(-1); setPhase("idle") }, SLIDE_SPEED + 10)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [phase])
|
||||
|
||||
// Auto-advance timer
|
||||
useEffect(() => {
|
||||
const t = setInterval(advance, INTERVAL)
|
||||
const t = setInterval(advance, SLIDE_INTERVAL)
|
||||
return () => clearInterval(t)
|
||||
}, [advance])
|
||||
|
||||
return (
|
||||
<div className="relative min-h-[280px] md:min-h-[360px] overflow-hidden bg-gray-100">
|
||||
{/* Outgoing image */}
|
||||
{prevIdx !== -1 && (
|
||||
<div ref={outRef} className="absolute inset-0 z-[1] will-change-transform">
|
||||
<Image
|
||||
src={GALLERY_IMAGES[prevIdx].src}
|
||||
alt={GALLERY_IMAGES[prevIdx].alt}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
<Image src={GALLERY_IMAGES[prevIdx].src} alt={GALLERY_IMAGES[prevIdx].alt} fill className="object-cover" sizes="(max-width: 768px) 100vw, 50vw" />
|
||||
</div>
|
||||
)}
|
||||
{/* Incoming / current image */}
|
||||
<div ref={inRef} className="absolute inset-0 z-[2] will-change-transform">
|
||||
<Image
|
||||
src={GALLERY_IMAGES[idx].src}
|
||||
alt={GALLERY_IMAGES[idx].alt}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
priority={idx === 0}
|
||||
/>
|
||||
<Image src={GALLERY_IMAGES[idx].src} alt={GALLERY_IMAGES[idx].alt} fill className="object-cover" sizes="(max-width: 768px) 100vw, 50vw" priority={idx === 0} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user