@@ -9,13 +9,23 @@ import "./Homepage.css";
99interface HomepageProps {
1010 onPlay : ( ) => void ;
1111 isVisible : boolean ;
12+ demoCount : number ;
13+ currentDemoIndex : number ;
14+ onSelectDemo : ( index : number ) => void ;
15+ onSwipeNextDemo : ( ) => void ;
16+ onSwipePrevDemo : ( ) => void ;
1217 isWebGPUWarningDismissed : boolean ;
1318 onDismissWebGPUWarning : ( ) => void ;
1419}
1520
1621export function Homepage ( {
1722 onPlay,
1823 isVisible,
24+ demoCount,
25+ currentDemoIndex,
26+ onSelectDemo,
27+ onSwipeNextDemo,
28+ onSwipePrevDemo,
1929 isWebGPUWarningDismissed,
2030 onDismissWebGPUWarning,
2131} : HomepageProps ) {
@@ -24,6 +34,7 @@ export function Homepage({
2434 const { canvasRef, screenToWorld, isWebGPU, isInitialized, isInitializing } = useEngine ( ) ;
2535 const { setPosition, setActive, setMode, setStrength, setRadius } = useInteraction ( ) ;
2636 const isMouseDownRef = useRef ( false ) ;
37+ const touchStartRef = useRef < { x : number ; y : number ; time : number } | null > ( null ) ;
2738
2839 // Mouse and touch interaction for homepage demo
2940 useEffect ( ( ) => {
@@ -70,6 +81,12 @@ export function Homepage({
7081 setActive ( false ) ;
7182 } ;
7283
84+ const handleTouchStart = ( e : TouchEvent ) => {
85+ const touch = e . touches [ 0 ] ;
86+ if ( ! touch ) return ;
87+ touchStartRef . current = { x : touch . clientX , y : touch . clientY , time : Date . now ( ) } ;
88+ } ;
89+
7390 const handleTouchMove = ( e : TouchEvent ) => {
7491 e . preventDefault ( ) ;
7592 const touch = e . touches [ 0 ] ;
@@ -82,10 +99,30 @@ export function Homepage({
8299 setActive ( true ) ;
83100 } ;
84101
85- const handleTouchEnd = ( ) => {
102+ const handleTouchEnd = ( e : TouchEvent ) => {
103+ const touch = e . changedTouches [ 0 ] ;
104+ const start = touchStartRef . current ;
105+ touchStartRef . current = null ;
106+ if ( touch && start && demoCount > 0 ) {
107+ const deltaX = touch . clientX - start . x ;
108+ const deltaY = touch . clientY - start . y ;
109+ const elapsed = Date . now ( ) - start . time ;
110+ const horizontalSwipe = Math . abs ( deltaX ) > 60 && Math . abs ( deltaX ) > Math . abs ( deltaY ) * 1.3 ;
111+ const withinTime = elapsed < 600 ;
112+
113+ if ( horizontalSwipe && withinTime ) {
114+ if ( deltaX < 0 ) {
115+ onSwipeNextDemo ( ) ;
116+ } else {
117+ onSwipePrevDemo ( ) ;
118+ }
119+ }
120+ }
121+
86122 setActive ( false ) ;
87123 } ;
88124
125+ canvas . addEventListener ( "touchstart" , handleTouchStart , { passive : true } ) ;
89126 canvas . addEventListener ( "mousedown" , handleMouseDown ) ;
90127 canvas . addEventListener ( "mousemove" , handleMouseMove ) ;
91128 canvas . addEventListener ( "mouseup" , handleMouseUp ) ;
@@ -95,6 +132,7 @@ export function Homepage({
95132 canvas . addEventListener ( "touchcancel" , handleTouchEnd ) ;
96133
97134 return ( ) => {
135+ canvas . removeEventListener ( "touchstart" , handleTouchStart ) ;
98136 canvas . removeEventListener ( "mousedown" , handleMouseDown ) ;
99137 canvas . removeEventListener ( "mousemove" , handleMouseMove ) ;
100138 canvas . removeEventListener ( "mouseup" , handleMouseUp ) ;
@@ -104,8 +142,24 @@ export function Homepage({
104142 canvas . removeEventListener ( "touchcancel" , handleTouchEnd ) ;
105143 setActive ( false ) ;
106144 isMouseDownRef . current = false ;
145+ touchStartRef . current = null ;
107146 } ;
108- } , [ isVisible , showWarning , canvasRef , screenToWorld , setPosition , setActive , setMode , setStrength , setRadius , isWebGPU , isMobile ] ) ;
147+ } , [
148+ isVisible ,
149+ showWarning ,
150+ canvasRef ,
151+ screenToWorld ,
152+ setPosition ,
153+ setActive ,
154+ setMode ,
155+ setStrength ,
156+ setRadius ,
157+ isWebGPU ,
158+ isMobile ,
159+ demoCount ,
160+ onSwipeNextDemo ,
161+ onSwipePrevDemo ,
162+ ] ) ;
109163
110164 if ( ! isVisible ) return null ;
111165
@@ -171,6 +225,21 @@ export function Homepage({
171225 </ a >
172226 </ div >
173227 </ div >
228+ { demoCount > 0 && (
229+ < div className = "homepage-demo-dots" aria-label = "Demo selector" >
230+ { Array . from ( { length : demoCount } ) . map ( ( _ , index ) => (
231+ < button
232+ key = { `demo-dot-${ index } ` }
233+ className = { `homepage-demo-dot-button ${ index === currentDemoIndex ? "active" : "" } ` }
234+ onClick = { ( ) => onSelectDemo ( index ) }
235+ aria-pressed = { index === currentDemoIndex }
236+ aria-label = { `Show demo ${ index + 1 } ` }
237+ >
238+ < span className = "homepage-demo-dot" />
239+ </ button >
240+ ) ) }
241+ </ div >
242+ ) }
174243 </ >
175244 ) ;
176245}
0 commit comments