简体   繁体   English

如何在 javascript 反应中创建一个按钮来启用/禁用 leaflet 的缩放和平移?

[英]How to create a button to enable/disable zoom and pan for leaflet in javascript react?

As the title says, I'm searching how to create a button to enable/disable zoom and pan for leaflet in javascript react.正如标题所说,我正在搜索如何创建一个按钮来启用/禁用 javascript 反应中 leaflet 的缩放和平移。 Actually creating a button is fine, but getting the effect I want on my map is not.实际上创建一个按钮很好,但是在我的 map 上获得我想要的效果不是。

I am working on a react project and trying to implement a leaflet map that swiches between an "edit" mode and a "static" mode.我正在研究一个反应项目并尝试实现一个在“编辑”模式和“静态”模式之间切换的 leaflet map。 Edit would display the map with leaflet-draw commands and allow the user to zoom and pan.编辑将显示带有传单绘制命令的 map,并允许用户缩放和平移。 Static would hide the whole draw interface and disallow zoom and pan, practically turning the map to a fix image. Static 将隐藏整个绘图界面并禁止缩放和平移,实际上将 map 变为固定图像。

I already implemented a leaflet map and a button that adds/remove a "hidden" class to the draw object in my component.我已经实现了一个 leaflet map 和一个添加/删除“隐藏” class 到绘图 ZA8Z6FDE6331C4BEB666 组件的按钮。 Now I am trying to add something to the same button that would switch the zoom and pan between active and inactive.现在我正在尝试向同一个按钮添加一些东西,以在活动和非活动之间切换缩放和平移。

I manually changed the properties "scrollWheelZoom" and "dragging" in my code and obtained the desired effects, so I guess I now need to change them with the button.我在代码中手动更改了属性“scrollWheelZoom”和“拖动”并获得了所需的效果,所以我想我现在需要使用按钮更改它们。 I tried to use a querySelector to select my map and change the properties, but it didn't work (I guess the map is already rendered and is not going to change).我尝试对 select 我的 map 使用 querySelector 并更改属性,但它没有用(我猜 map 已经呈现并且不会更改)。 Then I tried to set the parameter values equal to the boolean state that I use as my switch for the button, but I obtained the same results.然后我尝试将参数值设置为等于我用作按钮开关的 boolean state,但我得到了相同的结果。 Most exemples I saw on the web use a completely different way of creating the map, if I understand well that's because they were not using react in addition to leaflet.我在 web 上看到的大多数示例都使用完全不同的方式来创建 map,如果我理解得很好,那是因为除了 leaflet 之外,它们还没有使用 react。

Is there a way to update these properties of my map or to create the map in another way that would allow me to update the properies more easily?有没有办法更新我的 map 的这些属性,或者以另一种方式创建 map,让我更轻松地更新属性?

Here is the current version of the code.这是代码的当前版本。 "scrollWheelZoom" is updated with the query method and "dragging" with the state method. “scrollWheelZoom”使用查询方法更新,“拖动”使用 state 方法。

//css imports
import './Map.css';
import 'leaflet/dist/leaflet.css';
import 'leaflet-draw/dist/leaflet.draw.css';

//library imports
import React, { useRef, useEffect, useState } from 'react';
import { MapContainer, TileLayer, FeatureGroup } from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw'
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import L from 'leaflet';

//solution to the missing icon phenomenon in draw
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
    iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
    iconUrl: require('leaflet/dist/images/marker-icon.png'),
    shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});

//component proper
export default function Map(props) {
    const [map, setMap] = useState(null) //state that will contain the map object
    let [editState, setEditState] = useState(true); // state to toggle the edit buttons

    //draw object that contains parameters for the drawing functions
    let drawObject = useRef({
        circlemarker: false
    });


    //handler for the edit button to hide/display the drawing tools
    function editHandler(e) {
        const $plc = e.target.closest('.placeholders');//select placeholder
        const $editObjectDraw = $plc.querySelector('.leaflet-draw');//select target object
        const $editObjectMap = $plc.querySelector('.mapContainer');
        if (editState) {
            $editObjectDraw.classList.add('visually-hidden');//update visual state
            $editObjectMap.scrollWheelZoom = editState;
        } else {
            $editObjectDraw.classList.remove('visually-hidden');//update visual state
            $editObjectMap.scrollWheelZoom = editState;
        }
        setEditState(!editState); //reverse boolean state to go from add to remove
    }

    return (
        <div>
            <MapContainer className="mapContainer" center={[45.5053, -73.6138]} zoom={1} scrollWheelZoom={true} dragging={editState}  minZoom={0} maxZoom={18} ref={setMap}>
                <TileLayer
                    attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                />
                <FeatureGroup>
                    <EditControl
                        className='drawControl'
                        position='topright'
                        onEdited={_onChange}
                        onCreated={_onChange}
                        onDeleted={_onChange}
                        onMounted={_onChange}
                        onEditStart={_onChange}
                        onEditStop={_onChange}
                        onDeleteStart={_onChange}
                        onDeleteStop={_onChange}
                        draw={drawObject.current}
                    />
                </FeatureGroup>

            </MapContainer>
            <button className="mapControl" onClick={editHandler}>
                <EditOutlinedIcon style={{ fill: "#FCF0E5" }} />
                <span>Edit</span>
            </button>
        </div>
    )
}

This is my first question here, I hope I didn't make any big mistake before posting it.这是我在这里的第一个问题,我希望在发布之前我没有犯任何大错误。 Thank you in advance for the help.预先感谢您的帮助。

As I wrote in the comments you should not use vanilla js in combination with react as both are responsible for changing the state and that will result to unexpected bugs and behavior.正如我在评论中所写的那样,您不应将 vanilla js 与 react 结合使用,因为两者都负责更改 state,这将导致意外的错误和行为。

So what you need to achieve the goal is to initially disable all map interactions and then enabling them as you probably thought.因此,您需要先禁用所有 map 交互,然后按照您的想法启用它们。 In the beginning you can easily achieve that because you pass the properties as props and you create a state variable to toggle between edit buttons display.一开始您可以轻松实现这一点,因为您将属性作为道具传递并创建 state 变量以在编辑按钮显示之间切换。 Only exception is the zoom control that needs to be disabled via a useEffect in the first place.唯一的例外是首先需要通过useEffect禁用的缩放控件。 But in order to dynamically enable the map interactions you need to do it via the map reference because the mapcontainer's props are immutable meaning you cannot change them via state.但是为了动态启用 map 交互,您需要通过 map 参考来完成它,因为mapcontainer's道具是不可变的,这意味着您不能通过 state 更改它们。 Once they created they cannot be changed.一旦创建,就无法更改。 You have two options to do that.你有两个选择来做到这一点。 Change them directly in the button onChange event or create a custom react-leaflet component that will be rendered when edit mode is enabled or true.直接在按钮onChange事件中更改它们,或者创建一个自定义的 react-leaflet 组件,该组件将在启用编辑模式或为 true 时呈现。

export default function Map() {
  const [map, setMap] = useState(null); //state that will contain the map object
  let [editMode, setEditMode] = useState(false); // state to toggle the edit buttons

  useEffect(() => {
    if (map) map.zoomControl.disable();
  }, [map]);

  const handleClick = () => {
    if (!editMode) {
      map.touchZoom.enable();
      map.doubleClickZoom.enable();
      map.scrollWheelZoom.enable();
      map.keyboard.enable();
      map.zoomControl.enable();
      map.dragging.enable();
    } else {
      map.touchZoom.disable();
      map.doubleClickZoom.disable();
      map.scrollWheelZoom.disable();
      map.keyboard.disable();
      map.zoomControl.disable();
      map.dragging.disable();
    }
    setEditMode((prevState) => !prevState);
  };



<MapContainer
        className="mapContainer"
        center={[45.5053, -73.6138]}
        zoom={1}
        style={{ height: "90vh" }}
        minZoom={0}
        maxZoom={18}
        zoomSnap={false}
        ref={setMap}
        scrollWheelZoom={false}
        dragging={false}>
        ...
        {editMode && (
      <FeatureGroup>
        <EditControl
          onEdited={() => {}}
          onCreated={() => {}}
          onDeleted={() => {}}
          draw={{ rectangle: false }}
        />
      </FeatureGroup>
    )}
 </MapContainer>
  <button className="mapControl" onClick={handleClick}>
    <EditOutlinedIcon />
    <span>{!editMode ? "Static" : "Edit"} mode</span>
  </button>
</>

You can simplify the onChange function even more.您可以进一步简化onChange function。 Check the demo检查演示

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

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