3

I'm trying to animate my google maps markers like this...

Example of markers animated movement

And i tried to replicate this code into react (using, @react-google-maps/api), but couldn't make it work, i'm having multiple problems, number one, would be that, i'm saving the movement using useState, but, since i have to update this data multiple times quickly, that can cause an actual issue in my component, since is it not recommended to call seState inside a for loop, or multiple times in miliseconds in general.

This is my code so far (Focus on the useEffect and makeMarkerMove function)

interface IProps {
    resourceData: IResourceMarkers;
}

interface ICustomGeo {
    lat: number;
    lng: number;
}

interface ICustomMovement {
    newPosition: ICustomGeo;
}

const ResourceMarkersHero: FC<IProps> = ({
    resourceData: {
        g: {
            geopoint: { _lat, _long },
        },
    },
}) => {
    const [selectedMarker, setSelectedMarker] = useState<boolean>(false);
    const [markerMotion, setMarkerMotion] = useState<ICustomMovement>({
        newPosition: { lat: _lat, lng: _long },
    });

    const makeMarkerMove = () => {
        const delay = 10;

        const speed = 100; // how fast will the marker move
        const deltaLat = (_lat - markerMotion.newPosition.lat) / speed;
        const deltaLng = (_long - markerMotion.newPosition.lng) / speed;

        for (let i = 0; i <= 100; i++) {
            
            const newLat = markerMotion.newPosition.lat + deltaLat;
            const newLng = markerMotion.newPosition.lng + deltaLng;

            setTimeout(() => {
                setMarkerMotion((prev) => ({
                    ...prev,
                    newPosition: {
                        ...prev.newPosition,
                        lat: newLat,
                        lng: newLng,
                    },
                }));
            }, delay);
        }
    };

    useEffect(() => {
        if (_lat !== markerMotion.newPosition.lat) makeMarkerMove();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [_lat, _long]);

    return (
        <>
            {disponibilidad && (
                <Marker
                    position={markerMotion.newPosition}
                >
                  
                </Marker>
            )}
        </>
    );
};

export default ResourceMarkersHero;

The code that i wrote is not updating the state, so, how can i animate my markers smoothly, like in the previous example, how can i change my code to make this work.

Thanks in advance !

IMPORTANT UPDATE 1.0

In google maps docs, you usually use the function setPosition, to update marker's position, the problem, is that, how would i be able to access that function in react, if i'm using react-google-maps/api in order to avoid the setState problem ?

Well, i tried to use useRef hook, to access <Marker />component like this.

const ResourceMarkersHero: FC<IProps> = ({
    resourceData: {
        codigo_tipo_vehiculo,
        g: {
            geopoint: { _lat, _long },
        },
        identificacion,
        nombre,
        disponibilidad,
        equipo,
        telefono,
    },
}) => {

    const [selectedMarker, setSelectedMarker] = useState<boolean>(false);
    const [markerMotion, setMarkerMotion] = useState<ICustomMovement>({
        newPosition: { lat: _lat, lng: _long },
    });

    const markerRef = useRef() as RefObject<Marker>;

    console.log(markerRef.current);

    const makeMarkerMove = () => {
        const delay = 10;

        const speed = 100; // how fast will the marker move
        const deltaLat = (_lat - markerMotion.newPosition.lat) / speed;
        const deltaLng = (_long - markerMotion.newPosition.lng) / speed;

        for (let i = 0; i <= 100; i++) {
            const newLat = markerMotion.newPosition.lat + deltaLat;
            const newLng = markerMotion.newPosition.lng + deltaLng;

            setTimeout(() => {
                setMarkerMotion((prev) => ({
                    ...prev,
                    newPosition: {
                        ...prev.newPosition,
                        lat: newLat,
                        lng: newLng,
                    },
                }));
            }, delay);
        }
    };

    useEffect(() => {
        if (_lat !== markerMotion.newPosition.lat) makeMarkerMove();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [_lat, _long]);

    return (
        <>
            {disponibilidad && (
                <Marker
                    onClick={() => setSelectedMarker(!selectedMarker)}
                    position={markerMotion.newPosition}
                    ref={markerRef}
                >
                    {selectedMarker && (
                        <InfoWindow onCloseClick={() => setSelectedMarker(false)}>
                            <ResourceMarkersModal
                                identificacion={identificacion}
                                nombre={nombre}
                                equipo={equipo}
                                telefono={telefono}
                            />
                        </InfoWindow>
                    )}
                </Marker>
            )}
        </>
    );
};

export default ResourceMarkersHero;

But, when i console.log(markerRef.current) this is what i get

enter image description here

Can't find setPosition function yet...

Diego
  • 421
  • 3
  • 9
  • 34

2 Answers2

2

You don't see setPosition methoud because it must be located at prototype (click on [[Prototype]] property in a marker object).

So you can use any example of marker animation from pure js (like in your answer), but I think better to use requestAnimationFrame for smooth animation. I created simple example for you how it will work with @react-google-maps/api.

https://codesandbox.io/s/google-maps-marker-animate-pos-3j78j5

As you can see we shouldn't use setState or forceUpdate for rernder and you can be sure that you won't have problem with perfomance.

Function for animate marker I took from this answer

https://stackoverflow.com/a/55043218/9058905

bitvalser
  • 465
  • 2
  • 5
  • This seems like a really good and complete asnwer, i'll give it a try ! Thanks for your effort ! – Diego Jun 09 '22 at 20:41
  • I've just implemented this solution, and markers are being animated which is crazy ! . There are a couple of details that still need to being work on, but this i a great approach, thanks for your help my friend – Diego Jul 19 '22 at 16:13
0

You can force rendering by using using reducer

const [, forceUpdate] = useReducer(x => x + 1, 0);

And in the end of your set timeout add this

setTimeout(() => {
setMarkerMotion((prev) => ({
YOUR OLD CODE HERE
forceUpdate();
 }, delay);

This will force the component to render every time

  • Is this recommended ? Like, what major problem would i have in that component if i let it update itself like that – Diego May 26 '22 at 20:48