I have wrote a React project with react-leaflet
and hook. My goal is to move and rotate marker every second. However, moving part is working fine. But the rotation of marker is not working. I am really stuck for last few days. I couldn't find a good solution. Please help me.
The map component is as follows.
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import { useState, useEffect } from "react";
import L from 'leaflet';
import 'leaflet-marker-rotation';
const MySimpleMap = () => {
const [lat, setLat] = useState(22.899397);
const [lon, setLon] = useState(89.508279);
const [heading, setHeading] = useState(30)
useEffect(() => {
const interval = setInterval(() => {
myfun();
}, 1000);
return () => {
clearInterval(interval);
};
}, [lat]);
const defaultIcon = L.icon({
iconUrl: "https://unpkg.com/leaflet@1.0.3/dist/images/marker-icon.png",
iconSize: [20, 40],
iconAnchor: [18, 18],
popupAnchor: [0, -10],
shadowAnchor: [10, 10]
});
const myfun = () => {
setLat(lat + 0.00001);
setLon(lon + 0.00001);
setHeading(heading+5);
console.log("angle:" + heading);
};
return (
<MapContainer className="map" center={[lat, lon]} zoom={21}>
<TileLayer
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[lat, lon]} icon={defaultIcon} rotationAngle={heading} rotationOrigin="center">
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
);
};
export default MySimpleMap;
The complete problem is in this sandbox: https://codesandbox.io/s/vigilant-wood-kgv4p?file=/src/App.js
Using some of the provided answers for rotating markers in this post , was able to get it to work with your provided codesandbox. The default Marker
component does not have rotationAngle
or rotationOrigin
as valid props, you'd need to do something like done here to get that. So instead we can use re-use the rotation code and plug it into your code by lifting it into a re-usable function:
const applyRotation = (marker, _options) => {
const oldIE = L.DomUtil.TRANSFORM === "msTransform";
const options = Object.assign(_options, { rotationOrigin: "center" });
const { rotationAngle, rotationOrigin } = options;
if (rotationAngle && marker) {
marker._icon.style[L.DomUtil.TRANSFORM + "Origin"] = rotationOrigin;
if (oldIE) {
// for IE 9, use the 2D rotation
marker._icon.style[L.DomUtil.TRANSFORM] = `rotate(${rotationAngle} deg)`;
} else {
// for modern browsers, prefer the 3D accelerated version
marker._icon.style[
L.DomUtil.TRANSFORM
] += ` rotateZ(${rotationAngle}deg)`;
}
}
};
And then calling it once the rotation has been changed with a useEffect
because we also want the Markers
position to be updated otherwise the rotation won't work:
useEffect(() => {
applyRotation(markerRef.current, { rotationAngle: heading + 5 });
}, [heading]);
When you call myFunc
it updates the position (lat,lon) as well as the header (rotation) so by placing it in the useEffect, on next render Marker
should have updated its position.
I have a codebox example here.
The above solution does have some issue's you'll need to sift through the leaflet-rotatedmarker
library to get those missing edge cases. However if you can add that library a better solution would be importing that:
import L from "leaflet";
import "leaflet-rotatedmarker";
and using the extended setRotationAngle
instead.
useEffect(() => {
if (markerRef.current) {
markerRef.current.setRotationAngle(heading);
}
}, [heading]);
You can also use that to lift it into a RotatedMarker
component so that your original use case of the props rotationAngle={heading} rotationOrigin="center"
will work:
const RotatedMarker = forwardRef(({ children, ...props }, forwardRef) => {
const markerRef = useRef();
const { rotationAngle, rotationOrigin } = props;
useEffect(() => {
const marker = markerRef.current;
if (marker) {
marker.setRotationAngle(rotationAngle);
marker.setRotationOrigin(rotationOrigin);
}
}, [rotationAngle, rotationOrigin]);
return (
<Marker
ref={(ref) => {
markerRef.current = ref;
if (forwardRef) {
forwardRef.current = ref;
}
}}
{...props}
>
{children}
</Marker>
);
});
Here's the codepen where i tested the above out