Improve map UX: compact modal, visible crosshair, smart marker overlap
- Reduce create post modal height (max 45vh) and add scroll - Lower modal position (bottom: 8%) to keep map visible - Move crosshair higher (top: 30%) for better visibility - Implement smart marker offsetting for nearby posts - Add visual connection line from offset markers to true position - Create cascade effect for clustered markers (45° circular pattern) - Maintain pointer accuracy when zooming 🤖 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
2c4c9a7065
commit
c3e9426e64
|
|
@ -294,6 +294,50 @@ function openCenteredOverlay({ map, post, theme }) {
|
|||
};
|
||||
}
|
||||
|
||||
function calculateSmartOffset(post, markersRef, map) {
|
||||
if (!map || !markersRef.current) return { x: 0, y: 0 };
|
||||
|
||||
const lat = typeof post.lat === "number" ? post.lat : typeof post.latitude === "number" ? post.latitude : null;
|
||||
const lon = typeof post.lon === "number" ? post.lon : typeof post.lng === "number" ? post.lng : null;
|
||||
if (lat == null || lon == null) return { x: 0, y: 0 };
|
||||
|
||||
const currentPoint = map.project([lon, lat]);
|
||||
const PROXIMITY_THRESHOLD = 80; // pixels
|
||||
const MAX_OFFSET = 60; // max offset in pixels
|
||||
|
||||
const nearbyMarkers = [];
|
||||
|
||||
for (const marker of markersRef.current) {
|
||||
if (!marker.post || marker.post.id === post.id) continue;
|
||||
|
||||
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;
|
||||
if (mLat == null || mLon == null) continue;
|
||||
|
||||
const mPoint = map.project([mLon, mLat]);
|
||||
const dist = Math.sqrt(
|
||||
Math.pow(currentPoint.x - mPoint.x, 2) +
|
||||
Math.pow(currentPoint.y - mPoint.y, 2)
|
||||
);
|
||||
|
||||
if (dist < PROXIMITY_THRESHOLD) {
|
||||
nearbyMarkers.push({ marker, distance: dist, point: mPoint });
|
||||
}
|
||||
}
|
||||
|
||||
if (nearbyMarkers.length === 0) return { x: 0, y: 0 };
|
||||
|
||||
// Create a cascade effect: offset markers in a circular pattern
|
||||
const index = nearbyMarkers.length;
|
||||
const angle = (index * 45) * (Math.PI / 180); // 45 degrees apart
|
||||
const offsetMagnitude = Math.min(20 + (index * 12), MAX_OFFSET);
|
||||
|
||||
return {
|
||||
x: Math.cos(angle) * offsetMagnitude,
|
||||
y: Math.sin(angle) * offsetMagnitude
|
||||
};
|
||||
}
|
||||
|
||||
export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
|
@ -316,6 +360,9 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
|
||||
const lngLat = [lon, lat];
|
||||
|
||||
// Calculate smart offset for nearby markers
|
||||
const offset = calculateSmartOffset(post, markersRef, map);
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.classList.add("sw-appear");
|
||||
requestAnimationFrame(() => root.classList.add("sw-appear-in"));
|
||||
|
|
@ -334,9 +381,19 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
root.className = "post-pin post-pin--compact sw-appear sw-appear-in";
|
||||
root.style.zIndex = "1";
|
||||
|
||||
// Add connecting line if offset is significant
|
||||
const hasOffset = Math.abs(offset.x) > 5 || Math.abs(offset.y) > 5;
|
||||
const lineHTML = hasOffset
|
||||
? `<svg class="sw-offset-line" style="position:absolute;bottom:0;left:50%;transform:translateX(-50%);overflow:visible;pointer-events:none;z-index:-1;">
|
||||
<line x1="0" y1="0" x2="${-offset.x}" y2="${-offset.y}" stroke="rgba(56,189,248,0.4)" stroke-width="2" stroke-dasharray="4,4"/>
|
||||
<circle cx="${-offset.x}" cy="${-offset.y}" r="4" fill="#38bdf8" opacity="0.6"/>
|
||||
</svg>`
|
||||
: '';
|
||||
|
||||
root.innerHTML = `
|
||||
<div class="sw-template-mini-wrap"></div>
|
||||
<div class="post-pin-pointer-small"></div>
|
||||
${lineHTML}
|
||||
`;
|
||||
|
||||
const wrap = root.querySelector(".sw-template-mini-wrap");
|
||||
|
|
@ -352,7 +409,11 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
renderCompact();
|
||||
root.__renderCompact = renderCompact;
|
||||
|
||||
const marker = new maplibregl.Marker({ element: root, anchor: "bottom" })
|
||||
const marker = new maplibregl.Marker({
|
||||
element: root,
|
||||
anchor: "bottom",
|
||||
offset: [offset.x, offset.y]
|
||||
})
|
||||
.setLngLat(lngLat)
|
||||
.addTo(map);
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@
|
|||
/* Crosshair pour création */
|
||||
.map-crosshair{
|
||||
left:50%;
|
||||
top:40%;
|
||||
top:30%;
|
||||
transform:translate(-50%,-50%);
|
||||
}
|
||||
.crosshair-aim{
|
||||
|
|
@ -141,10 +141,12 @@
|
|||
/* Panneau de création */
|
||||
.create-post-panel{
|
||||
left:50%;
|
||||
bottom:20%;
|
||||
bottom:8%;
|
||||
transform:translateX(-50%);
|
||||
width:92%;
|
||||
max-width:520px;
|
||||
max-height:45vh;
|
||||
overflow-y:auto;
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
border-radius:16px;
|
||||
border:1px solid rgba(148, 163, 184, 0.9);
|
||||
|
|
|
|||
Loading…
Reference in New Issue