react-amap海量点优化

前言:高版本的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); 
上一篇:震撼推荐!性能测试全攻略:零基础也能玩转性能测试!


下一篇:Modern Effective C++:Item 6 auto推导若非己愿,使用显式类型初始化惯用法