繁体   English   中英

React Leaflet Routing Machine:在删除所有航点后单击添加标记仅在第二次单击后触发

React Leaflet Routing Machine: onClick to add Marker after all waypoints are removed fires only after second click

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我遵循了Leaflet Routing Machine 关于交互即 onClicks 的建议。

通过我的实现,我将航点保存在本地存储中——将我从地图点击中获得的纬度和经度 obj 保存到一个名为markers的数组中

事件处理程序有一个条件,它将点击分成两个结果——添加(到标记数组)或更新它。

就像我在标题中所说的那样,初始交互很好,只是当我删除任何标记并尝试再次添加时才出现问题。 我还注意到markers数组是完全空的,下一个触发的事件是一个更新,显然它应该是一个补充:

这是路由机中的相关代码:

class Routing extends MapLayer {
  static contextType = UserContextDispatch;

  constructor(props) {
    super(props);
    this.state = {
      showSpinner: false,
      localDispatch: null,
    };

    this.handleLoader = this.handleLoader.bind(this);
    this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
    this.handleSetMarker = this.handleSetMarker.bind(this);
  }


  handleRemoveWayPoint() {

    var waypoints = this.control.getWaypoints();

    for (let i = waypoints.length - 1; i >= 0; i--) {
      console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
      if (waypoints[i].latLng !== null) {
        waypoints[i].latLng = null;
        break;
      }
    }
    this.control.setWaypoints(waypoints);
  }

  createLeafletElement(props) {
    const { map } = this.props.leaflet;

    if (map && !this.control) {

      this.control = L.Routing.control({
        collapsible: true,
        show: false,
        position: 'bottomleft',
        lineOptions: {
          styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
        },
        waypoints: [null],
        createMarker: function(i, wp, nWps) {
          if (i === 0) {
            return L.marker(wp.latLng, {
              icon: startIcon,
              draggable: true,
              keyboard: true,
              alt: 'current location'
            }).on('drag', function(e) {
              e.latlng.alt = 'current location';

              console.log('there be dragons start!!', e);
              RoutingMachineRef.handleSetMarker({
                ...e.oldLatLng,
                ...e.latlng
              });
            });
          }
          if (i === nWps - 1) {
            return L.marker(wp.latLng, {
              icon: endIcon,
              draggable: true,
              alt: 'current destination'
            }).on('drag', function(e) {
              e.latlng.alt = 'current destination';

              console.log('there be dragons dest!!', e);
              RoutingMachineRef.handleSetMarker({
                ...e.oldLatLng,
                ...e.latlng
              });
            });
          }
        }
      });

      L.Routing.errorControl(this.control).addTo(map);
    }

    return this.control.getPlan();
  }

  componentDidMount() {
    const { map } = this.props.leaflet;

    console.log('markers ', markers);
    this.setState(prevState => {
      localDispatch: prevState.localDispatch = this.context.dispatch;
    });

    map.addControl(this.control);
  }

  updateLeafletElement(fromProps, toProps) {
    const { map } = this.props.leaflet;
    var self = this;
    self;
    var { markers } = this.props;

    function createButton(label, container) {
      var btn = L.DomUtil.create('button', '', container);
      btn.setAttribute('type', 'button');
      btn.innerHTML = label;
      return btn;
    }

    var { localDispatch } = this.state;
    var container = L.DomUtil.create('div'),
      startBtn = createButton('Start from this location', container),
      destBtn = createButton('Go to this location', container);
    map.on(
      'click',

      function(e) {
        L.popup()
          .setContent(container)
          .setLatLng(e.latlng)
          .openOn(map);

        L.DomEvent.on(startBtn, 'click', function() {
  
          if (e.latlng) {
            e.latlng.alt = 'current location';
            console.log('adding);
            localDispatch({
              type: 'addMarker',
              payload: {
                marker: e.latlng
              }
            });
          }

          if (markers.length === 0) {
            console.log('updating ');
            e.latlng.alt = 'current location';

            localDispatch({
              type: 'updateMarkers',
              payload: {
                marker: e.latlng
              }
            });
          }

          self.control.spliceWaypoints(0, 1, e.latlng);
          map.closePopup();
        });

        L.DomEvent.on(
          destBtn,
          'click',
          function() {
            console.log('e', e);
            if (markers[1] === undefined) {
              e.latlng.alt = 'current destination';
              console.log('e.latlng ', e.latlng);
              localDispatch({
                type: 'addMarker',
                payload: {
                  marker: e.latlng
                }
              });
            }
            if (toProps.markers[1] !== undefined) {
              console.log('updating ');
              e.latlng.alt = 'current destination';

              localDispatch({
                type: 'updateMarkers',
                payload: {
                  marker: e.latlng
                }
              });
            }

            this.control.spliceWaypoints(1, 1, e.latlng);

            map.closePopup();
          }.bind(this)
        );
      }.bind(this)
    );


    if (toProps.removeRoutingMachine !== false) {
      this.control.setWaypoints([]);
    }
  }

  componentWillUnmount() {
    this.destroyRouting();
  }

  destroyRouting() {
    const { map } = this.props.leaflet;
    if (map) {
      map.removeControl(this.control);
    }
  }
}

export default withLeaflet(Routing);

提前致谢!

1 个回复

如您所见,我在RoutingMachine本身中有一些与Map (航点的 onClick)相关的RoutingMachine 在考虑之后,我将其作为handlerFunction移至Map组件。 现在它起作用了!

import React, { useState, useEffect, useRef } from 'react';
import { Button, Dimmer, Loader } from 'semantic-ui-react';

import L from 'leaflet';
import * as ELG from 'esri-leaflet-geocoder';

import Control from 'react-leaflet-control';
// import MapboxLayer from '../MapboxLayer/MapboxLayer.jsx';
import Routing from '../RoutingMachine/RoutingMachine.jsx';

import { parse, stringify } from 'flatted';

import { userState, userDispatch } from '../Context/UserContext.jsx';
import UIContext from '../Context/UIContext.jsx';

function currentMapViewPropsAreEqual(prevProps, nextProps) {
  console.log('prevProps, nextProps ', prevProps, nextProps);
  console.log(
    'prevProps.currentMapView === nextProps.currentMapView && prevProps.Map === nextProps.Map && prevProps.TileLayer === nextProps.TileLayer ',
    prevProps.currentMapView === nextProps.currentMapView &&
      prevProps.Map === nextProps.Map &&
      prevProps.TileLayer === nextProps.TileLayer
  );
  return (
    prevProps.currentMapView === nextProps.currentMapView &&
    prevProps.Map === nextProps.Map &&
    prevProps.TileLayer === nextProps.TileLayer
  );
}

function MyMap({ currentMapView, Map, TileLayer }) {
  console.log('currentMapView; ', currentMapView);
  var [animate, setAnimate] = useState(false);
  var [userLocation, setUserLocation] = useState(null);

  const [myState, setMyState] = useState(null);

  var handleWaypointsOnMapRef = useRef(handleWaypointsOnMap);

  var mapRef = useRef();
  var mapRefForRoutingMachine = useRef();
  var { state } = userState();
  var { dispatch } = userDispatch();
  var {
    currentMap,
    isRoutingVisible,
    removeRoutingMachine,
    isLengthOfMarkersLessThanTwo,
    markers
  } = state;

  useEffect(() => {
    handleWaypointsOnMapRef.current = handleWaypointsOnMap;
  }); // update after each render

  useEffect(() => {
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;

    console.log('foo');

    console.log('currentMap ', currentMapView);
    map.locate({ setView: true });
    map.on('locationfound', handleOnLocationFound);
  }, []);

  useEffect(() => {
    var searchControl = new ELG.Geosearch({
      useMapBounds: false
    });
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;

    console.log('mapRef ', mapRef);

    searchControl.addTo(map);

    var cb = e => handleWaypointsOnMapRef.current(e); // then use most recent cb value

    searchControl.on('results', cb);

    if (Object.keys(currentMap).length === 0) {
      dispatch({
        type: 'setMap',
        payload: {
          currentMap: stringify(map)
        }
      });
    }

    return () => {
      searchControl.off('results', cb);
    };
  }, []);

  function handleOnClickClearOneMarkerAtTime() {
    dispatch({
      type: 'setIsRoutingVisible',
      payload: {
        isRoutingVisible: false
      }
    });
    mapRefForRoutingMachine.current.handleRemoveWayPoint();
    dispatch({
      type: 'deleteUserMarkers'
    });
  }

  function handleOnClickClearAllMarkers() {
    mapRefForRoutingMachine.current.handleClearWayPoints();
    dispatch({
      type: 'resetUserMarkers'
    });
  }

  function handleOnClickMarkerClick(e) {
    e.originalEvent.view.L.DomEvent.stopPropagation(e);
  }

  function handleWaypointsOnMap(e) {
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;
    dispatch({
      type: 'setIsRoutingVisible',
      payload: {
        isRoutingVisible: true
      }
    });
    dispatch({
      type: 'setRemoveRoutingMachine',
      payload: {
        removeRoutingMachine: false
      }
    });

    function createButton(label, container) {
      var btn = L.DomUtil.create('button', '', container);
      btn.setAttribute('type', 'button');
      btn.innerHTML = label;
      return btn;
    }
    var container = L.DomUtil.create('div'),
      startBtn = createButton('Start from this location', container),
      destBtn = createButton('Go to this location', container);

    L.popup()
      .setContent(container)
      .setLatLng(e.latlng)
      .openOn(map);

    L.DomEvent.on(startBtn, 'click', function() {
      if (markers.length === 0) {
        e.latlng.alt = 'current location';

        console.log('adding current location', e.latlng);

        dispatch({
          type: 'addMarker',
          payload: {
            marker: e.latlng
          }
        });
      }

      if (markers[0] != undefined) {
        e.latlng.alt = 'current location';
        console.log('updating current location', e.latlng);

        dispatch({
          type: 'updateMarkers',
          payload: {
            marker: e.latlng
          }
        });
      }

      mapRefForRoutingMachine.current.handleSpliceWaypoints(0, 1, e.latlng);

      map.closePopup();
    });

    L.DomEvent.on(
      destBtn,
      'click',
      function() {
        console.log('e', e);
        if (markers.length === 1) {
          e.latlng.alt = 'current destination';
          console.log('adding destination ', e.latlng);
          dispatch({
            type: 'addMarker',
            payload: {
              marker: e.latlng
            }
          });
        }
        if (markers.length === 2 && markers[1] !== undefined) {
          e.latlng.alt = 'current destination';
          console.log('updating destination', e.latlng);

          dispatch({
            type: 'updateMarkers',
            payload: {
              marker: e.latlng
            }
          });
        }

        mapRefForRoutingMachine.current.handleSpliceWaypoints(1, 1, e.latlng);

        map.closePopup();
      }.bind(this)
    );
  }

  function handleOnViewportChanged(e) {
    console.log('viewport change', e);

    console.log('currentMapView ', currentMapView);
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;

    map.on('zoomend', function() {
      var zoom = map.getZoom();
      console.log('zoom ', zoom);

      console.log("'dispatch setMapZoom'; ");
      dispatch({
        type: 'setMapZoom',
        payload: {
          currentMapView: zoom
        }
      });
    });
  }

  function handleOnLocationFound(e) {
    console.log('e ', e);
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;
    map.setZoom(currentMapView);

    var latlng = e.latlng;
    var radius = e.accuracy;
    var circle = L.circle(latlng, radius);
    circle.addTo(map);
  }

  return (
    <Map
      preferCanvas={true}
      id="myMap"
      animate={animate}
      zoom={currentMapView}
      ref={mapRef}
      onViewportChanged={handleOnViewportChanged}
      onClick={e => handleWaypointsOnMap(e)}
    >
      <TileLayer
        url={`https://api.mapbox.com/styles/v1/${process.env.MAPBOX_USERNAME}/${
          process.env.MAPBOX_STYLE_ID
        }/tiles/256/{z}/{x}/{y}@2x?access_token=${process.env.MAPBOX_ACCESS_TOKEN}`}
        attribution='Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery &copy; <a href="https://www.mapbox.com/">Mapbox</a>'
      />

      <Control position="bottomleft">
        <Button onClick={handleOnClickClearOneMarkerAtTime} color="orange" size="small">
          delete one marker!
        </Button>
      </Control>
      <Control position="bottomright">
        <Button onClick={handleOnClickClearAllMarkers} color="red" size="small">
          clear all!
        </Button>
      </Control>

      {mapRef && (
        <Routing
          isRoutingVisible={isRoutingVisible}
          ref={mapRefForRoutingMachine}
          markers={markers}
          stringify={stringify}
          isLengthOfMarkersLessThanTwo={isLengthOfMarkersLessThanTwo}
          removeRoutingMachine={removeRoutingMachine}
          userLocation={userLocation}
        />
      )}
    </Map>
  );
}

var MemoizedMyMap = React.memo(MyMap, currentMapViewPropsAreEqual);

export default MemoizedMyMap;

这是路由机:

import { MapLayer } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-routing-machine';
import { withLeaflet } from 'react-leaflet';
import UserContextDispatch from '../Context/UserContext.jsx';
import { Dimmer, Loader } from 'semantic-ui-react';
import { isEqual } from 'lodash';

import AwesomeDebouncePromise from 'awesome-debounce-promise';

class Routing extends MapLayer {
  static contextType = UserContextDispatch;

  constructor(props) {
    super(props);
    this.state = {
      showSpinner: false,
      localDispatch: null,
      markerIsBeingDragged: false,
      currentMarker: {}
    };

    this.handleLoader = this.handleLoader.bind(this);
    this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
    this.handleClearWayPoints = this.handleClearWayPoints.bind(this);
    this.handleSpliceWaypoints = this.handleSpliceWaypoints.bind(this);
    this.handleSetMarker = this.handleSetMarker.bind(this);
  }

  handleSetMarker(marker) {
    var { markers } = this.props;

    if (markers[0] !== undefined && markers[0].alt === 'current location') {
      this.setState(prevState => ({
        currentMarker: { ...prevState.currentMarker, ...marker }
      }));
    }
    if (markers[1] !== undefined && markers[1].alt === 'current destination') {
      this.setState(prevState => ({
        currentMarker: { ...prevState.currentMarker, ...marker }
      }));
    }

    console.log('this.state ', this.state);
  }

  handleSpliceWaypoints(start, end, obj) {
    this.control.spliceWaypoints(start, end, obj);
  }

  handleLoader() {
    var { showSpinner } = this.state;

    if (this.state.showSpinner === false) {
      this.setState(function(prevState) {
        return { showSpinner: !prevState.showSpinner };
      });
      return (
        <Dimmer active inverted>
          <Loader />
        </Dimmer>
      );
    }
    this.setState(function(prevState) {
      return { showSpinner: (prevState.showSpinner = true) };
    });
  }

  handleRemoveWayPoint() {
    var waypoints = this.control.getWaypoints();

    for (let i = waypoints.length - 1; i >= 0; i--) {
      console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
      if (waypoints[i].latLng !== null) {
        waypoints[i].latLng = null;
        break;
      }
    }
    console.log('waypoints ', waypoints);
    this.control.setWaypoints(waypoints);
  }

  handleClearWayPoints() {
    this.control.setWaypoints([L.latLng(null, null), L.latLng(null, null)]);
  }

  componentDidMount() {
    const { map } = this.props.leaflet;

    var { markers } = this.props;

    this.control.setWaypoints([L.latLng(markers[0]), L.latLng(markers[1])]);
    this.setState(prevState => {
      localDispatch: prevState.localDispatch = this.context.dispatch;
    });

    map.addControl(this.control);
  }

  createLeafletElement(props) {
    const { map } = this.props.leaflet;

    var startIcon = new L.Icon({
      iconUrl:
        'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
      shadowUrl:
        'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
      iconSize: [25, 41],
      iconAnchor: [12, 41],
      popupAnchor: [1, -34],
      shadowSize: [41, 41]
    });

    var endIcon = new L.Icon({
      iconUrl:
        'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
      shadowUrl:
        'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
      iconSize: [25, 41],
      iconAnchor: [12, 41],
      popupAnchor: [1, -34],
      shadowSize: [41, 41]
    });

    if (map && !this.control) {
      var RoutingMachineRef = this;

      this.control = L.Routing.control({
        collapsible: true,
        show: false,
        position: 'bottomleft',
        lineOptions: {
          styles: [{ color: 'chartreuse', opacity: 1, weight: 5 }]
        },
        waypoints: [null],
        createMarker: function(i, wp, nWps) {
          if (i === 0) {
            return L.marker(wp.latLng, {
              icon: startIcon,
              draggable: true,
              keyboard: true,
              alt: 'current location'
            }).on('move', function(e) {
              e.target._latlng.alt = 'current location';
              console.log('e.target._latlng', e.target._latlng);

              console.log('there be dragons start!!', e);
              RoutingMachineRef.setState(prevState => ({
                markerIsBeingDragged: !prevState.markerIsBeingDragged
              }));
              RoutingMachineRef.handleSetMarker(e.target._latlng);
            });
          }
          if (i === nWps - 1) {
            return L.marker(wp.latLng, {
              icon: endIcon,
              draggable: true,
              alt: 'current destination'
            }).on('move', function(e) {
              e.target._latlng.alt = 'current destination';

              console.log(' e.target._latlng', e.target._latlng);
              RoutingMachineRef.setState(prevState => ({
                markerIsBeingDragged: !prevState.markerIsBeingDragged
              }));
              console.log('there be dragons dest!!', e);
              RoutingMachineRef.handleSetMarker(e.target._latlng);
            });
          }
        }
      });

      L.Routing.errorControl(this.control).addTo(map);
    }

    return this.control.getPlan();
  }

  updateLeafletElement(fromProps, toProps) {
    var { currentMarker, localDispatch, markerIsBeingDragged } = this.state;

    // console.log('fromProps, toProps ', fromProps, toProps);
    if (markerIsBeingDragged && currentMarker.hasOwnProperty('alt')) {
      if (isEqual(toProps.markers[0], currentMarker) === false) {
        localDispatch({
          type: 'updateMarkers',
          payload: {
            marker: currentMarker
          }
        });
      }
      if (isEqual(toProps.markers[1], currentMarker) === false) {
        localDispatch({
          type: 'updateMarkers',
          payload: {
            marker: currentMarker
          }
        });
      }

      this.setState(prevState => ({
        markerIsBeingDragged: !prevState.markerIsBeingDragged
      }));
    }

    if (toProps.removeRoutingMachine !== false) {
      this.control.setWaypoints([]);
    }
  }

  componentWillUnmount() {
    console.log("'unmount' ", 'unmount');
    this.destroyRouting();
  }

  destroyRouting() {
    const { map } = this.props.leaflet;
    if (map) {
      map.removeControl(this.control);
    }
  }
}

export default withLeaflet(Routing);
1 on(“ click”)仅在第二次单击后触发

我正在使用锚点链接加载更多帖子,问题是它不是在第一次点击中触发(仅在第一次时),而是在第二次加载更多帖子时,它可以与第一次点击一起使用。 jQuery(window).load(function() { jQuery('#load-more').on('click', funct ...

2 仅在第二次单击后才触发验证

我还是新手。 我正在尝试创建一个注册表单。 目前,我在验证Firestore中是否已经存在用户名时陷入困境。 我设法创建了功能,并且效果很好。 但是,当我尝试在验证器中实现该功能时,当我第一次单击它时,它不会显示错误。 仅当我第二次单击时,才会显示错误。 有人知道为什么吗? 以及我 ...

3 jQuery Sortable仅在第二次单击后触发

我已经将jquery的“可排序”功能集成到我的网站中,并且除了一个较小的异常外,它的运行情况非常好。 只有在页面上的任意位置单击一次后,它才起作用。 页面的可排序元素所在的部分是在ajax查询之后加载的,因此我使用的是“ on”功能。 我很想弄清楚如何使它生效而无需先单击。 ...

5 更改React组件的顺序仅在第二次单击后触发重新渲染

所以我将数组映射到列表组以显示项目列表。 我希望能够按项目的任何属性对该列表进行排序。 我能够很容易地对数组求助,并且我有一个Reactstrap下拉列表来触发排序,并且数组可以正确排序,甚至可以正确地在组件中更新。 但是,该组件不会重新渲染。 但是,如果我再次单击该按钮以打开下拉列表, ...

6 动态添加的控件导致静态按钮事件仅在第二次单击后才会触发

我有一个自定义用户控件,其中包含控件的动态列表以及在aspx页上声明的静态按钮(以及OnClick声明)。 该按钮显示或隐藏包含动态控件列表的面板。 我发现一个我认为与动态添加的控件有关的问题,其中按钮事件方法(即使它不是动态添加的)仅在第二次单击时触发。 我认为这与以下事实有关:在创 ...

7 动态删除和重新添加链接元素直到第二次单击后才会触发

我有一个表格,该表格会刷新表单中的输入元素。 键入关键字后,我将通过首先删除所有现有行,然后动态添加新表行来刷新表数据。 每个表格行都包含指向另一个页面的链接。 在初始页面加载时,该链接在首次点击后即可正常工作; 但在随后的表格刷新时,直到第二次点击该链接才会触发。 许多其他SO帖 ...

8 第二次单击后触发敲除单击绑定

如果单击带有此功能的页面,则我具有一个可剔除到页面上的部分的剔除绑定 我在其中调用函数的html 问题是它在第二次单击而不是第一次单击后触发。 当您单击页面上的某个按钮时,它也不会触发,再次单击它会触发,然后在其他按钮上正常运行时,也会发生这种情况。 ...

9 选择使用bootstrap select插件触发事件“更改”,仅在第二次单击后触发

所以我一直在制作Marketo表单,然后添加个人样式和调整来改进UI。 其中一个调整是使用bootstrap select替换现有的select下拉列表(替换它们的小JS插件)。 两个选择是链接的,但是当原始选择发生更改时,它需要更改事件来触发一些条件逻辑,以确定是否应显示另一个选择。 ...

10 为什么我的代码仅在第二次单击后才能运行

如果单击下面的代码,则折叠并取消折叠项目列表。 一切正常,但是第一次单击似乎无效,我看不出原因。 仅在第一次单击“空闲”后重新打开或刷新页面时,才可以正常工作。 我在互联网上找不到类似的问题。 有任何想法吗? function tt(e) { e.onclick = ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM