perf(monitor): isolate follow viewport events

This commit is contained in:
lingniu
2026-07-16 10:41:41 +08:00
parent e0a5ba6f5d
commit 9384d6acf5
2 changed files with 25 additions and 1 deletions

View File

@@ -14,7 +14,7 @@ const markerSetMap = vi.fn();
const markerSetPosition = vi.fn();
const labelSetPosition = vi.fn();
const setZoomAndCenter = vi.fn();
const setCenter = vi.fn();
const setCenter = vi.fn(() => mapHandlers.get('moveend')?.({}));
const panTo = vi.fn();
const mapOff = vi.fn();
const mapDestroy = vi.fn();
@@ -521,6 +521,24 @@ test('renders one selected plate and smoothly follows it until the map is dragge
expect((hiddenFloatingLabels[0].options.text as { content: string }).content).toBe('粤A12345');
});
test('does not treat intermediate programmatic follow frames as user viewport changes', async () => {
useFastAnimationFrames();
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
window.AMapLoader = { load: vi.fn(async () => amapMock()) };
const onViewportChange = vi.fn();
const view = render(<FleetMap vehicles={[]} monitorMap={pointMap} selectedVin="LTEST000000000001" onSelect={() => undefined} onViewportChange={onViewportChange} />);
await waitFor(() => expect(onViewportChange).toHaveBeenCalled());
onViewportChange.mockReset();
view.rerender(<FleetMap vehicles={[]} monitorMap={{
...pointMap,
points: [{ ...pointMap.points[0], longitude: 113.27, latitude: 23.14 }, pointMap.points[1]]
}} selectedVin="LTEST000000000001" onSelect={() => undefined} onViewportChange={onViewportChange} />);
await waitFor(() => expect(setCenter).toHaveBeenLastCalledWith(wgs84ToGcj02(113.27, 23.14)), { timeout: 1_800 });
await waitFor(() => expect(onViewportChange).toHaveBeenCalledTimes(1));
});
test('renders every nearby plate with staggered positions at maximum zoom', async () => {
getZoom.mockReturnValue(20);
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };

View File

@@ -302,6 +302,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
const selectionPositionRef = useRef('');
const centeredVinRef = useRef('');
const followSelectedRef = useRef(true);
const programmaticFollowRef = useRef(false);
const selectedVinRef = useRef(selectedVin);
const [state, setState] = useState<'loading' | 'ready' | 'fallback' | 'error'>('loading');
const [loadAttempt, setLoadAttempt] = useState(0);
@@ -389,6 +390,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
const denseLabels = AMap.LabelsLayer ? new AMap.LabelsLayer({ zooms: [19, 20], zIndex: 110, collision: false, allowCollision: true }) : null;
labels?.setMap(map);
notifyViewport = () => {
if (programmaticFollowRef.current) return;
setMapZoom(map.getZoom?.() ?? 5);
window.clearTimeout(viewportTimerRef.current);
viewportTimerRef.current = window.setTimeout(() => {
@@ -400,6 +402,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
map.on?.('zoomend', notifyViewport);
stopFollowing = () => {
if (!centeredVinRef.current) return;
programmaticFollowRef.current = false;
followSelectedRef.current = false;
setFollowSelected(false);
};
@@ -431,6 +434,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
pointAnimationFrameRef.current = undefined;
pointAnimationTimerRef.current = undefined;
movingPointIDsRef.current.clear();
programmaticFollowRef.current = false;
if (selectMassPoint) massInstance?.off?.('click', selectMassPoint);
if (notifyViewport) {
mapInstance?.off?.('moveend', notifyViewport);
@@ -493,6 +497,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
window.clearTimeout(pointAnimationTimerRef.current);
pointAnimationFrameRef.current = undefined;
pointAnimationTimerRef.current = undefined;
programmaticFollowRef.current = false;
const previousData = renderedMassDataRef.current;
const previousDataByID = new Map(previousData.map((point) => [point.id, point]));
const vehiclePointIDs = new Set(monitorMode ? mapPoints.map((point) => point.vin) : fallbackPoints.map((point) => point.vin));
@@ -531,6 +536,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
selectionRef.current?.setPosition?.(motion.point.lnglat);
const motionComplete = elapsed >= motion.durationMs;
if (followSelectedRef.current && !selectedFollowComplete && (elapsed - lastFollowAt >= MAP_FOLLOW_FRAME_MS || motionComplete)) {
programmaticFollowRef.current = !motionComplete;
mapRef.current?.setCenter?.(motion.point.lnglat);
lastFollowAt = elapsed;
selectedFollowComplete = motionComplete;