refactor: rename map feature types and improve path following animation with flyTo transitions

This commit is contained in:
taDuc
2026-06-17 00:51:22 +07:00
parent c8a16573ca
commit 85166c87ef
2 changed files with 56 additions and 15 deletions
+2 -2
View File
@@ -284,7 +284,7 @@ export function useMapInteraction({
type: "Feature", type: "Feature",
properties: { properties: {
id, id,
type: "attack_route", type: "military_route",
geometry_preset: "line", geometry_preset: "line",
entity_id: null, entity_id: null,
entity_ids: [], entity_ids: [],
@@ -306,7 +306,7 @@ export function useMapInteraction({
type: "Feature", type: "Feature",
properties: { properties: {
id, id,
type: "war", type: "battle",
geometry_preset: "circle-area", geometry_preset: "circle-area",
entity_id: null, entity_id: null,
entity_ids: [], entity_ids: [],
+46 -5
View File
@@ -259,8 +259,11 @@ export function createReplayMapEffects() {
const path = removeDuplicateCoordinates(coordinates); const path = removeDuplicateCoordinates(coordinates);
if (path.length === 0) return; if (path.length === 0) return;
if (path.length === 1) { if (path.length === 1) {
map.easeTo({ map.flyTo({
center: path[0], center: path[0],
zoom: typeof zoom === "number" ? zoom : map.getZoom(),
pitch: map.getPitch(),
bearing: map.getBearing(),
duration: clampNumber(duration, 250, 60000, 5000), duration: clampNumber(duration, 250, 60000, 5000),
}); });
return; return;
@@ -272,35 +275,73 @@ export function createReplayMapEffects() {
if (totalDistance <= 0) return; if (totalDistance <= 0) return;
const totalDuration = clampNumber(duration, 250, 60000, 5000); const totalDuration = clampNumber(duration, 250, 60000, 5000);
const startedAt = performance.now();
// Allocate flyDuration dynamically based on the total step duration
let flyDuration = 1500;
if (totalDuration < 3000) {
flyDuration = Math.round(totalDuration * 0.4);
} else if (totalDuration < 4500) {
flyDuration = 1200;
}
const followDuration = Math.max(100, totalDuration - flyDuration);
let rafId = 0; let rafId = 0;
let flyTimeoutId: NodeJS.Timeout | null = null;
let unregister: Cleanup | null = null; let unregister: Cleanup | null = null;
let onMoveStart: ((e: any) => void) | null = null;
const stop = () => { const stop = () => {
if (flyTimeoutId) {
clearTimeout(flyTimeoutId);
flyTimeoutId = null;
}
if (rafId) { if (rafId) {
cancelAnimationFrame(rafId); cancelAnimationFrame(rafId);
rafId = 0; rafId = 0;
} }
if (onMoveStart) {
map.off("movestart", onMoveStart);
onMoveStart = null;
}
unregister?.(); unregister?.();
unregister = null; unregister = null;
}; };
onMoveStart = (e: any) => {
if (e && e.isFollowPath) {
return;
}
stop();
};
map.on("movestart", onMoveStart);
map.flyTo({
center: path[0],
zoom: typeof zoom === "number" ? zoom : map.getZoom(),
pitch: map.getPitch(),
bearing: map.getBearing(),
duration: flyDuration,
}, { isFollowPath: true });
flyTimeoutId = setTimeout(() => {
const startedAt = performance.now();
const tick = (now: number) => { const tick = (now: number) => {
const progress = Math.min(1, (now - startedAt) / totalDuration); const progress = Math.min(1, (now - startedAt) / followDuration);
const targetDistance = totalDistance * progress; const targetDistance = totalDistance * progress;
const center = interpolateMeasuredPath(measured, targetDistance); const center = interpolateMeasuredPath(measured, targetDistance);
map.jumpTo({ map.jumpTo({
center, center,
}); }, { isFollowPath: true });
if (progress >= 1) { if (progress >= 1) {
stop(); stop();
return; return;
} }
rafId = requestAnimationFrame(tick); rafId = requestAnimationFrame(tick);
}; };
rafId = requestAnimationFrame(tick);
}, flyDuration);
unregister = registerCleanup(stop); unregister = registerCleanup(stop);
rafId = requestAnimationFrame(tick);
}, },
}; };
} }