@@ -24,10 +24,32 @@ function loadVideo(src) {
2424const physicsData = [ ] ;
2525const main = document . querySelector ( 'main' ) ;
2626
27+ // Get actual viewport dimensions accounting for mobile quirks
28+ function getViewportDimensions ( ) {
29+ const rect = main . getBoundingClientRect ( ) ;
30+ return {
31+ width : rect . width ,
32+ height : rect . height
33+ } ;
34+ }
35+
2736// Centralized ball size management
2837function getBallRadius ( ) {
29- const k = Math . round ( window . innerWidth / 200 ) ;
30- return window . innerWidth / ( 2 * k ) ;
38+ const viewport = getViewportDimensions ( ) ;
39+ const minDimension = Math . min ( viewport . width , viewport . height ) ;
40+
41+ // Better scaling for mobile devices
42+ if ( minDimension < 400 ) {
43+ // Small mobile screens
44+ return Math . max ( 25 , minDimension / 12 ) ;
45+ } else if ( minDimension < 800 ) {
46+ // Larger mobile screens and small tablets
47+ return Math . max ( 30 , minDimension / 15 ) ;
48+ } else {
49+ // Desktop and large tablets
50+ const k = Math . round ( viewport . width / 200 ) ;
51+ return viewport . width / ( 2 * k ) ;
52+ }
3153}
3254
3355function setBallSize ( video , radius ) {
@@ -39,8 +61,9 @@ function setBallSize(video, radius) {
3961function spawnVideo ( video ) {
4062 const radius = getBallRadius ( ) ;
4163 const diameter = radius * 2 ;
64+ const viewport = getViewportDimensions ( ) ;
4265
43- const x = Math . random ( ) * ( window . innerWidth - diameter ) ;
66+ const x = Math . random ( ) * ( viewport . width - diameter ) ;
4467 const y = 100 ;
4568 const vx = ( Math . random ( ) - 0.5 ) * 4 ;
4669 const vy = ( Math . random ( ) - 0.5 ) * 4 ;
@@ -77,6 +100,8 @@ async function startNextDownload(i) {
77100}
78101
79102function updatePhysics ( ) {
103+ const viewport = getViewportDimensions ( ) ;
104+
80105 for ( let i = 0 ; i < main . children . length ; i ++ ) {
81106 const video = main . children [ i ] ;
82107 const data = physicsData [ i ] ;
@@ -94,17 +119,18 @@ function updatePhysics() {
94119 const centerX = data . x + data . radius ;
95120 const centerY = data . y + data . radius ;
96121
97- if ( centerX - data . radius <= 0 || centerX + data . radius >= window . innerWidth ) {
122+ // Use viewport dimensions instead of window dimensions
123+ if ( centerX - data . radius <= 0 || centerX + data . radius >= viewport . width ) {
98124 data . vx = - data . vx * 0.8 ;
99125 data . vr *= 0.9 ;
100- data . x = Math . max ( 0 , Math . min ( window . innerWidth - data . radius * 2 , data . x ) ) ;
126+ data . x = Math . max ( 0 , Math . min ( viewport . width - data . radius * 2 , data . x ) ) ;
101127 }
102128
103- if ( centerY + data . radius >= window . innerHeight ) {
129+ if ( centerY + data . radius >= viewport . height ) {
104130 data . vy = - data . vy * 0.8 ;
105131 data . vr = data . vx / data . radius ; // no slip
106132 data . vx *= 0.95 ;
107- data . y = window . innerHeight - data . radius * 2 ;
133+ data . y = viewport . height - data . radius * 2 ;
108134 }
109135
110136 video . style . left = data . x + 'px' ;
@@ -197,7 +223,106 @@ function updateBallRadius() {
197223 }
198224}
199225
226+ // Handle both resize and orientation change events
200227window . addEventListener ( 'resize' , updateBallRadius ) ;
228+ window . addEventListener ( 'orientationchange' , function ( ) {
229+ // Delay to allow viewport to settle after orientation change
230+ setTimeout ( updateBallRadius , 100 ) ;
231+ } ) ;
232+
233+ // Add touch event handling for mobile interaction
234+ function addTouchInteraction ( ) {
235+ let touchStartTime = 0 ;
236+
237+ main . addEventListener ( 'touchstart' , function ( e ) {
238+ e . preventDefault ( ) ; // Prevent scrolling and zooming
239+ touchStartTime = Date . now ( ) ;
240+ } , { passive : false } ) ;
241+
242+ main . addEventListener ( 'touchmove' , function ( e ) {
243+ e . preventDefault ( ) ; // Prevent scrolling
244+ } , { passive : false } ) ;
245+
246+ main . addEventListener ( 'touchend' , function ( e ) {
247+ e . preventDefault ( ) ;
248+ const touchDuration = Date . now ( ) - touchStartTime ;
249+
250+ // If it's a quick tap (less than 200ms), add some energy to nearby balls
251+ if ( touchDuration < 200 && e . changedTouches . length > 0 ) {
252+ const touch = e . changedTouches [ 0 ] ;
253+ const rect = main . getBoundingClientRect ( ) ;
254+ const touchX = touch . clientX - rect . left ;
255+ const touchY = touch . clientY - rect . top ;
256+
257+ // Find balls near the touch point and give them a little push
258+ for ( let i = 0 ; i < physicsData . length ; i ++ ) {
259+ const data = physicsData [ i ] ;
260+ const ballCenterX = data . x + data . radius ;
261+ const ballCenterY = data . y + data . radius ;
262+ const distance = Math . sqrt (
263+ Math . pow ( touchX - ballCenterX , 2 ) +
264+ Math . pow ( touchY - ballCenterY , 2 )
265+ ) ;
266+
267+ // If touch is within 100px of ball center, give it a push
268+ if ( distance < 100 ) {
269+ const pushStrength = Math . max ( 0.5 , ( 100 - distance ) / 100 * 3 ) ;
270+ const angle = Math . atan2 ( ballCenterY - touchY , ballCenterX - touchX ) ;
271+ data . vx += Math . cos ( angle ) * pushStrength ;
272+ data . vy += Math . sin ( angle ) * pushStrength ;
273+ data . vr += ( Math . random ( ) - 0.5 ) * 0.2 ;
274+ }
275+ }
276+ }
277+ } , { passive : false } ) ;
278+ }
279+
280+ // Prevent context menu on long press
281+ main . addEventListener ( 'contextmenu' , function ( e ) {
282+ e . preventDefault ( ) ;
283+ } ) ;
284+
285+ // Initialize touch interaction
286+ addTouchInteraction ( ) ;
287+
288+ // Handle mobile viewport changes and prevent scrolling
289+ function initializeMobileHandling ( ) {
290+ // Prevent pull-to-refresh on mobile
291+ document . body . addEventListener ( 'touchstart' , function ( e ) {
292+ if ( e . touches . length > 1 ) {
293+ e . preventDefault ( ) ; // Prevent pinch zoom
294+ }
295+ } , { passive : false } ) ;
296+
297+ document . body . addEventListener ( 'touchend' , function ( e ) {
298+ if ( e . touches . length > 0 ) {
299+ e . preventDefault ( ) ;
300+ }
301+ } , { passive : false } ) ;
302+
303+ // Handle viewport height changes (mobile keyboard, etc.)
304+ let lastViewportHeight = window . innerHeight ;
305+ window . addEventListener ( 'resize' , function ( ) {
306+ const currentHeight = window . innerHeight ;
307+ if ( Math . abs ( currentHeight - lastViewportHeight ) > 100 ) {
308+ // Significant height change, likely keyboard or orientation
309+ setTimeout ( function ( ) {
310+ updateBallRadius ( ) ;
311+ // Ensure balls stay within new boundaries
312+ const viewport = getViewportDimensions ( ) ;
313+ for ( let i = 0 ; i < physicsData . length ; i ++ ) {
314+ const data = physicsData [ i ] ;
315+ data . x = Math . max ( 0 , Math . min ( viewport . width - data . radius * 2 , data . x ) ) ;
316+ data . y = Math . max ( 0 , Math . min ( viewport . height - data . radius * 2 , data . y ) ) ;
317+ }
318+ } , 300 ) ;
319+ }
320+ lastViewportHeight = currentHeight ;
321+ } ) ;
322+ }
323+
324+ // Initialize mobile handling
325+ initializeMobileHandling ( ) ;
201326
202327// Initialize the app
203328startNextDownload ( 0 ) ;
0 commit comments