前言:高版本的react-amap 支持MassMarkers 组件用于一次性添加大量的标记点。本次优化的海量点是在低版本react-amap的基础上。官方推荐使用聚合useCluster属性来优化海量点的渲染。
直接附上代码:
import React, { Component } from "react";
import { Map, Markers, Polygon, InfoWindow } from 'react-amap'
import {
Form,
Switch,
message,
} from "antd";
import ActionsForm from "components/RoutePanel/ActionsForm";
import { MAP_AMAP_KEY, MAP_AMAP_VERSION } from 'constants/config'
import {
API_FETCH_ZONES_DEPT_POINT,
} from 'constants/api'
import fetch from 'common/fetch'
import styles from "./amap.css";
import { debounce } from 'lodash';
const polygonType = {
main: 'main',
adjacent: 'adjacent',
normal: 'normal',
}
// 获取区域样式
const getPolygonStyle = (pt) => {
switch (pt) {
case polygonType.main:
return {
fillColor: '#DC2626',
fillOpacity: 0.5,
strokeOpacity: 0.5,
strokeWeight: 2,
strokeColor: '#DC2626',
}
case polygonType.adjacent:
return {
fillColor: '#34D399',
fillOpacity: 0.5,
strokeOpacity: 1,
strokeWeight: 1,
strokeColor: '#34D399',
}
case polygonType.normal:
default:
return {
fillColor: '#333333',
fillOpacity: 0.3,
strokeOpacity: 0.5,
strokeWeight: 1,
strokeColor: '#333333',
}
}
}
export class ZoneNeighborhoodMap extends Component {
constructor(props) {
super(props)
this.map = null; // 地图实例
this.mapEvents = {
created: (mapInstance) => {
this.map = mapInstance;
this.map.on('zoomend', this.handleZoom);
},
close: () => {
this.map = null;
if (this.map) {
this.map.off('zoomend', this.handleZoom);
}
}
};
this.state = {
infoWindowPosition: null,
infoWindowTitle: '',
polygonActive: false,
pointReferShow: true, //运输点参考
pointReferData: [],
areaReferShow: true, //已绘制参考
areaReferData: [],
locTypes: [],
data: props.data,
// infoWindowPosition: {
// longitude: 120,
// latitude: 30,
// },
infoWindowVisible: false,
infoWindowData: {
id: "",
desc: "",
},
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
useCluster:false
}
this.markerEvents = {
created: (e) => {},
mouseover: (e) => {
const position = e.target.getPosition();
this.setState({
infoWindow: {
position: {
longitude: position.lng,
latitude: position.lat,
},
visible: true,
data: e.target.getExtData(),
},
});
},
mouseout: (e) => {
this.setState({
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
});
},
};
}
componentWillUnmount() {
if (this.map) {
this.map.destroy();
}
}
componentWillUnmount() {
if (this.map) {
this.map.destroy()
}
}
// 缩放事件处理函数
handleZoom = () => {
const zoomLevel = this.map.getZoom();
console.log('zoomLevel',zoomLevel)
if (zoomLevel > 8) {
this.setState({ useCluster: false });
} else {
this.setState({ useCluster: true });
}
};
setFitViewWithZoneId(zoneId) {
const fitOverlays = []
this.map.getAllOverlays().forEach(polygon => {
if (polygon.getExtData().zoneId === zoneId) {
fitOverlays.push(polygon)
}
})
if (fitOverlays.length) {
this.map.setFitView(fitOverlays, false, undefined, this.map.getZoom())
}
}
renderPolygons() {
const { mainZoneId, polygons, toggleAdjacentZone, adjacentZoneIds } = this.props
const l = polygons.length
const _polygons = []
for (let i = 0; i < l; i++) {
const detail = polygons[i]
if (detail.geoArea && detail.geoArea.length) {
let _polygonType = polygonType.normal
if (detail.zoneId === mainZoneId) {
_polygonType = polygonType.main
} else if (adjacentZoneIds.includes(detail.zoneId)) {
_polygonType = polygonType.adjacent
}
detail.geoArea.forEach((path, pathId) => {
_polygons.push(
<Polygon
path={path}
key={`${detail.id}:${pathId}`}
style={getPolygonStyle(_polygonType)}
events={{
click: () => {
if (detail.zoneId === mainZoneId) {
return
}
toggleAdjacentZone(detail.zoneId)
},
mousemove: (e) => {
this.setState(() => ({
infoWindowPosition: e.lnglat,
infoWindowTitle: detail.zoneDesc
}))
},
mouseout: () => {
this.setState(() => ({
infoWindowPosition: null
}))
}
}}
extData={{ zoneId: detail.zoneId }}
/>
)
})
}
}
return _polygons
}
renderInfoWindow() {
const { infoWindowPosition, infoWindowTitle } = this.state
if (!infoWindowPosition) {
return null
}
return <InfoWindow
position={{
longitude: infoWindowPosition.lng,
latitude: infoWindowPosition.lat,
}}
isCustom={true}
content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
offset={[2,-10]}
visible
/>
}
getPoint = ()=>{
fetch(API_FETCH_ZONES_DEPT_POINT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ deptId:this.props.deptId })
}).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if(json.data instanceof Array){
if (!json.data.length) {
message.info("没有可显示的运输地点");
}else{
if(json.data.length > 500){
this.setState({
useCluster: true,
})
}
json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
this.setState({pointReferData:json.data},()=>{
if (!this.map) return; // 如果地图实例未初始化,则不执行
this.map.setFitView();
})
}
}
})
.catch(e=>{})
}
renderInfo = () => {
const { position, visible, data } = this.state.infoWindow;
const {locTypes} = this.state
if (!data) {
return null;
}
const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
const tds = (
<div className={styles.info}>
<div className='inforow'>
<span>运输地点代码</span>
<span>{data.locId}</span>
</div>
<div className='inforow'>
<span>运输地点描述</span>
<span>{data.locDesc}</span>
</div>
<div className='inforow'>
<span>类型</span>
<span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
</div>
<div className='inforow'>
<span>城市</span>
<span>{data.city}</span>
</div>
<div className='inforow'>
<span>省份</span>
<span>{data.province}</span>
</div>
<div className='inforow'>
<span>国家</span>
<span>{data.country}</span>
</div>
<div className='inforow'>
<span>经度</span>
<span>{data.longitude}</span>
</div>
<div className='inforow'>
<span>纬度</span>
<span>{data.latitude}</span>
</div>
<div className='inforow'>
<span>地址</span>
<span>{data.addr}</span>
</div>
<div className='inforow'>
<span>限行区域</span>
<span>{data.udzDesc1}({data.udz1})</span>
</div>
<div className='inforow'>
<span>业务区域</span>
<span>{data.udzDesc2}({data.udz2})</span>
</div>
</div>
);
return (
<InfoWindow
position={position}
visible={visible}
isCustom={true}
offset={[2, 2]}
>
{tds}
</InfoWindow>
);
};
pointRender = (extData) => {
return (
<div
style={{
color: "#4e72b8",
width: "8px",
height: "8px",
borderRadius: "50%",
background: "#4169E1",
textAlign: "center",
}}
/>
);
};
initPointReferData = () => {
if (!this.state.pointReferShow) {
this.setState({
pointReferData: [],
});
} else {
this.getPoint()
}
};
componentDidMount() {
this.initPointReferData();
}
// 运输点参考
// changePointReferShow = (checked) => {
// this.setState({ pointReferShow: checked }, () => {
// this.initPointReferData();
// });
// };
changePointReferShow = debounce((checked) => {
this.setState({ pointReferShow: checked }, () => {
this.initPointReferData();
});
}, 300);