Fix marker stability and reduce image preview size
- Use deterministic hash-based offset calculation for stable marker positions - Markers now stay in same position across zoom/pan operations - Check geographic distance instead of pixel distance for clustering - Reduce image preview from 60px to 40px for compact modal - Reduce button font size to save space Fixes: Markers no longer jump around when zooming Fixes: Modal stays compact with image preview 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c3e9426e64
commit
d845fd7a5a
|
|
@ -608,7 +608,7 @@ export default function MapView({
|
||||||
<img
|
<img
|
||||||
src={draftImagePreview}
|
src={draftImagePreview}
|
||||||
alt="Preview"
|
alt="Preview"
|
||||||
style={{ width: "60px", height: "60px", objectFit: "cover", borderRadius: "8px" }}
|
style={{ width: "40px", height: "40px", objectFit: "cover", borderRadius: "6px" }}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -617,7 +617,7 @@ export default function MapView({
|
||||||
setDraftImagePreview("");
|
setDraftImagePreview("");
|
||||||
setDraftImage("");
|
setDraftImage("");
|
||||||
}}
|
}}
|
||||||
style={{ padding: "4px 8px", fontSize: "12px" }}
|
style={{ padding: "4px 8px", fontSize: "11px" }}
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,18 @@ function openCenteredOverlay({ map, post, theme }) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hashPostId(id) {
|
||||||
|
// Simple hash function for stable offset calculation
|
||||||
|
const str = String(id);
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const char = str.charCodeAt(i);
|
||||||
|
hash = ((hash << 5) - hash) + char;
|
||||||
|
hash = hash & hash; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return Math.abs(hash);
|
||||||
|
}
|
||||||
|
|
||||||
function calculateSmartOffset(post, markersRef, map) {
|
function calculateSmartOffset(post, markersRef, map) {
|
||||||
if (!map || !markersRef.current) return { x: 0, y: 0 };
|
if (!map || !markersRef.current) return { x: 0, y: 0 };
|
||||||
|
|
||||||
|
|
@ -302,35 +314,31 @@ function calculateSmartOffset(post, markersRef, map) {
|
||||||
if (lat == null || lon == null) return { x: 0, y: 0 };
|
if (lat == null || lon == null) return { x: 0, y: 0 };
|
||||||
|
|
||||||
const currentPoint = map.project([lon, lat]);
|
const currentPoint = map.project([lon, lat]);
|
||||||
const PROXIMITY_THRESHOLD = 80; // pixels
|
const PROXIMITY_THRESHOLD = 100; // pixels - larger to detect more overlaps
|
||||||
const MAX_OFFSET = 60; // max offset in pixels
|
|
||||||
|
|
||||||
const nearbyMarkers = [];
|
// Check distance in geographic coordinates (more stable across zoom levels)
|
||||||
|
const nearbyCount = markersRef.current.filter(marker => {
|
||||||
for (const marker of markersRef.current) {
|
if (!marker.post || marker.post.id === post.id) return false;
|
||||||
if (!marker.post || marker.post.id === post.id) continue;
|
|
||||||
|
|
||||||
const mLat = typeof marker.post.lat === "number" ? marker.post.lat : marker.post.latitude;
|
const mLat = typeof marker.post.lat === "number" ? marker.post.lat : marker.post.latitude;
|
||||||
const mLon = typeof marker.post.lon === "number" ? marker.post.lon : marker.post.lng;
|
const mLon = typeof marker.post.lon === "number" ? marker.post.lon : marker.post.lng;
|
||||||
if (mLat == null || mLon == null) continue;
|
if (mLat == null || mLon == null) return false;
|
||||||
|
|
||||||
const mPoint = map.project([mLon, mLat]);
|
// Geographic distance (rough approximation)
|
||||||
const dist = Math.sqrt(
|
const latDiff = Math.abs(lat - mLat);
|
||||||
Math.pow(currentPoint.x - mPoint.x, 2) +
|
const lonDiff = Math.abs(lon - mLon);
|
||||||
Math.pow(currentPoint.y - mPoint.y, 2)
|
const geoDist = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);
|
||||||
);
|
|
||||||
|
|
||||||
if (dist < PROXIMITY_THRESHOLD) {
|
// Small threshold for geographic clustering (~100m at equator)
|
||||||
nearbyMarkers.push({ marker, distance: dist, point: mPoint });
|
return geoDist < 0.001;
|
||||||
}
|
}).length;
|
||||||
}
|
|
||||||
|
|
||||||
if (nearbyMarkers.length === 0) return { x: 0, y: 0 };
|
if (nearbyCount === 0) return { x: 0, y: 0 };
|
||||||
|
|
||||||
// Create a cascade effect: offset markers in a circular pattern
|
// Use deterministic hash-based offset so it's stable across zoom/pan
|
||||||
const index = nearbyMarkers.length;
|
const hash = hashPostId(post.id);
|
||||||
const angle = (index * 45) * (Math.PI / 180); // 45 degrees apart
|
const angle = (hash % 8) * 45 * (Math.PI / 180); // 8 positions, 45° apart
|
||||||
const offsetMagnitude = Math.min(20 + (index * 12), MAX_OFFSET);
|
const offsetMagnitude = 25 + ((hash % 3) * 15); // 25, 40, or 55 pixels
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: Math.cos(angle) * offsetMagnitude,
|
x: Math.cos(angle) * offsetMagnitude,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue