高德地图实现一个比例圆环形聚合点缩放

前言:碰到一个需求,效果实现一个该范围不同点数的圆环比例以及总数。比例按照对应的区域内不同内容的数量实现比例圆环比例。

查看高德api只有点聚合效果,我们可以根据他的基础api来增加实现更高级的效果改造。

先上效果图

1、最低层级时候效果:(这里就是打点而已)

 

2、缩放时候:(这时候将红色和蓝色圆环缩放在一个区域,我们绘制出来圆环显示对应数值3个红色和4个蓝色)

3、继续缩放时候效果(注意:红色和蓝色是该区域内的比例,所占比例是一样的,总数是7)

 

 附上代码:可以直接复制黏贴使用

该功能核心是将canvas绘制的圆环通过base64转为图片,然后传给高德api渲染

 

(比较粗略,但是这个实现基础,有其他需求可以自行修改。点个赞评论再走,不要白嫖)

import React from 'react';
import { connect } from 'dva';
import styles from './index.less';
import iconAddress from '../../static/dir-marker.png';
import { Button } from 'antd';

/**
 * 全局变量
 * **/
const AMap = window.AMap;

class LBSMap extends React.Component {

  state = {
    mapLang: (localStorage.getItem('lang') === 'TC' || localStorage.getItem('lang') === 'CHS') ? 'zh_cn' : 'en',   //en:英文,zh_en:中英文对照
    aaa: null,
    blueIconArr: [],
    redIconArr: [],
    markers: [],
  };

  componentDidMount() {
    this.renderRing();
  }

  /********************************************使用renderClusterMarker属性实现聚合点的完全自定义绘制*****************************************/
  renderRing = (blueIconArr = []) => {
    const markers = [];
    /***
     * 创建地图实例
     * **/
    const map = new AMap.Map('lbsMap', {
      zoom: 13,//级别
      center: [113.55891, 22.17059],//中心点坐标
      // lang: this.state.mapLang,
      expandZoomRange: true,
    });
    /***
     * 异步同时加载多个插件
     * AMap.MarkerClusterer点聚合插件、AMap.CircleEditor圆编辑插件、AMap.ElasticMarker灵活点标记,
     * 可以随着地图级别改变样式和大小的 Marker、AMap.AdvancedInfoWindow高级信息窗体
     * **/
    AMap.plugin(['AMap.ToolBar', 'AMap.MarkerClusterer'], function() {
      var toolbar = new AMap.ToolBar();
      map.addControl(toolbar);
    });

    /******
     *  蓝色的点模拟数据
     * ****/
      // 创建一个 蓝色Icon
    const blueIcon_3d93fd = new AMap.Icon({
        size: new AMap.Size(25, 34),// 图标尺寸
        image: iconAddress, // 图标的取图地址
        imageSize: new AMap.Size(135, 40),// 图标所用图片大小
        imageOffset: new AMap.Pixel(-9, -3), // 图标取图偏移量
      });
    for (let i = 0; i < 7; i++) {
      if (i % 2 === 0) {
        this.state.blueIconArr.push({ x: `113.57${i}41`, y: `22.164${i}32` });
      } else {
        this.state.blueIconArr.push({ x: `113.56${i}11`, y: `22.132${i}59` });
      }
    }
    const redIcon_f34234 = new AMap.Icon({
      size: new AMap.Size(25, 34),
      image: iconAddress,
      imageSize: new AMap.Size(135, 40),
      imageOffset: new AMap.Pixel(-96, -3),
    });

    for (let i = 0; i < 6; i++) {
      if (i % 2 === 0) {
        this.state.redIconArr.push({ x: `113.55${i}71`, y: `22.167${i}42` });
      } else {
        this.state.redIconArr.push({ x: `113.54${i}91`, y: `22.122${i}59` });
      }
    }

    this.state.blueIconArr.forEach(item => {
      markers.push(new AMap.Marker({
        position: new AMap.LngLat(item.x, item.y),
        icon: blueIcon_3d93fd,
        offset: new AMap.Pixel(-15, -20),
        type: 'blueIcon_3d93fd',
      }));
    });
    this.state.redIconArr.forEach(item => {
      markers.push(new AMap.Marker({
        position: new AMap.LngLat(item.x, item.y),
        icon: redIcon_f34234,
        offset: new AMap.Pixel(-15, -20),
        type: 'redIcon_f34234',
      }));
    });


    var _renderClusterMarker = function(mapContext) {
      console.log('context', mapContext.markers);
      /*************计算颜色在圆的比例为多少*************/
      const orangeColorRing = [];
      const yellowColorRing = [];
      const greenColorRing = [];
      mapContext.markers.forEach(item => {
        const itemColorType = item.De.type;
        if (itemColorType === 'greenBlueIcon_0ccae7') {
          orangeColorRing.push(itemColorType);
        }
        if (itemColorType === 'redIcon_f34234') {
          yellowColorRing.push(itemColorType);
        }
        if (itemColorType === 'blueIcon_3d93fd') {
          greenColorRing.push(itemColorType);
        }
      });
      const orangeNumber = orangeColorRing.length;
      const yellowNumber = yellowColorRing.length;
      const greenNumber = greenColorRing.length;
      const total = orangeNumber + yellowNumber + greenNumber;
      const orangePer = orangeNumber / total;
      const yellowPer = yellowNumber / total;
      const greenPer = greenNumber / total;
      const ringPerInTotal = orangePer + yellowPer + greenPer;
      const perInTotal1 = (orangePer / ringPerInTotal) * 2;
      const perInTotal2 = (yellowPer / ringPerInTotal) * 2;
      const perInTotal3 = (greenPer / ringPerInTotal) * 2;

      function process() {
        const ring = arguments[0];
        const canvas = document.getElementById(ring.canvasId);
        const context = canvas.getContext('2d');
        const centerX = ring.canvasW / 2;
        const centerY = ring.canvasH / 2;
        const borderWidth = ring.bdWidth;
        const radius = ring.canvasW / 2 - borderWidth / 2;
        canvas.width = ring.canvasW;
        canvas.height = ring.canvasH;
        //绘制内圈
        context.save();
        context.beginPath();
        context.arc(centerX, centerY, radius, 0, 360, false);
        context.fillStyle = 'rgba(255, 255, 255, 0.75)';
        context.fill();
        context.stroke();
        context.restore();
        //圆环中文字
        context.save();
        context.beginPath();
        context.font = '18px Georgia';
        context.textAlign = 'center';
        context.fillStyle = 'black';
        context.fillText(mapContext.count, centerX, centerY + 6);
        context.restore();

        const ringFunction1 = (start, end, color) => {
          context.save();
          context.beginPath();
          context.lineWidth = borderWidth;
          context.arc(centerX, centerY, radius, start, end, false);
          context.strokeStyle = color;
          context.stroke();
          context.closePath(); //路径结束
          context.restore();
        };

        const rad = Math.PI;
        const rad1 = -Math.PI / 2 + perInTotal1 * rad;
        const rad2 = -Math.PI / 2 + (perInTotal1 + perInTotal2) * rad;
        const rad3 = -Math.PI / 2 + (perInTotal1 + perInTotal2 + perInTotal3) * rad;

        ringFunction1(-Math.PI / 2, rad1, '#0ccae7');
        ringFunction1(rad1, rad2, '#f34234');
        ringFunction1(rad2, rad3, '#3d93fd');
      }

      /*********************调用方法*************************/
      const canvasDiv = document.getElementById('canvasDiv');
      const canvasW = canvasDiv.offsetWidth;
      const canvasH = canvasDiv.offsetWidth;
      process({
        canvasId: 'canvasDiv',  //canvas的Id
        canvasW: canvasW,        //canvas的width
        canvasH: canvasH,        //canvas的height
        bdWidth: 6,          //圆环的宽
      });
      /***************将绘制的canvas转化为img交给高德***********/
      const dataURL = canvasDiv.toDataURL();
      const img = document.createElement('img');
      img.src = dataURL;
      img.alt = '';
      const count = markers.length;
      const size = Math.round(30 + Math.pow(mapContext.count / count, 1 / 5) * 20);   //设置图像偏移量
      mapContext.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
      mapContext.marker.setContent(img);
    };
    /**********************运行高德地图自定义实例***************/
    var cluster = new AMap.MarkerClusterer(map, markers, {
      gridSize: 80,
      renderClusterMarker: _renderClusterMarker,
    });
  };


  addRed = () => {
    this.setState({ blueIconArr: [] });
    //this.state.blueIconArr.push({x: `113.53331`, y: `22.1644332`})
    // 创建一个红色 icon
    const redIcon_f34234 = new AMap.Icon({
      size: new AMap.Size(25, 34),
      image: iconAddress,
      imageSize: new AMap.Size(135, 40),
      imageOffset: new AMap.Pixel(-96, -3),
    });
    // 创建一个青色 icon
    const greenBlueIcon_0ccae7 = new AMap.Icon({
      size: new AMap.Size(25, 34),
      image: iconAddress,
      imageSize: new AMap.Size(135, 40),
      imageOffset: new AMap.Pixel(-51, -3),
    });
  };

  render() {
    console.log('state', this.state.blueIconArr);
    return (
      <React.Fragment>
        <div id="lbsMap" className={styles.LBSMap}>
          <canvas id="canvasDiv" width="56" height="56"></canvas>
        </div>
      </React.Fragment>
    );
  }
}

export default LBSMap;

 

posted @ 2020-05-19 13:50  月亮出来了  阅读(2656)  评论(3编辑  收藏  举报