如何在React Leaflet中使用SVG组件?

问题描述 投票:0回答:2

我将创建的 svg 转换为组件。我将颜色和字符串或整数表达式作为道具发送到其中,并且我想相应地创建一个动态标记。

我希望我的 svg 图标出现在左上角以替换下面地图上的蓝色标记。

TrackingMap.tsx

import React, { useState } from 'react';
//Third Party imports
import 'leaflet/dist/leaflet.css';
import { MapContainer, TileLayer } from 'react-leaflet';
import L from 'leaflet';

//Components imports
import Routing from './Routing';

//Utils import
import { MapWithPopupdatav2 } from './fakeData2';
import { CoordinatInterface, EachPointforRouting } from 'types/Map';



interface DefaultMapWithPopupProps {
  // dataSource: RootMapWithPopupData;
  height?: string;
  width?: string;
}

const TrackingMap = ({ height }: DefaultMapWithPopupProps) => {
  const [markersDataSource, setMarkersDataSource] = useState(
    MapWithPopupdatav2.data.map(item => item.gridData.data.map(item => item))
  );

  const [routeWayColor, setRouteWayColor] = useState<string[]>(
    MapWithPopupdatav2.data.map(item => item.color)
  );

  const [dataForRouting, setDataForRouting] = useState<EachPointforRouting[][]>(
    MapWithPopupdatav2.data.map(eachPoint =>
      eachPoint.gridData.data.map(point =>
        L.latLng(point.latitude, point.longitude)
      )
    )
  );

  const markersLatLon: CoordinatInterface[][] = MapWithPopupdatav2.data.map(
    eachPoint =>
      eachPoint.gridData.data.map(point => ({
        latitude: point.latitude,
        longitude: point.longitude,
      }))
  );
  function centerMapDataCalculate(data: CoordinatInterface[][]) {
    let newArray: CoordinatInterface[] = [];
    data.forEach(item => {
      item.map(point => {
        newArray.push(point);
      });
    });
    return newArray;
  }

  const markersCoordinatesForMapCentering: CoordinatInterface[] =
    centerMapDataCalculate(markersLatLon);
  return (
    <MapContainer scrollWheelZoom={true} style={{ height: `${height}` }}>
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      {dataForRouting.map((eachPoint, index) => {
        return (
          <React.Fragment key={index}>
            <Routing
              eachPoint={eachPoint}
              dataSource={markersDataSource[index]}
              color={routeWayColor[index]}
              bounds={markersCoordinatesForMapCentering}
            />
            <Marker />
          </React.Fragment>
        );
      })}
    </MapContainer>
  );
};

export default TrackingMap;

路由.tsx

import { useEffect } from 'react';
//Third Party İmports
import L, { latLngBounds } from 'leaflet';
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css';
import 'leaflet-routing-machine';
import { useMap } from 'react-leaflet';

//Components
import { Marker } from 'components/icons';

//Utils import
import { RoutingPropsforTrackingMap } from 'types/Map';


L.Marker.prototype.options.icon = L.icon({
  iconUrl: 'https://unpkg.com/[email protected]/dist/images/marker-icon.png',
});

const Routing = ({
  eachPoint,
  dataSource,
  color,
  bounds,
}: RoutingPropsforTrackingMap) => {
  const map = useMap();
  let markerBounds = latLngBounds([]);
  useEffect(() => {
    if (!map) return;

    const routingControl = L.Routing.control({
      waypoints: eachPoint,
      createMarker: function (
        waypointIndex: any,
        waypoint: any,
        numberOfWaypoints: any
      ) {
        //  return L.marker(waypoint.latLng, { icon: svgIcon }).bindPopup(
        return L.marker(waypoint.latLng).bindPopup(
          `<p><b>Location Code:</b>${dataSource[waypointIndex].locationCode}</p>
          <p><b>Location Type:</b>${dataSource[waypointIndex].locationType}</p>
          <p><b>Visit Order:</b>${dataSource[waypointIndex].visitOrder}</p>
          `
        );
      },
      lineOptions: {
        styles: [
          {
            color: color,
            opacity: 1,
            weight: 7,
          },
        ],
      },
      addWaypoints: false,
      draggableWaypoints: false,
      fitSelectedRoutes: false,
      showAlternatives: true,
    }).addTo(map);

    if (bounds.length && bounds.length > 0) {
      bounds.forEach(marker => {
        markerBounds.extend([marker.latitude, marker.longitude]);
      });
      map.fitBounds(markerBounds);
    }

    return () => map.removeControl(routingControl);
  }, [map]);

  return null;
};
export default Routing;

标记.tsx

import * as React from 'react';

const SvgComponent = (props: any) => (
  <div className="markerIcon">
    <p>{props.visitOrder}</p>
    <svg
      width={99}
      height={122}
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      <g
        style={{
          mixBlendMode: 'darken',
        }}
      >
        <g filter="url(#a)">
          <ellipse cx={49} cy={33} rx={36} ry={33} fill="#0AF291" />
        </g>
        <path
          d="M83.5 33c0 17.277-15.32 31.5-34.5 31.5S14.5 50.277 14.5 33 29.82 1.5 49 1.5 83.5 15.723 83.5 33Z"
          stroke="#000"
          strokeWidth={3}
        />
      </g>
      <g filter="url(#b)">
        <path d="m49.5 116 43.734-75H5.766L49.5 116Z" fill="#0AF291" />
      </g>
      <path
        d="m50.796 116.756-1.296 2.222-1.296-2.222-43.734-75L3.155 39.5h92.69l-1.315 2.256-43.734 75Z"
        stroke="#000"
        strokeWidth={3}
      />
      <defs>
        <filter
          id="a"
          x={13}
          y={0}
          width={72}
          height={70}
          filterUnits="userSpaceOnUse"
          colorInterpolationFilters="sRGB"
        >
          <feFlood floodOpacity={0} result="BackgroundImageFix" />
          <feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
          <feColorMatrix
            in="SourceAlpha"
            values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
            result="hardAlpha"
          />
          <feOffset dy={4} />
          <feGaussianBlur stdDeviation={2} />
          <feComposite in2="hardAlpha" operator="arithmetic" k2={-1} k3={1} />
          <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
          <feBlend in2="shape" result="effect1_innerShadow_0_1" />
        </filter>
        <filter
          id="b"
          x={0.544}
          y={38}
          width={97.913}
          height={87.956}
          filterUnits="userSpaceOnUse"
          colorInterpolationFilters="sRGB"
        >
          <feFlood floodOpacity={0} result="BackgroundImageFix" />
          <feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
          <feColorMatrix
            in="SourceAlpha"
            values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
            result="hardAlpha"
          />
          <feOffset dy={4} />
          <feGaussianBlur stdDeviation={2} />
          <feComposite in2="hardAlpha" operator="arithmetic" k2={-1} k3={1} />
          <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
          <feBlend in2="shape" result="effect1_innerShadow_0_1" />
        </filter>
      </defs>
    </svg>
  </div>
);

export default SvgComponent;

Routing.tsx 中的属性颜色和访问订单值如下

<Marker color={color} visitOrder={dataSource[waypointIndex].visitOrder}/>

我想用它作为。

javascript reactjs leaflet react-leaflet leaflet-routing-machine
2个回答
1
投票

据我所知,我认为不可能在Leaflet地图上渲染react svg组件,因为地图渲染是由Leaflet处理的,而不是由React处理的。我认为这同样适用于其他知名的库,例如 Mapbox。我在那里遇到了类似的问题,解决方案与下面的解决方案类似。

你可以做的是渲染一个

L.divicon
并将 svg 图标作为 html 传递。我用你的 svg 玩了一下,但没能 100% 获得你得到的实际图像。无论如何,它非常接近你所拥有的,我认为你可以轻松地适应它。所以这里是一个简单的例子,如何在react-leaflet中渲染一个svg图标作为Leaflet标记。

因此只需创建一个 svg 图标变量,它是

L.divIcon

的实例
const svgIcon = L.divIcon({
  html: `
  <svg
      width="99"
      height="122"
      fill="none"
    xmlns="http://www.w3.org/2000/svg"
    >
   ...your svg icon content
</svg>`,
  className: "svg-icon",
  iconSize: [24, 40],
  iconAnchor: [12, 40]
});

然后将其传递给Marker的图标道具

<Marker position={position} icon={svgIcon}>

演示


0
投票

我更喜欢以编程方式添加到地图,并结合使用 L.Marker 和 L.Icon。此外,我更喜欢避免使用内联 svg。

const iconSvg: string = require('../../assets/MyIcon.svg').default;
const icon = L.icon({
                                    className: "marker",
                                    iconUrl: iconSvg,
                                    iconSize: [55, 55],
                                    iconAnchor: [27.5, 27.5]
                                });
L.marker(firstPoint, { icon }).addTo(map)
© www.soinside.com 2019 - 2024. All rights reserved.