高德地圖實現一個比例圓環形聚合點縮放


前言:碰到一個需求,效果實現一個該范圍不同點數的圓環比例以及總數。比例按照對應的區域內不同內容的數量實現比例圓環比例。

查看高德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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM