Reputation: 681
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
Can't find setPosition function yet...
Upvotes: 3
Views: 3093
Reputation: 515
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
Upvotes: 2
Reputation: 601
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
Upvotes: 0