React-Google-Maps 显示两个标记,一个在原始位置,另一个标记跟随地图的新中心 onDrag

Posted

技术标签:

【中文标题】React-Google-Maps 显示两个标记,一个在原始位置,另一个标记跟随地图的新中心 onDrag【英文标题】:React-Google-Maps displays two markers, one at the original position and another marker that follows the new center of the map onDrag 【发布时间】:2021-01-02 15:11:38 【问题描述】:

我使用react-google-maps 在使用 ReactJS 获取她的坐标后显示用户的位置。但是,当我从原始位置移动标记时,初始标记停留在那里,因此创建了两个标记。我不知道如何解决这个问题。

我希望无论发生什么,地图标记保持在中心,当用户拖动或放大/缩小标记时,标记保持在中心,以便用户的位置始终位于地图的中心。这样,用户将能够更新她的位置。根据您的示例,当我拖动时,标记保持固定在它的位置这个组件的设计是为了让用户可以设置她的位置,稍微调整一下标记的位置,以防标记有点偏离 任何帮助将不胜感激:

App.js

import React from "react";
import WrappedMap from "./Map";
import "./styles.css";

export default class App extends React.Component 
  constructor(props) 
    super(props);

    this.state = 
      location: "",
      place: ""
    ;
  

  handleLocation = (location) => 
    this.setState( location: location );
  ;

  handlePlace = (place) => 
    this.setState( place: place );
  ;

  render() 
    return (
      <div className="App">
        <WrappedMap
          googleMapURL=`https://maps.googleapis.com/maps/api/js?key=`
          loadingElement=<div style= height: `100%`  />
          containerElement=
            <div
              style=
                height: `50%`,
                width: "95%",
                position: "absolute",
                marginTop: "25%"
              
            />
          
          mapElement=<div style= height: `100%`  />
          location=this.state.location
          handleLocation=this.handleLocation
          changeState=this.changeState
          place=this.state.place
          handlePlace=this.handlePlace
          handleUseGPS=this.handleUseGPS
        />
      </div>
    );
  

Map.js

import React,  useRef, useState, useEffect  from "react";
import Geocode from "react-geocode";
import Button from "@material-ui/core/Button";
import 
  GoogleMap,
  withScriptjs,
  withGoogleMap,
  Marker
 from "react-google-maps";
// import "./Sign.css";
function Map(
  location,
  handleLocation,
  changeState,
  place,
  handlePlace,
  handleUseGPS
) 
  const [center, setCenter] = useState(location);
  const [showMap, setShowMap] = useState(false);
  const refMap = useRef(null);
  var options = 
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 30000
  ;
  function success(pos) 
    var crd = pos.coords;
    console.log(crd);
    console.log("Your current position is:");
    console.log(`Latitude : $crd.latitude`);
    console.log(`Longitude: $crd.longitude`);
    console.log(`More or less $crd.accuracy meters.`);
    const loc = 
      lat: crd.latitude,
      lng: crd.longitude
    ;
    handleLocation(loc);
    getAndChangeAddress(loc);
    setCenter(loc);
    setShowMap(true);
  
  function error(err) 
    if (!navigator.geolocation) 
      console.log("Geolocation is not supported by your browser");
     else 
      console.log("loading");
    
    let typeErr = err.code;
    console.log(`Code: $typeErr`);
    switch (typeErr) 
      case 1:
        console.log("User has not given permissions");
        break;
      case 2:
        console.log(
          "The acquisition of the geolocation failed because at least one internal source of position returned an internal error."
        );
        break;
      case 3:
        console.log("Timeout reached before obtaining information");
        break;
      default:
        break;
    
    console.warn(`ERROR($err.code): $err.message`);
    handlePlace("");
    handleLocation();
    // handleUseGPS(true);
    // changeState(7);
  
  const handleBoundsChanged = () => 
    const mapCenter = refMap.current.getCenter(); //get map center
    setCenter(mapCenter);
  ;
  useEffect(() => 
    navigator.geolocation.getCurrentPosition(success, error, options);
  , []);
  const handleDragEnd = () => 
    const newCenter = refMap.current.getCenter();
    const newLocation = 
      lat: newCenter.lat(),
      lng: newCenter.lng()
    ;
    handleLocation(newLocation);
    getAndChangeAddress(newLocation);
  ;
  const returnToMenu = () => 
    // changeState(4);
  ;
  const getAndChangeAddress = (loc) => 
    const lat = loc.lat.toString();
    const lng = loc.lng.toString();
    console.log(typeof lat);
    console.log(`From getAddress() function => lat: $lat,  lng: $lng`);
    Geocode.fromLatLng(lat, lng).then(
      (response) => 
        const address = response.results[0].formatted_address;
        console.log(`Formatted address: $address`);
        handlePlace(address);
      ,
      (error) => 
        console.error(error);
        console.log("Error occuredd in getting address");
      
    );
  ;
  return (
    <>
      <div className="sign-in-form">
        showMap && (
          <GoogleMap
            ref=refMap
            defaultZoom=15
            defaultCenter=center
            onBoundsChanged=handleBoundsChanged
            onDragEnd=handleDragEnd
          >
            <Marker
              // defaultPlace=center
              position=center
              // ref=refMap
              // defaultPosition=center
              // onDrag=handleBoundsChanged
              // onDragEnd=handleDragEnd
            />
          </GoogleMap>
        )
        location.lat !== "" && (
          <>
            <hr />
            <div style= margin: "1em" >place</div>
            <hr />
          </>
        )
        <Button
          className="otp-button"
          onClick=returnToMenu
          fullWidth
          variant="contained"
        >
          SAVE LOCATION
        </Button>
      </div>
    </>
  );

export default withScriptjs(withGoogleMap(Map));

另见:CodeSandbox Link

【问题讨论】:

确保纬度经度有效,您可以使用 (0,0) 或默认纬度经度进行初始化 @AvaniBataviya 经纬度有效。它会获取设备的 GPS。 【参考方案1】:

我相信这里发生的事情是您已将标记位置设置为center,因此无论何时拖动,都会生成第二个标记。

相反,react-google-maps docs 显示了一个选项,您可以在其中将纬度和经度硬编码到标记。优点:没有重复标记。缺点:如果用户输入了需要移动标记的不同地址,则需要编写更新函数。

更改这些行,您的问题应该得到解决:

初始化钩子

function Map(
  location,
  handleLocation,
  changeState,
  place,
  handlePlace,
  handleUseGPS
) 
  const [center, setCenter] = useState(location);
  const [showMap, setShowMap] = useState(false);
  const [mylat, setLat] = useState(0);              /* <------ add this hook */
  const [mylong, setLong] = useState(0);            /* <------ and this hook */
  const refMap = useRef(null);

...

函数成功()


  function success(pos) 
    var crd = pos.coords;
    console.log(crd);
    console.log("Your current position is:");
    console.log(`Latitude : $crd.latitude`);
    console.log(`Longitude: $crd.longitude`);
    console.log(`More or less $crd.accuracy meters.`);

    setLat(crd.latitude);                   /* <------ set state here*/
    setLong(crd.longitude);                 /* <------ set state here*/

    const loc = 
      lat: crd.latitude,
      lng: crd.longitude
    ;
    handleLocation(loc);
    getAndChangeAddress(loc);
    setCenter(loc);
    setShowMap(true);
  

返回谷歌地图

          <GoogleMap
            ref=refMap
            defaultZoom=15
            defaultCenter=center
            onBoundsChanged=handleBoundsChanged
            onDragEnd=handleDragEnd
          >
            <Marker
              // defaultPlace=center
              position= lat: mylat, lng: mylong        /* <----- lat and long here */
              // ref=refMap
              // defaultPosition=center
              // onDrag=handleBoundsChanged
              // onDragEnd=handleDragEnd
            />
          </GoogleMap>

OP 澄清说&lt;Marker /&gt; 实际上应该随着屏幕中心移动,问题是存在重复。

经过多次调试,我发现错误是由于元素的呈现方式造成的。变化:

index.js

import React,  Component  from 'react';
import  render  from 'react-dom';

import App from "./App";

render(<App />, document.getElementById('root'));

您的问题已解决。这也有效:

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
    <App />, /* <-------- remove <StrictMode /> */
  rootElement
);

您的控制台中有一条关于严格模式的警告,将其关闭似乎可以解决您的问题,这也是您的 GoogleMap 组件无法按预期工作的根本原因:

警告:在严格模式树中检测到旧版上下文 API。 所有 16.x 版本都将支持旧 API,但使用它的应用程序 > 应迁移到新版本。 请更新以下组件:GoogleMap、Marker 在此处了解有关此警告的更多信息:... 在 StrictMode 中(在 src/index.js:8)


我还发现了另一个 *** 问题,您的代码是从中建模的,因此我也将在此处链接以供将来的查看者使用:

https://***.com/a/56138599/14198287 工作 MRE:https://stackblitz.com/edit/react-fhgr5w?file=index.js

【讨论】:

他已将标记位置设置为“中心”,这样每当用户滚动时,标记都会保持在中心。标记很好地保持在中心,但为什么要使用另一个标记?哪个不在中心?同样在应用您的解决方案后,问题仍然存在。 @buckyroberts 你能确认你只想放大/缩小用户的位置吗?该功能显示了拖动功能,这表明您希望让用户在地图上移动,而不是放大/缩小他们当前的位置。 Shivam Jha 看到了这个沙盒,这有我的解决方案并且确实解决了 OP 的问题:codesandbox.io/s/react-google-maps-demo-forked-stwgm @HarleyLang 我希望无论发生什么,地图标记保持在中心,当用户拖动或放大/缩小标记时,标记保持在中心,以便用户的位置始终在地图的中心。这样,用户将能够更新她的位置。根据您的示例,当我拖动时,标记保持固定在其位置。该组件的设计目的是让用户可以设置她的位置,并稍微调整标记的位置,以防标记有点偏离。顺便说一句,非常感谢您的努力。我很欣赏那个人! @buckyroberts 你能用这些标准更新这个问题吗?我明天某个时候会看一下组件文档,看看我是否可以创建该功能,但如果你打败了我,请继续发布你的答案! @HarleyLang 非常感谢你!我不能感谢你足够的兄弟。你刚刚解决了一个我工作了一个月的问题。上帝保佑你

以上是关于React-Google-Maps 显示两个标记,一个在原始位置,另一个标记跟随地图的新中心 onDrag的主要内容,如果未能解决你的问题,请参考以下文章

如何在 react-google-maps 中添加标记?

如何使用 react-google-maps 通过单击在地图上添加标记?

当用户点击 react-google-maps 中的地图或标记时执行操作

react-google-maps 水平对齐 MarkerWithLabel

使用react-google-maps刷新不需要的页面。

在地图上显示多个标记时,单击标记时如何只打开一个信息窗口?