Fix flyover button clickability and camera positioning
This commit is contained in:
@@ -9,10 +9,12 @@ const APP = () => {
|
|||||||
const mapRef = useRef(null);
|
const mapRef = useRef(null);
|
||||||
const startMarkerRef = useRef(null);
|
const startMarkerRef = useRef(null);
|
||||||
const cityMarkersRef = useRef([]);
|
const cityMarkersRef = useRef([]);
|
||||||
|
const animationRef = useRef(null);
|
||||||
const [selectedPoint, setSelectedPoint] = useState({ lat: 51.5074, lon: -0.1278 });
|
const [selectedPoint, setSelectedPoint] = useState({ lat: 51.5074, lon: -0.1278 });
|
||||||
const [direction, setDirection] = useState(45);
|
const [direction, setDirection] = useState(45);
|
||||||
const [lineOfSightData, setLineOfSightData] = useState(null);
|
const [lineOfSightData, setLineOfSightData] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [mapStyle, setMapStyle] = useState('light'); // 'light' or 'dark'
|
const [mapStyle, setMapStyle] = useState('light'); // 'light' or 'dark'
|
||||||
const [tolerance, setTolerance] = useState(50);
|
const [tolerance, setTolerance] = useState(50);
|
||||||
const [selectedCity, setSelectedCity] = useState(null);
|
const [selectedCity, setSelectedCity] = useState(null);
|
||||||
@@ -74,6 +76,7 @@ const APP = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
if (animationRef.current) cancelAnimationFrame(animationRef.current);
|
||||||
if (mapRef.current) mapRef.current.remove();
|
if (mapRef.current) mapRef.current.remove();
|
||||||
};
|
};
|
||||||
}, []); // Run only once
|
}, []); // Run only once
|
||||||
@@ -108,6 +111,11 @@ const APP = () => {
|
|||||||
}, [isLocked]);
|
}, [isLocked]);
|
||||||
|
|
||||||
const handleStartAgain = () => {
|
const handleStartAgain = () => {
|
||||||
|
if (animationRef.current) {
|
||||||
|
cancelAnimationFrame(animationRef.current);
|
||||||
|
setIsPlaying(false);
|
||||||
|
}
|
||||||
|
|
||||||
setIsLocked(false);
|
setIsLocked(false);
|
||||||
setLineOfSightData(null);
|
setLineOfSightData(null);
|
||||||
setSelectedCity(null);
|
setSelectedCity(null);
|
||||||
@@ -122,6 +130,14 @@ const APP = () => {
|
|||||||
if (mapRef.current.getLayer('preview-line')) {
|
if (mapRef.current.getLayer('preview-line')) {
|
||||||
mapRef.current.setLayoutProperty('preview-line', 'visibility', 'visible');
|
mapRef.current.setLayoutProperty('preview-line', 'visibility', 'visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset map pitch and zoom
|
||||||
|
mapRef.current.flyTo({
|
||||||
|
center: [selectedPoint.lon, selectedPoint.lat],
|
||||||
|
zoom: 3,
|
||||||
|
pitch: 0,
|
||||||
|
bearing: 0
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,6 +148,60 @@ const APP = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const startFlyOver = () => {
|
||||||
|
if (!lineOfSightData || !mapRef.current) return;
|
||||||
|
|
||||||
|
if (isPlaying) {
|
||||||
|
if (animationRef.current) cancelAnimationFrame(animationRef.current);
|
||||||
|
setIsPlaying(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsPlaying(true);
|
||||||
|
|
||||||
|
const coordinates = lineOfSightData.line_coordinates.map(c => [c.lon, c.lat]);
|
||||||
|
const route = turf.lineString(coordinates);
|
||||||
|
const duration = 20000; // 20 seconds for the full flyover
|
||||||
|
const startTime = performance.now();
|
||||||
|
const routeDistance = turf.length(route);
|
||||||
|
|
||||||
|
function animate(currentTime) {
|
||||||
|
if (!mapRef.current) return;
|
||||||
|
|
||||||
|
const elapsed = currentTime - startTime;
|
||||||
|
const phase = Math.min(elapsed / duration, 1); // 0 to 1
|
||||||
|
|
||||||
|
// Stop when we reach the end
|
||||||
|
if (phase >= 1) {
|
||||||
|
setIsPlaying(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDist = routeDistance * phase;
|
||||||
|
|
||||||
|
// Look 100km ahead, camera is at the current distance
|
||||||
|
const target = turf.along(route, Math.min(currentDist + 100, routeDistance)).geometry.coordinates;
|
||||||
|
const eye = turf.along(route, currentDist).geometry.coordinates;
|
||||||
|
|
||||||
|
const camera = mapRef.current.getFreeCameraOptions();
|
||||||
|
|
||||||
|
// Position camera 50,000m (50km) above the 'eye' point
|
||||||
|
camera.position = maplibregl.MercatorCoordinate.fromLngLat(
|
||||||
|
{ lng: eye[0], lat: eye[1] },
|
||||||
|
50000
|
||||||
|
);
|
||||||
|
|
||||||
|
// Look at the target point
|
||||||
|
camera.lookAtPoint({ lng: target[0], lat: target[1] });
|
||||||
|
|
||||||
|
mapRef.current.setFreeCameraOptions(camera);
|
||||||
|
|
||||||
|
animationRef.current = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
animationRef.current = requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Update preview whenever point or direction changes
|
// Update preview whenever point or direction changes
|
||||||
if (mapRef.current && mapRef.current.isStyleLoaded()) {
|
if (mapRef.current && mapRef.current.isStyleLoaded()) {
|
||||||
|
|||||||
@@ -126,16 +126,13 @@
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled-controls input, .disabled-controls .action-btn {
|
.disabled-controls input {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn-secondary {
|
/* Allow action buttons and settings buttons to work even when locked */
|
||||||
pointer-events: auto !important;
|
.action-btn, .action-btn-secondary, .setting-row button {
|
||||||
}
|
|
||||||
|
|
||||||
.setting-row button {
|
|
||||||
pointer-events: auto !important;
|
pointer-events: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user