Skip to content

Commit 288cbbd

Browse files
authored
Merge pull request #5 from dnathe4th/claude/compass-redesign-011CUu4h2RCLCqms8gH6haLQ
Redesign compass with horizon view and smooth tracking
2 parents 5d3c39f + b5d3481 commit 288cbbd

File tree

1 file changed

+136
-48
lines changed

1 file changed

+136
-48
lines changed

ocean-crosser.html

Lines changed: 136 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -50,64 +50,103 @@
5050
align-items: center;
5151
justify-content: center;
5252
position: relative;
53+
overflow: hidden;
5354
}
5455

56+
/* Horizon view - like looking at the ocean */
5557
#compass {
56-
width: 300px;
57-
height: 300px;
58-
border-radius: 50%;
59-
background: rgba(0, 0, 0, 0.5);
60-
border: 5px solid rgba(255, 255, 255, 0.8);
58+
width: 100%;
59+
height: 100%;
6160
position: relative;
61+
display: flex;
62+
flex-direction: column;
63+
justify-content: center;
64+
align-items: center;
65+
}
66+
67+
/* Compass strip that scrolls horizontally */
68+
#compass-strip {
69+
position: absolute;
70+
top: 50%;
71+
left: 0;
72+
right: 0;
73+
height: 80px;
74+
background: rgba(0, 0, 0, 0.6);
75+
border-top: 2px solid rgba(255, 255, 255, 0.5);
76+
border-bottom: 2px solid rgba(255, 255, 255, 0.5);
77+
display: flex;
78+
align-items: center;
79+
overflow: hidden;
80+
}
81+
82+
#compass-tape {
83+
display: flex;
84+
position: absolute;
85+
height: 100%;
6286
transition: transform 0.1s ease-out;
87+
white-space: nowrap;
6388
}
6489

65-
#compass-arrow {
90+
.compass-mark {
91+
display: inline-flex;
92+
flex-direction: column;
93+
align-items: center;
94+
justify-content: center;
95+
width: 60px;
96+
font-weight: bold;
97+
color: rgba(255, 255, 255, 0.8);
98+
}
99+
100+
.compass-mark.major {
101+
font-size: 20px;
102+
color: #FFD700;
103+
}
104+
105+
/* Center crosshair */
106+
#crosshair {
66107
position: absolute;
67108
top: 50%;
68109
left: 50%;
69-
transform: translate(-50%, -50%) rotate(0deg);
70-
width: 0;
71-
height: 0;
72-
border-left: 20px solid transparent;
73-
border-right: 20px solid transparent;
74-
border-bottom: 120px solid #FF4444;
75-
transition: transform 0.1s ease-out;
110+
transform: translate(-50%, -50%);
111+
width: 40px;
112+
height: 40px;
113+
pointer-events: none;
114+
z-index: 100;
76115
}
77116

78-
#compass-arrow::after {
117+
#crosshair::before,
118+
#crosshair::after {
79119
content: '';
80120
position: absolute;
81-
top: 120px;
82-
left: -20px;
83-
width: 0;
84-
height: 0;
85-
border-left: 20px solid transparent;
86-
border-right: 20px solid transparent;
87-
border-top: 120px solid #FFFFFF;
121+
background: #FF4444;
88122
}
89123

90-
.compass-label {
91-
position: absolute;
92-
font-weight: bold;
93-
font-size: 20px;
94-
color: white;
124+
#crosshair::before {
125+
left: 50%;
126+
top: 0;
127+
width: 3px;
128+
height: 100%;
129+
margin-left: -1.5px;
95130
}
96131

97-
.compass-label.n { top: 10px; left: 50%; transform: translateX(-50%); }
98-
.compass-label.e { right: 10px; top: 50%; transform: translateY(-50%); }
99-
.compass-label.s { bottom: 10px; left: 50%; transform: translateX(-50%); }
100-
.compass-label.w { left: 10px; top: 50%; transform: translateY(-50%); }
132+
#crosshair::after {
133+
top: 50%;
134+
left: 0;
135+
height: 3px;
136+
width: 100%;
137+
margin-top: -1.5px;
138+
}
101139

102140
#heading-display {
103141
position: absolute;
104-
top: 50%;
142+
top: 20%;
105143
left: 50%;
106-
transform: translate(-50%, -50%);
107-
font-size: 48px;
144+
transform: translateX(-50%);
145+
font-size: 64px;
108146
font-weight: bold;
109-
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
147+
text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.8);
110148
z-index: 10;
149+
color: #FFD700;
111150
}
112151

113152
#destination {
@@ -186,12 +225,11 @@ <h1>🌊 Ocean Crosser</h1>
186225

187226
<div id="compass-container">
188227
<div id="compass">
189-
<div class="compass-label n">N</div>
190-
<div class="compass-label e">E</div>
191-
<div class="compass-label s">S</div>
192-
<div class="compass-label w">W</div>
193-
<div id="compass-arrow"></div>
194228
<div id="heading-display">--°</div>
229+
<div id="crosshair"></div>
230+
<div id="compass-strip">
231+
<div id="compass-tape"></div>
232+
</div>
195233
</div>
196234
</div>
197235

@@ -314,13 +352,15 @@ <h1>🌊 Ocean Crosser</h1>
314352
// State
315353
let userLocation = null;
316354
let currentHeading = 0;
355+
let smoothedHeading = 0;
317356
let isCompassActive = false;
318357
let debugMode = true; // Set to true to see debug info
358+
const SMOOTHING_FACTOR = 0.15; // Lower = smoother but slower response
319359

320360
// DOM elements
321361
const statusEl = document.getElementById('status');
322362
const headingDisplayEl = document.getElementById('heading-display');
323-
const compassArrowEl = document.getElementById('compass-arrow');
363+
const compassTapeEl = document.getElementById('compass-tape');
324364
const destinationNameEl = document.getElementById('destination-name');
325365
const distanceEl = document.getElementById('distance');
326366
const locationInfoEl = document.getElementById('location-info');
@@ -331,6 +371,33 @@ <h1>🌊 Ocean Crosser</h1>
331371
debugEl.style.display = 'block';
332372
}
333373

374+
// Create compass tape with cardinal directions
375+
function createCompassTape() {
376+
const directions = [
377+
{ deg: 0, label: 'N', major: true },
378+
{ deg: 45, label: 'NE', major: false },
379+
{ deg: 90, label: 'E', major: true },
380+
{ deg: 135, label: 'SE', major: false },
381+
{ deg: 180, label: 'S', major: true },
382+
{ deg: 225, label: 'SW', major: false },
383+
{ deg: 270, label: 'W', major: true },
384+
{ deg: 315, label: 'NW', major: false }
385+
];
386+
387+
// Create tape 3 times to allow seamless wrapping
388+
for (let repeat = 0; repeat < 3; repeat++) {
389+
directions.forEach(dir => {
390+
const mark = document.createElement('div');
391+
mark.className = dir.major ? 'compass-mark major' : 'compass-mark';
392+
mark.textContent = dir.label;
393+
mark.dataset.degree = dir.deg;
394+
compassTapeEl.appendChild(mark);
395+
});
396+
}
397+
}
398+
399+
createCompassTape();
400+
334401
// Utility functions
335402
function log(message) {
336403
console.log(message);
@@ -348,6 +415,18 @@ <h1>🌊 Ocean Crosser</h1>
348415
return radians * 180 / Math.PI;
349416
}
350417

418+
// Smooth heading changes to reduce jitter
419+
function smoothHeading(newHeading, oldHeading) {
420+
// Handle angle wrapping (359° to 1° transition)
421+
let diff = newHeading - oldHeading;
422+
if (diff > 180) diff -= 360;
423+
if (diff < -180) diff += 360;
424+
425+
let smoothed = oldHeading + (diff * SMOOTHING_FACTOR);
426+
smoothed = (smoothed + 360) % 360; // Normalize to 0-360
427+
return smoothed;
428+
}
429+
351430
// Calculate distance between two points using Haversine formula
352431
function calculateDistance(lat1, lon1, lat2, lon2) {
353432
const R = 6371; // Earth's radius in km
@@ -479,8 +558,10 @@ <h1>🌊 Ocean Crosser</h1>
479558
}
480559

481560
if (heading !== null) {
482-
currentHeading = Math.round(heading);
483-
updateDisplay(currentHeading);
561+
currentHeading = heading;
562+
// Apply smoothing to reduce jitter
563+
smoothedHeading = orientationEventCount === 1 ? heading : smoothHeading(heading, smoothedHeading);
564+
updateDisplay(Math.round(smoothedHeading));
484565
} else {
485566
if (orientationEventCount === 1) log('Warning: No valid heading data available');
486567
}
@@ -497,19 +578,26 @@ <h1>🌊 Ocean Crosser</h1>
497578
absoluteEventCount++;
498579

499580
if (event.absolute && event.alpha !== null) {
500-
currentHeading = Math.round(360 - event.alpha);
501-
updateDisplay(currentHeading);
581+
let heading = 360 - event.alpha;
582+
currentHeading = heading;
583+
// Apply smoothing to reduce jitter
584+
smoothedHeading = absoluteEventCount === 1 ? heading : smoothHeading(heading, smoothedHeading);
585+
updateDisplay(Math.round(smoothedHeading));
502586
}
503587
}
504588

505589
// Update display with current heading
506590
function updateDisplay(heading) {
507-
// Update compass arrow rotation
508-
compassArrowEl.style.transform = `translate(-50%, -50%) rotate(${heading}deg)`;
509-
510591
// Update heading display
511592
headingDisplayEl.textContent = `${heading}°`;
512593

594+
// Move compass tape (negative because we want the tape to move opposite of heading)
595+
// 60px per direction mark, 8 marks = 480px for 360°
596+
// Middle repeat starts at -480px (8 marks * 60px)
597+
const pixelsPerDegree = 480 / 360; // 1.333px per degree
598+
const offset = -heading * pixelsPerDegree - 480; // -480 to start at middle repeat
599+
compassTapeEl.style.transform = `translateX(${offset}px)`;
600+
513601
// Find destination in this direction
514602
const destination = findDestinationInDirection(
515603
userLocation.lat,
@@ -520,7 +608,7 @@ <h1>🌊 Ocean Crosser</h1>
520608
if (destination) {
521609
destinationNameEl.textContent = destination.name;
522610
distanceEl.textContent = `${formatDistance(destination.distance)} away`;
523-
locationInfoEl.textContent = `Bearing: ${Math.round(destination.bearing)}° | Match: ${(30 - destination.angleDiff).toFixed(0)}% accuracy`;
611+
locationInfoEl.textContent = `Bearing: ${Math.round(destination.bearing)}° | Match accuracy: ${Math.round((30 - destination.angleDiff) / 30 * 100)}%`;
524612
statusEl.textContent = 'Tracking...';
525613
} else {
526614
destinationNameEl.textContent = 'Open Ocean';

0 commit comments

Comments
 (0)