fix(map): enable fluid mobile pinch-to-zoom and tactile zoom controls without GPS follow lock (v20)
This commit is contained in:
+97
-17
@@ -92,6 +92,8 @@
|
||||
|
||||
// New 5% Driving Mode Enhancements
|
||||
let isFollowCarLocked = true;
|
||||
let isUserInteractingWithMap = false;
|
||||
let touchInteractionTimer = null;
|
||||
let radarRangeMode = 'city'; // 'city' (1,000 ft) or 'highway' (2,500 ft)
|
||||
let lastCautionSpokenCamId = null;
|
||||
|
||||
@@ -129,6 +131,7 @@
|
||||
btnLocPalmdaleEast: document.getElementById('btn-loc-palmdale-east'),
|
||||
btnLocPalmdaleSouth: document.getElementById('btn-loc-palmdale-south'),
|
||||
btnLocRancho: document.getElementById('btn-loc-rancho'),
|
||||
btnLocOverview: document.getElementById('btn-loc-overview'),
|
||||
mapSearchForm: document.getElementById('map-search-form'),
|
||||
mapSearchInput: document.getElementById('map-search-input'),
|
||||
|
||||
@@ -268,18 +271,26 @@
|
||||
|
||||
// --- LEAFLET MAP SETUP (HIGH-RESOLUTION ZERO-WATERMARK RADAR DARK MODE) ---
|
||||
function initLeafletMap() {
|
||||
// Center initially on Palmdale, California
|
||||
// Center initially on Palmdale, California with full responsive touch zoom
|
||||
map = L.map('radar-map', {
|
||||
center: [34.5950, -118.1350],
|
||||
zoom: 14,
|
||||
minZoom: 3,
|
||||
maxZoom: 19,
|
||||
zoomControl: false,
|
||||
attributionControl: false
|
||||
attributionControl: false,
|
||||
touchZoom: true,
|
||||
bounceAtZoomLimits: false,
|
||||
doubleClickZoom: true,
|
||||
scrollWheelZoom: true,
|
||||
dragging: true
|
||||
});
|
||||
window.flockMap = map;
|
||||
|
||||
// High-Resolution Radar Dark Tiles (Zero API Key, Zero Watermarks, Sub-Meter Street Clarity up to Zoom 19)
|
||||
darkTileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
subdomains: 'abc',
|
||||
minZoom: 3,
|
||||
maxZoom: 19,
|
||||
className: 'dark-radar-tile',
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
@@ -288,6 +299,7 @@
|
||||
// Prepare optional Satellite Layer (Zero API Key, Zero Watermarks)
|
||||
satelliteLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
||||
maxNativeZoom: 18,
|
||||
minZoom: 3,
|
||||
maxZoom: 19,
|
||||
attribution: '© Esri, Maxar, Earthstar Geographics'
|
||||
});
|
||||
@@ -295,19 +307,82 @@
|
||||
// High-contrast street labels overlay for satellite view (Zero Watermarks)
|
||||
satelliteLabelsLayer = L.tileLayer('https://services.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}', {
|
||||
maxNativeZoom: 18,
|
||||
minZoom: 3,
|
||||
maxZoom: 19
|
||||
});
|
||||
|
||||
// Minimal zoom controls on bottom-left
|
||||
// Tactile radar zoom controls (+ / -) on bottom-left
|
||||
L.control.zoom({ position: 'bottomleft' }).addTo(map);
|
||||
|
||||
// When the user drags the map, pause auto-centering and show Recenter button
|
||||
function setMapInteracting(active, delay = 0) {
|
||||
clearTimeout(touchInteractionTimer);
|
||||
if (delay > 0) {
|
||||
touchInteractionTimer = setTimeout(() => {
|
||||
isUserInteractingWithMap = false;
|
||||
}, delay);
|
||||
} else {
|
||||
isUserInteractingWithMap = active;
|
||||
}
|
||||
}
|
||||
|
||||
// Direct multi-touch gesture detection on map container
|
||||
const mapEl = document.getElementById('radar-map');
|
||||
if (mapEl) {
|
||||
mapEl.addEventListener('touchstart', (e) => {
|
||||
setMapInteracting(true);
|
||||
if (e.touches && e.touches.length >= 2) {
|
||||
// Pinch-to-zoom gesture in progress
|
||||
if (isFollowCarLocked) {
|
||||
isFollowCarLocked = false;
|
||||
updateFollowCarUI();
|
||||
}
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
mapEl.addEventListener('touchmove', (e) => {
|
||||
setMapInteracting(true);
|
||||
if (e.touches && e.touches.length >= 2) {
|
||||
if (isFollowCarLocked) {
|
||||
isFollowCarLocked = false;
|
||||
updateFollowCarUI();
|
||||
}
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
mapEl.addEventListener('touchend', () => {
|
||||
setMapInteracting(false, 600);
|
||||
}, { passive: true });
|
||||
|
||||
mapEl.addEventListener('touchcancel', () => {
|
||||
setMapInteracting(false, 600);
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Leaflet drag and zoom gesture hooks
|
||||
map.on('dragstart', () => {
|
||||
setMapInteracting(true);
|
||||
if (isFollowCarLocked) {
|
||||
isFollowCarLocked = false;
|
||||
updateFollowCarUI();
|
||||
}
|
||||
});
|
||||
|
||||
map.on('dragend', () => {
|
||||
setMapInteracting(false, 500);
|
||||
});
|
||||
|
||||
map.on('zoomstart', () => {
|
||||
setMapInteracting(true);
|
||||
// Unlock auto-center so user can freely inspect cameras at whatever zoom level they pick
|
||||
if (isFollowCarLocked) {
|
||||
isFollowCarLocked = false;
|
||||
updateFollowCarUI();
|
||||
}
|
||||
});
|
||||
|
||||
map.on('zoomend', () => {
|
||||
setMapInteracting(false, 500);
|
||||
});
|
||||
}
|
||||
|
||||
// --- LOAD CAMERA DATA (DEFAULTS + VERIFIED COMMUNITY + MUNICIPAL HUBS) ---
|
||||
@@ -726,7 +801,7 @@
|
||||
isFollowCarLocked = !isFollowCarLocked;
|
||||
updateFollowCarUI();
|
||||
if (isFollowCarLocked) {
|
||||
map.setView([currentPosition.lat, currentPosition.lng], Math.max(map.getZoom(), 15), { animate: true });
|
||||
map.panTo([currentPosition.lat, currentPosition.lng], { animate: true, duration: 0.5 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,7 +809,7 @@
|
||||
isFollowCarLocked = true;
|
||||
updateFollowCarUI();
|
||||
if (!isContinuousTracking && navigator.geolocation) {
|
||||
startContinuousGpsTracking({ forceRecenter: true, flyTo: true });
|
||||
startContinuousGpsTracking({ forceRecenter: true, centerMap: true });
|
||||
} else {
|
||||
map.panTo([currentPosition.lat, currentPosition.lng], { animate: true, duration: 0.5 });
|
||||
}
|
||||
@@ -1063,8 +1138,8 @@
|
||||
}
|
||||
|
||||
if (options.flyTo) {
|
||||
map.flyTo([curLat, curLng], 16, { duration: 1.0 });
|
||||
} else if (isFollowCarLocked) {
|
||||
map.flyTo([curLat, curLng], map.getZoom() || 15, { duration: 1.0 });
|
||||
} else if (isFollowCarLocked && !isUserInteractingWithMap) {
|
||||
map.panTo([curLat, curLng], { animate: true, duration: 0.5 });
|
||||
}
|
||||
|
||||
@@ -1148,12 +1223,9 @@
|
||||
userMarker.setLatLng([curLat, curLng]);
|
||||
}
|
||||
|
||||
// Map Following
|
||||
if (isFollowCarLocked) {
|
||||
// Map Following (smooth tracking at user's chosen zoom level, paused during active pinch/drag)
|
||||
if (isFollowCarLocked && !isUserInteractingWithMap) {
|
||||
map.panTo([curLat, curLng], { animate: true, duration: 0.5 });
|
||||
if (currentSpeed > 25 && map.getZoom() < 15) {
|
||||
map.setZoom(15);
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate camera proximity immediately on every movement step
|
||||
@@ -1227,7 +1299,7 @@
|
||||
isContinuousTracking = true;
|
||||
updateTrackingUI(true);
|
||||
if (lastGpsPosition && (options.forceRecenter || options.centerMap)) {
|
||||
map.setView([lastGpsPosition.lat, lastGpsPosition.lng], Math.max(map.getZoom(), 15), { animate: true });
|
||||
map.panTo([lastGpsPosition.lat, lastGpsPosition.lng], { animate: true, duration: 0.5 });
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1367,8 +1439,8 @@
|
||||
carElem.style.transform = `rotate(${angle}deg)`;
|
||||
}
|
||||
|
||||
// Smoothly pan map along if follow car is locked
|
||||
if (isFollowCarLocked) {
|
||||
// Smoothly pan map along if follow car is locked and user is not gesturing
|
||||
if (isFollowCarLocked && !isUserInteractingWithMap) {
|
||||
map.panTo([currentPosition.lat, currentPosition.lng], { animate: false });
|
||||
}
|
||||
|
||||
@@ -1519,7 +1591,7 @@
|
||||
if (lastGpsPosition) {
|
||||
isFollowCarLocked = true;
|
||||
updateFollowCarUI();
|
||||
map.flyTo([lastGpsPosition.lat, lastGpsPosition.lng], Math.max(map.getZoom(), 16), { duration: 1.0 });
|
||||
map.flyTo([lastGpsPosition.lat, lastGpsPosition.lng], map.getZoom() || 15, { duration: 1.0 });
|
||||
checkProximityToCameras();
|
||||
}
|
||||
|
||||
@@ -2178,6 +2250,14 @@
|
||||
jumpToLocation(34.63179, -118.12594, 'West AV (Rancho Vista & AV Mall)');
|
||||
});
|
||||
}
|
||||
if (el.btnLocOverview) {
|
||||
el.btnLocOverview.addEventListener('click', () => {
|
||||
isFollowCarLocked = false;
|
||||
updateFollowCarUI();
|
||||
map.flyTo([34.6300, -118.1300], 11, { duration: 1.0 });
|
||||
showToast('Zoomed out: Showing all Antelope Valley Flock cameras');
|
||||
});
|
||||
}
|
||||
|
||||
// Address & City Search
|
||||
if (el.mapSearchForm) {
|
||||
|
||||
Reference in New Issue
Block a user