带有 react-leaflet 的自定义标记图标

custom marker icon with react-leaflet

我尝试了我在网上找到的所有东西,Whosebug 和 Github,但我还是做不到。

我想制作一个带有自定义图标的自定义标记,但使用下面的代码我总是遇到错误:'TypeError: options.icon.createIcon is not a function'

这是我的代码(文件夹路径没有错误,所有内容都在 src/js 或 src/img 中)

Icon.js

import L from 'leaflet';

const iconPerson = L.Icon.extend({
  options: {
    iconUrl: require('../img/marker-pin-person.svg'),
    iconRetinaUrl: require('../img/marker-pin-person.svg'),
    iconAnchor: null,
    popupAnchor: null,
    shadowUrl: null,
    shadowSize: null,
    shadowAnchor: null,
    iconSize: new L.Point(60, 75),
    className: 'leaflet-div-icon'
  }
});

export { iconPerson };

MarkerPinPerson

import React from 'react';
import { Marker } from 'react-leaflet';
import {  iconPerson  } from './Icons';


export default class MarkerPinPerson extends React.Component {

  render() {

    return (
      <Marker
        position={this.props.markerPosition}
        icon={ iconPerson }
        >
      </Marker>
      );
  }
}

真心寻求您的帮助!

我终于找到了 Icon.js 文件的正确代码:

import L from 'leaflet';

const iconPerson = new L.Icon({
    iconUrl: require('../img/marker-pin-person.svg'),
    iconRetinaUrl: require('../img/marker-pin-person.svg'),
    iconAnchor: null,
    popupAnchor: null,
    shadowUrl: null,
    shadowSize: null,
    shadowAnchor: null,
    iconSize: new L.Point(60, 75),
    className: 'leaflet-div-icon'
});

export { iconPerson };

我被带到这里是为了弄清楚如何在服务器端呈现自定义图标(使用 react-leaflet-universal)。我想我会 post 这个,以防将来有人出于同样的原因发现自己在这里。就像 react-leaflet-markercluster 的情况一样,我能够通过在 return 函数中要求 leaflet 来实现它,例如:

<Map center={this.props.center}
             zoom={zoom}
             className={leafletMapContainerClassName}
             scrollWheelZoom={false}
             maxZoom={18}
             preferCanvas={false}
        >
            {() => {
                const MarkerClusterGroup = require('react-leaflet-markercluster').default;
                const L = require('leaflet');

                const myIcon = L.icon({
                    iconUrl: require('../assets/marker.svg'),
                    iconSize: [64,64],
                    iconAnchor: [32, 64],
                    popupAnchor: null,
                    shadowUrl: null,
                    shadowSize: null,
                    shadowAnchor: null
                });

                return (
                    <React.Fragment>
                        <TileLayer
                            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                            attribution=''
                            setParams={true}
                        />
                        <MarkerClusterGroup>
                            {coordArray.map(item => {
                                return (
                                    <Marker icon={myIcon} key={item.toString()} position={[item.lat, item.lng]}>
                                        {item.title && <Popup>
                                            <span>{item.title}</span>
                                        </Popup>}
                                    </Marker>
                                )
                            })}
                        </MarkerClusterGroup>
                    </React.Fragment>
                );
            }}
        </Map>

您不需要使用 require。而不是给 iconUrl = "../assets/name" 你只需要导入你的 png 或 svg 然后你可以给你的 iconUrl 源。看下面的例子:

// 首先导入你的图片或 svg

import heart from "../../images/other/love.svg";

// 为您的图标提供来源

let loveIcon = L.icon({
  iconUrl: heart,
  iconRetinaUrl: heart,
  iconAnchor: [5, 55],
  popupAnchor: [10, -44],
  iconSize: [25, 55],
});

// 只需将其添加到您的地图

L.marker([28, 50], {
       icon: loveIcon,
     }).addTo(map);

我也遇到了同样的问题。 这是我的工作解决方案:

`import L from 'leaflet';
import marker from '../assets/placer.svg';
const myIcon = new L.Icon({
    iconUrl: marker,
    iconRetinaUrl: marker,
    popupAnchor:  [-0, -0],
    iconSize: [32,45],     
});`

您可以组织不同的文件,并更改 Svg 组件属性,如颜色、高度宽度等...

  1. 我们要创建一个函数React Component,注意我们必须将{...props}中的一个spreat传递给svg文件,这样我们可以在执行时更改它:

import React from "react";
export default function PinMoto(props) {
return (
    //its a SVG example, it`s by half, or corrupted, to not occupy large caracter space here, use your SVG file here...
    <svg width="37" height="45" viewBox="0 0 26 34" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
        <path d="M13 0C6.0974 0 0.481453 5.83195 0.481453 13C0.481453 15.1519 0.999529 17.2855 1.98441 19.1779L12.3154 33.5811C12.4529 33.8397 12.715 34 13 34C13.285 34 13.547 33.8397 13.6846 33.5811L24.0194 19.1715C25.0005 17.2855 25.5185 15.1518 25.5185 12.9999C25.5185 5.83195 19.9026 0 13 0Z" fill="#DC462D" {...props}/>
        <g clip-Path="url(#clip0)">
        <path d="M19.0012 12.7109C17.3488 12.7109 16.0023 14.1322 16.0023 15.8763C16.0023 17.6204 17.3453 19.0417 19.0012 19.0417C20.6535 19.0417 22 17.6242 22 15.8763C22 14.1285 20.6535 12.7109 19.0012 12.7109ZM19.0012 18.2513C17.7602 18.2513 16.7512 17.1863 16.7512 15.8763C16.7512 14.5663 17.7602 13.5013 19.0012 13.5013C20.2422 13.5013 21.2512 14.5663 21.2512 15.8763C21.2512 17.1863 20.2422 18.2513 19.0012 18.2513Z" fill="white" />
            
        </g>
        <defs>
            <clippath id="clip0">
                <rect width="18" height="19" fill="white" transform="translate(4 4)" />
            </clippath>
        </defs>
    </svg>);
}

  1. 在另一个文件中导入这个组件Utils.js,在这个文件中我们可以找到一个函数,它是return一个实时修改的React svg组件:

import PinMoto from '../svg_pins/PinMoto'
import { ReactDOM } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { divIcon } from 'leaflet'

export const getRequiredSVGPinByCategory = (category,  myStyle) => { 
    let pin
    switch (category) {              
        case 'motorcycle':
            pin = <PinMoto {...myStyle}/>
            break;                                 
        case 'truck':
            pin = <PinCaminhao {...myStyle}/>
            break;                                                                
        default:
            //pin = <PinPadrao {...myStyle}/>
            break;
        }
    const iconMarkup = renderToStaticMarkup(
       pin
    )
    const customMarketIcon = divIcon({
        html: iconMarkup
    })
    return customMarketIcon
}

  1. 在主文件中,包含一个MapContainer,我们可以这样使用:

import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from 'leaflet';
import   { getRequiredSVGPinByCategory } from '../../utils/util'
    
//your jsx and codes...
    <MapContainer
        center={[-20.268589, -40.290479]}
        zoom={10}
        scrollWheelZoom={true}
        style={{height: 500, width: '100%'}}>
        <TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
            <Marker position={[-20.268589, -40.290479]}
            icon={ getRequiredSVGPinByCategory('motorcycle', {fill: 'orange'} ) }>
            </Marker>
    </MapContainer>
//...

希望对大家有用

在 react-leaflet v3 中这对我来说非常有效: 首先我制作了一个 SVG 图标,然后用 encodeURIComponent 编码,最后我将它传递给 Marker

    import React, {useEffect} from 'react';
    import {MapContainer, Marker, Popup, TileLayer} from 'react-leaflet';
    import Main from "../publicComponents/main";
    import L from "leaflet";
    
    const position = [35.72428729739558, 51.447000503540046]
    const Red_MARKER = `data:image/svg+xml;utf8,${encodeURIComponent(`<?xml version="1.0" encoding="iso-8859-1"?>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36.059" height="36.059" viewBox="0 0 36.059 36.059" style="transform:rotate([=10=]deg)">
      <defs>
        <filter id="Path_10080" x="0" y="0" width="36.059" height="36.059" filterUnits="userSpaceOnUse">
          <feOffset input="SourceAlpha"/>
          <feGaussianBlur stdDeviation="2.5" result="blur"/>
          <feFlood flood-opacity="0.161"/>
          <feComposite operator="in" in2="blur"/>
          <feComposite in="SourceGraphic"/>
        </filter>
      </defs>
      <g id="Group_8038" data-name="Group 8038" transform="translate(5719.5 1106.5)">
        <rect id="Rectangle_2670" data-name="Rectangle 2670" width="21" height="21" transform="translate(-5712 -1099)" fill="none"/>
        <g transform="matrix(1, 0, 0, 1, -5719.5, -1106.5)" filter="url(#Path_10080)">
          <path id="Path_10080-2" data-name="Path 10080" d="M15.4,12.766a6.414,6.414,0,0,0,1.781-5.634l-.446-2.55-2.55-.446A6.414,6.414,0,0,0,8.553,5.916L6.746,7.723c.234-.232-.845.866-.626.626l-2.96,2.96a2.644,2.644,0,0,0,0,3.735l3.114,3.114a2.644,2.644,0,0,0,3.735,0l2.96-2.96Z" transform="translate(19.2 2.96) rotate(45)" fill="${"red"}"/>
        </g>
      </g>
    </svg>
    `)}`;

    const BoatIcon = L.icon({
        iconUrl: Red_MARKER,
        iconSize: [40, 40],
        iconAnchor: [12, 12],
        popupAnchor: [0, 0],
    });
    

    const Index = () => {
        return (<Main>
                <MapContainer center={position} zoom={13}
                              style={{width: "100%", height: "calc(100vh - 80px)", overflow: "hidden"}}>
                    <TileLayer
                        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                    />
                    <Marker position={position} icon={BoatIcon}>
                        <Popup>
                            A pretty CSS3 popup. <br/> Easily customizable.
                        </Popup>
                    </Marker>
                </MapContainer></Main>
        );
    };
    
    export default Index;

您可以只使用 react-leaflet-marker npm 包。它支持动态反应组件作为传单标记

npm i react-leaflet-marker --save

 <Marker
    position={[55.796391, 49.108891]}
    size={[80, 20]} // required for placement
    // you can use optional `placement`
    placement="center" // "top", "bottom"
>
    <div style={{
        background: 'red',
        textAlign: 'center'
    }}>
        center
    </div>
</Marker>