Skip to content

Commit 0d9e000

Browse files
slop
1 parent 0d9e000 commit 0d9e000

File tree

2 files changed

+176
-9
lines changed

2 files changed

+176
-9
lines changed

static/kvh/app.js

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,32 @@ function loadVideo(src) {
2424
const physicsData = [];
2525
const 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
2837
function 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

3355
function setBallSize(video, radius) {
@@ -39,8 +61,9 @@ function setBallSize(video, radius) {
3961
function 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

79102
function 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
200227
window.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
203328
startNextDownload(0);

static/kvh/index.html

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,61 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
66
<title>KVH</title>
77
<style>
8+
* {
9+
box-sizing: border-box;
10+
}
11+
12+
html, body {
13+
margin: 0;
14+
padding: 0;
15+
width: 100%;
16+
height: 100%;
17+
overflow: hidden;
18+
background: gray;
19+
position: fixed;
20+
-webkit-overflow-scrolling: touch;
21+
}
22+
23+
main {
24+
width: 100vw;
25+
height: 100vh;
26+
position: relative;
27+
overflow: hidden;
28+
}
29+
830
video {
931
position: absolute;
1032
border-radius: 50%;
1133
object-fit: cover;
1234
width: 70px;
1335
height: 70px;
36+
pointer-events: none;
37+
-webkit-user-select: none;
38+
-moz-user-select: none;
39+
-ms-user-select: none;
40+
user-select: none;
41+
}
42+
43+
/* Prevent zoom on double tap */
44+
body {
45+
touch-action: manipulation;
46+
}
47+
48+
/* Handle safe areas on mobile devices */
49+
@supports (padding: max(0px)) {
50+
main {
51+
padding-left: max(0px, env(safe-area-inset-left));
52+
padding-right: max(0px, env(safe-area-inset-right));
53+
padding-top: max(0px, env(safe-area-inset-top));
54+
padding-bottom: max(0px, env(safe-area-inset-bottom));
55+
}
1456
}
1557
</style>
1658
</head>
17-
<body style="margin:0;overflow:hidden;background:gray">
59+
<body>
1860
<main></main>
1961
<script src="app.js"></script>
2062
</body>

0 commit comments

Comments
 (0)