Skip to content

Commit df3c767

Browse files
committed
feat: demo control lingers — tour resumes after 5s of stillness
Leaving the tube no longer snaps the tour back: the pointer stays parked where the visitor left it, and the tour only picks the loop back up after five seconds without any interaction (same timer covers going idle while still hovering). The shared motion values mean the resumed tour springs off from the parked position.
1 parent 05ebbdb commit df3c767

1 file changed

Lines changed: 23 additions & 8 deletions

File tree

apps/web/src/components/layout/landing/demo-screen.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -528,16 +528,31 @@ export function DemoScreen() {
528528
)
529529
}, [])
530530

531-
// The tour runs itself until the visitor's mouse takes the screen; on leave
532-
// it picks the loop back up from wherever they left it.
531+
// The tour runs itself until the visitor's mouse takes the screen. Control
532+
// is held for as long as they keep interacting; after 5s of stillness (or
533+
// after leaving) the tour resumes — springing off from wherever the cursor
534+
// was left, since the same motion values carry both modes.
533535
const [interactive, setInteractive] = useState(false)
534536
const [pressed, setPressed] = useState(false)
535537
const rootRef = useRef<HTMLDivElement>(null)
538+
const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
539+
const touch = () => {
540+
setInteractive(true)
541+
if (idleTimer.current) clearTimeout(idleTimer.current)
542+
idleTimer.current = setTimeout(() => setInteractive(false), 5000)
543+
}
544+
useEffect(
545+
() => () => {
546+
if (idleTimer.current) clearTimeout(idleTimer.current)
547+
},
548+
[]
549+
)
536550
const { scene, phase, next, goTo } = useTour(reduceMotion, interactive)
537551
const cursor = useDemoCursor(phase, next, scene, interactive)
538552

539553
// Map the real pointer into the demo's 2× coordinate space.
540554
const onPointerMove = (e: React.PointerEvent) => {
555+
touch()
541556
const rect = rootRef.current?.getBoundingClientRect()
542557
if (!rect) return
543558
cursor.x.set((e.clientX - rect.left) * 2)
@@ -549,13 +564,13 @@ export function DemoScreen() {
549564
ref={rootRef}
550565
className="h-full w-full overflow-hidden"
551566
style={{ background: P.bg, cursor: "none" }}
552-
onPointerEnter={() => setInteractive(true)}
553-
onPointerLeave={() => {
554-
setInteractive(false)
555-
setPressed(false)
556-
}}
567+
onPointerEnter={touch}
568+
onPointerLeave={() => setPressed(false)}
557569
onPointerMove={onPointerMove}
558-
onPointerDown={() => setPressed(true)}
570+
onPointerDown={() => {
571+
touch()
572+
setPressed(true)
573+
}}
559574
onPointerUp={() => setPressed(false)}
560575
>
561576
<div

0 commit comments

Comments
 (0)