Web JS Amapポイント、ポイント集約、カスタムアイコン、カスタムフォーム情報、スキン変更等の機能実装と再利用性の高いコンポーネントのカプセル化チュートリアル


序文

この記事では、Amap JS API を使用して、マップ ポイント、集計ポイント、カスタム アイコン、クリック フォーム情報表示などの基本機能を実装する方法について説明し、実際のプロジェクトで遭遇するシナリオ要件に基づいて、再利用性の高いコンポーネントをデモおよびカプセル化します。


1. ポイント集約とは何ですか?

地図上に句読点が多い場合、地図レベルを下げると重なってしまいますが、その際、ローカルエリア内の重なっている点を1つの点に集約し、現在のエリア内の重なっている点の数をマークすることができます。表示用です。地図がズームインまたはズームアウトすると動的に応答します。レンダリングします。

ここに画像の説明を挿入します


2. 開発前の準備

    需要到高德开放平台-控制台申请key,我的应用——添加key——服务平台选择Web端(JS API)

ここに画像の説明を挿入します


3. APIの例

次に、Vue プロジェクトを使用して API 関数を説明します。

1.Amapの紹介

  入口文件index.html引入高德sdk,key填写申请的key
<script src="https://webapi.amap.com/maps?v=1.4.15&key=您申请的key值"></script>

2. マップインスタンスを作成する

创建一个地图容器

<div id="map"></div>

`マップ インスタンスを作成すると、関数はこのインスタンスに依存します。Vue はマウントされた周期関数内で実行する必要があります。

 mounted() {
    
    
//地图实例
 let map = new AMap.Map(
        'map',//地图容器id
        {
    
    
        resizeEnable: true, //是否监控地图容器尺寸变化
        zoom:11, //初始化地图层级
        center: [116.397428, 39.90923], //初始化地图中心点
      //  mapStyle:'amap://styles/blue',//地图样式(背景)可选,可以在后台新建自定义样式
        });
 }

ここに画像の説明を挿入します

マップ スタイルは次のように設定されます:
mapStyle:"amap://styles/${theme}"
テーマ値は次のようにさまざまなスタイルを正式に統合します:
ここに画像の説明を挿入します
公式に提供されたスタイルが満足できない場合は、カスタマイズすることもできます。を作成し
ここに画像の説明を挿入します
、作成した ID を導入して置き換えることができます。
mapStyle:'amap://styles/08539321a17cd7c322f76950f2cxxxxx'

3. 句読点を追加する

   //新建一个标点
   let marker = new AMap.Marker({
    
    
        position:[116.397428, 39.90923], //位置
        offset: new AMap.Pixel(-13, -30),//偏移
        //icon:'', //图标可选,可以使用本地或者在线图标
      });
      
  //监听标点点击事件
   marker.on('click',e=>{
    
    
    console.log(e,'click')
  })
 
   //标点添加到地图上
   map.add(marker)

ここに画像の説明を挿入します

4.句読点を削除する

删除一个或者多个标点,入参markers数组表示标点对象集合

   let marker = new AMap.Marker({
    
    
        position:[116.39, 39.90], //位置
      });
   let marker2 = new AMap.Marker({
    
    
     position:[117.39, 40.90], //位置
    });
    map.add(marker)
    map.add(marker2)
    //删除第一个标点
    map.remove([marker]);

5. すべての句読点 (オーバーレイ) を削除します。

   map.clearMap()

6. 集合ポイント

  //添加2个标点
  let marker = new AMap.Marker({
    
    
        position:[116.397428, 39.90923], //位置
      });
   let marker2 = new AMap.Marker({
    
    
     p position:[116.3680, 39.9200], //位置
    });
    map.add(marker)
    map.add(marker2)
    
   /*设置聚合
   *@param map:地图实例
   *@param markers:标点对象数组
   */
     let cluster = new AMap.MarkerClusterer(map, markers, {
    
    
        gridSize:80
      });

未聚合
ここに画像の説明を挿入します

聚合效果:
集約効果

7. 収束点のスタイルをカスタマイズする

聚合点自定义样式通过设置renderClusterMarker字段配置渲染函数,并在渲染函数中通过dom操作生成样式节点插入聚合点父节点上

      //聚合点实例
     let cluster = new AMap.MarkerClusterer(map, markers, {
    
    
        gridSize: 80,
        renderClusterMarker:renderClusterMarker,//自定义样式渲染
      });
   
  //渲染函数
      function renderClusterMarker(context) {
    
    
      var div = document.createElement("div");
      div.style.width = "50px";
      div.style.height = "50px";
      div.style.lineHeight = "50px";
      div.style.backgroundImage = `url(/static/images/icon.png)`;//自定义图标背景
      div.style.backgroundSize = "100%";
      div.style.backgroundRepeat = "no-repeat";
      div.innerHTML = context.count;//聚合个数
      div.style.color = "#fff";
      div.style.fontSize = "16px";
      div.style.paddingBottom = "10px";
      div.style.boxSizing = "border-box";
      div.style.textAlign = "center";
      var size = Math.round(
        30 + Math.pow(context.count / markers.length, 1 / 5) * 20//markers所有标点对象集合
      );
      context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
      context.marker.setContent(div);
      }

ここに画像の説明を挿入します

8. 集計のクリア

每次重新渲染设置聚合需要清除之前,不然数量会叠加

//cluster:聚合点实例
 cluster&&cluster.setMap(null);

9.フォーム情報を開く

   //新建一个标点
   let marker = new AMap.Marker({
    
    
        position:[116.397428, 39.90923], //位置
        offset: new AMap.Pixel(-13, -30),//偏移
        //icon:'', //图标可选,可以使用本地或者在线图标
      });
      
  //监听标点点击事件(显示窗体信息)
   marker.on('click',e=>{
    
    
       //创建窗体实例
      let infoWindow =new AMap.InfoWindow({
    
    
            content:'test',//窗体内容,支持插入dom.innerHTML 
            anchor:'top-right'//锚点,窗体相对鼠标点击位置
        });
        //显示窗体
        //map:地图实例,[lng,lat]:窗体在地图中位置
        infoWindow.open(map,[e.lnglat.lng,e.lnglat.lat])
  })

   //标点添加到地图上
   map.add(marker)
     
       

anchor可取值:top-left、top-center、top-right、middle-left、center、middle-right、bottom-left、 bottom-center、bottom-right

ここに画像の説明を挿入します

4. 実践開発

要件

1. さまざまなエンジニアリング プロジェクトの位置を地図上にマークする必要があると仮定します。エンジニアリング プロジェクトは、建設中のプロジェクト、完了したプロジェクト、遅れているプロジェクトの 3 つのタイプに分類されます。異なるタイプに異なるアイコンが対応します。 2. 集計機能を実装します。
3.該当するプロジェクトのポップアップ
ウィンドウをクリックして、プロジェクト情報を表示します
4. データ検索には外部検索条件があり、検索などの検索後にマップが再描画されますxxxx から xxxx までの建設中のプロジェクト、またはタイプに基づいた検索など。 5. マップ スタイルの
スキンを変更する大画面機能
6. 次回の開発と使用を容易にするための共通コンポーネントへのカプセル化

レンダリングは次のとおりです。

ここに画像の説明を挿入します

パッケージングのアイデアの分析

怎样封装才能方便使用呢?
コンポーネントのカプセル化では、最初に親コンポーネントに参照を書き込み、次に逆方向に作業してサブコンポーネントのロジックを実装する、逆方向の方法を使用できます。
親コンポーネント内でこのようにマップ コンポーネントを参照することを考えるのは簡単です。

<amap :center="mapCenter" :zoom="zoom" :markers="markers"></amap>

地図の中心点 (センター)、レベル (ズーム)、句読点の経度と緯度の配列 (マーカー) を渡すと、自動的にレンダリングされます。この円錐を使用して拡張を続けます。

聚合样式和信息窗体要如何设计才能适应不同场景的自定义呢?

Vue のカスタム コンテンツで最初に思い浮かぶのは、もちろんスロットですが、スロットの形で呼び出し元に公開することで、自由に DIY できます。

<amap :center="mapCenter" :zoom="zoom" :markers="markers">
       <!-- 聚合样式 -->
        <template v-slot:cluster>
        </template>
        <!-- 窗体样式 -->
        <template v-slot:infoWindow>
        </template>
</amap>

呼び出し全体を推測しましたが、フォームまたは集約スロットでレンダリング データを取得する方法という疑問がまだ残っています。このデータは各句読点と 1 対 1 に対応しています。句読点パラメータ (マーカー) を介してデータを渡し、スコープ スロットを介してデータを渡すことができ、最終的な形状は次のようになります。

  <amap :center="mapCenter" :zoom="zoom" :markers="markers">
       <!-- 聚合样式 -->
        <template v-slot:cluster>
        </template>
        <!-- 窗体样式 -->
        <template v-slot:infoWindow="{ data }">
        </template>
  </amap>

調査の結果、集約ポイント番号データはスコープスロットを介して送信できないことが残念ですが、カプセル化されたコンポーネントの dom 操作を通じてスロットノードに直接スパンノードを追加し、中央に表示する番号を記述して、そして、レンダリング ノードを記述します。これにより、この番号を除いて、残りのアイコン スタイルはスロットを通じてカスタマイズできます。

完全なコード

まず、Amap ツール クラス
amap.jsをカプセル化します。

/**
 * 高德地图工具类
 */
class amap {
    
    
    /**
     * 构造函数
     * @param  id :地图容器id
     * @param  params 地图配置参数
     */
    constructor(id, params) {
    
    
        this.markers = [];//所有标点数组集合
        this.cluster=null;//聚合点实例
        this.map = new AMap.Map(id, {
    
    
            ...params
        });

    }

    /**
    * 添加标点
    * @param markers:标点数组,item支持经纬度或者对象
    * @param clickEvent:标点点击事件回调
    */
    addMarkers(markers = [], clickEvent = () => {
    
     }) {
    
    
        for (let item of markers) {
    
    
            let params = {
    
    
                offset: new AMap.Pixel(-13, -30)
            };
            if (Array.isArray(item)) {
    
    
                params.position = item;
            } else if (typeof item === "object") {
    
    
                params = {
    
     ...item,...params };
            }
            //新建一个标点
            let marker = new AMap.Marker(params);
            //标点点击事件
            marker.on("click", (e) => {
    
    
                typeof clickEvent === 'function' && clickEvent({
    
     ...params, lnglat: e.lnglat })
            });
            //标点添加到地图上
            this.map.add(marker);
            //保存到实例
            this.markers.push(marker)
        }
    }
    //清空地图覆盖物
    clearMap() {
    
    
        this.markers=[]
        this.map.clearMap();
    }
    /**
     * 聚合点
     * @param renderClusterMarker:聚合点自定义渲染函数
     */
    clusterMarker(renderClusterMarker) {
    
    
        //清除之前的聚合
        this.cluster&&this.cluster.setMap(null);  
        //设置聚合
        this.cluster= new AMap.MarkerClusterer(this.map, this.markers, {
    
    
            gridSize: 80,
            renderClusterMarker: renderClusterMarker
        });
    }
    //打开信息窗口
    showInfoWindow({
     
      lng, lat, ...params }) {
    
    
        //创建窗体实例
        let infoWindow = new AMap.InfoWindow(params);
        //显示窗体
        //map:地图实例,[lng,lat]:窗体在地图中位置
        infoWindow.open(this.map, [lng, lat])
    }
    //关闭信息窗口
    closeInfoWindow() {
    
    
        this.map.clearInfoWindow();
    }

}

export default amap

Gaode マップ コンポーネント
amap.vue

<template>
  <div id="amap-container" class="amap-container" :style="layoutStyle">
    <!-- 自定义渲染样式 -->
    <div class="cust-cluster-wrap">
      <slot name="cluster"></slot>
    </div>
    <div class="cust-infoWindow-wrap">
      <slot name="infoWindow" :data="currentMarkerData"></slot>
    </div>
  </div>
</template>

<script>
import amap from "./amap";
export default {
    
    
  name: "Amap",
  props: {
    
    
    //地图宽单位px
    width: {
    
    
      type: [Number, String],
      default: "100%",
    },
    //地图高单位px
    height: {
    
    
      type: [Number, String],
      default: "100%",
    },
    //地图实例化参数
    mapParams: {
    
    
      type: Object,
      default: () => {
    
    },
    },
    //地图中心点
    center: {
    
    
      type: Array,
      default: () => [116.397428, 39.90923],
    },
    //地图层级
    zoom: {
    
    
      type: Number,
      default: 11,
    },
    //标点
    markers: {
    
    
      type: Array,
      default: () => [],
    },
    //是否聚合点
    isCluster: {
    
    
      type: Boolean,
      default: true,
    },
    //点击标点是否显示信息窗口
    isShowInfoWindow: {
    
    
      type: Boolean,
      default: true,
    },
    //信息窗口配置参数
    infoWindowParams: {
    
    
      type: Object,
      default: () => {
    
    },
    },
    //是否点击地图关闭信息窗口
    closeIwOnClickMap: {
    
    
      type: Boolean,
      default: true,
    },
  },
  data() {
    
    
    return {
    
    
      map: null, //地图实例
      cluster: null, //聚合点实例
      currentMarkerData: {
    
    },
    };
  },
  computed: {
    
    
    //设置地图容器宽高
    layoutStyle() {
    
    
      //%或者px兼容处理
      const getAttrVal = (val) =>
        val.toString().includes("%") ? val : `${
      
      val}px`;
      return {
    
    
        width: getAttrVal(this.width),
        height: getAttrVal(this.height),
      };
    },
    //是否自定义聚合点样式
    isCustcluster() {
    
    
      return this.$scopedSlots.cluster;
    },
    //是否自定义信息窗口
    isCustInfoWindow() {
    
    
      return this.$scopedSlots.infoWindow;
    },
  },
  watch: {
    
    
  //监听标点数据重新渲染
    markers: {
    
    
      handler(val) {
    
    
        if (this.map) {
    
    
          //清空地图标点
          this.map.clearMap();
          //重新渲染
          this.addMarkers(val);
          this.isCluster && this.clusterMarker(); //设置聚合点
        }
      },
      immediate: false,
      deep: true,
    },
  },
  mounted() {
    
    
    this.createMap(); //创建地图
    this.addMarkers(this.markers); //添加标点
    this.isCluster && this.clusterMarker(); //设置聚合点
  },
  beforeDestroy() {
    
    
    //销毁地图
    this.map && this.map.map.destroy();
  },
  methods: {
    
    
    //创建地图实例
    createMap() {
    
    
      this.map = new amap("amap-container", {
    
    
        ...this.mapParams,
        zoom: this.zoom,
        center: this.center,
      });
      //地图加载完成
      this.map.map.on("complete", () => {
    
    
        this.$emit("initComplete");
      });
      //地图点击事件
      this.map.map.on("click", (e) => {
    
    
        this.closeIwOnClickMap&&this.closeInfoWindow()
        this.$emit("mapClick", e);
      });
    },
    //标点
    addMarkers(markers = []) {
    
    
      this.map.addMarkers(markers, (e) => {
    
    
        this.currentMarkerData = e;
        //点击标点显示信息窗口
        if (this.isShowInfoWindow) {
    
    
          //等待currentMarkerData数据渲染更新完成在打开信息窗口
          this.$nextTick(() => {
    
    
            this.map.showInfoWindow({
    
    
              lat: e.lnglat.lat,
              lng: e.lnglat.lng,
              ...this.infoWindowParams,
              isCustom: this.isCustInfoWindow,
              content: this.getCustInfoWindowDom() || e.infoWindowContent || "",
            });
          });
        }
        //派发标点点击事件
        this.$emit("markerClick", e);
      });
    },
    //聚合标点
    clusterMarker() {
    
    
      //自定义渲染函数
      function renderClusterMarker(context) {
    
    
        //获取自定义聚合点DOM
        let custClusterDom =
          document.getElementsByClassName("cust-cluster-wrap")[0];
        let div = document.createElement("div");
        div.innerHTML = custClusterDom.innerHTML;
        let span = document.createElement("span");
        span.style.position = "absolute";
        span.style.top = "50%";
        span.style.left = "50%";
        span.style.transform = "translate(-50%,-50%)";
        span.style.zIndex = "99";
        //设置聚合数
        span.innerHTML = context.count;
        //插入聚合数量span节点
        div.children[0].appendChild(span);
        let size = Math.round(
          30 + Math.pow(context.count / this.map.markers.length, 1 / 5) * 20
        );
        context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
        context.marker.setContent(div);
      }

      //聚合
      this.map &&
        this.map.clusterMarker(
          this.isCustcluster ? renderClusterMarker.bind(this) : undefined
        );
    },
    //获取自定义窗口Dom
    getCustInfoWindowDom() {
    
    
      if (!this.isCustInfoWindow) return;
      return document.getElementsByClassName("cust-infoWindow-wrap")[0]
        .innerHTML;
    },
    //关闭信息窗口
    closeInfoWindow() {
    
    
      this.map.closeInfoWindow();
    },
  },
};
</script>

<style lang="scss" scoped>
.amap-container {
    
    
  width: 100%;
  height: 100%;
}
.cust-cluster-wrap {
    
    
  position: fixed;
  top: 0;
  left: 0;
  transform: translate(-100%, -100%);
}
.cust-infoWindow-wrap {
    
    
  position: fixed;
  top: 0;
  left: 0;
  transform: translate(-100%, -100%);
}
</style>

ページコール:
inde.vue

<template>
  <div class="container">
    <!-- 地图区域 -->
    <div class="map-wrap">
      <amap
        :center="mapCenter"
        :zoom="zoom"
        :mapParams="mapParams"
        :markers="markers"
        :infoWindowParams="infoWindowParams"
        isCluster
        @initComplete="onInitComplete"
        @mapClick="onMapClick"
      >
        <!-- 聚合样式 -->
        <template v-slot:cluster>
          <div class="cluster">
            <img class="icon" src="/static/images/icon.png" />
          </div>
        </template>
        <!-- 窗体样式 -->
        <template v-slot:infoWindow="{ data }">
          <div class="infoWindow">
            <div class="name">{
    
    {
    
     data.projectName }}</div>
            <div class="row">电话:{
    
    {
    
     data.phone }}</div>
            <div class="row">地址:{
    
    {
    
     data.address }}</div>
          </div>
        </template>
      </amap>
    </div>
    <!-- 搜索按钮 -->
    <button class="search" @click="onSearch">搜索(模拟刷新数据)</button>
  </div>
</template>

<script>
import amap from "./component/amap/amap.vue";
export default {
    
    
  components: {
    
    
    amap,
  },
  data() {
    
    
    return {
    
    
      markers: [], //标点集合
      mapCenter: [116.397428, 39.90923],
      zoom: 13,
      mapParams: {
    
    
        mapStyle: "amap://styles/blue", //地图样式
      },
      infoWindowParams: {
    
    
        anchor: "top-right",
        offset: new AMap.Pixel(0, 15), //偏移
      },
    };
  },
  created() {
    
    
    this.onSearch();
  },
  mounted() {
    
    },
  methods: {
    
    
    //加载完成
    onInitComplete() {
    
    
      console.log("加载完成");
    },
    //点击地图
    onMapClick(e) {
    
    
      console.log(e, "点击地图");
    },
    //搜索
    onSearch() {
    
    
      this.markers = [];
      this.$nextTick(() => {
    
    
        //模拟接口生成数据
        for (let i = 0; i < parseInt(Math.random() * 20); i++) {
    
    
          let [lng, lat] = this.mapCenter;
          let position = [lng + i * Math.random() * 0.05, lat + i * 0.01];
          this.markers.push({
    
    
            position,//经纬度
            icon: `/static/images/map_icon${
      
      
              parseInt(Math.random() * 3) + 1
            }.png`,//标点图标
            projectName: `项目${
      
      i}`,//项目名称
            phone: "13333333333",//电话
            address: "北京市朝阳区望京阜荣街10号",//地址
          });
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.container {
    
    
  width: 100%;
  height: 100%;
}
.map-wrap {
    
    
  height: 90%;
  width: 100%;
  .cluster {
    
    
    height: 60px;
    width: 60px;
    border-radius: 50%;
    color: #fff;
    font-size: 16px;
    line-height: 50px;
    .icon {
    
    
      width: 100%;
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
    }
  }
  .infoWindow {
    
    
    padding: 20px;
    box-sizing: border-box;
    border-radius: 10px;
    background: #fff;
    .name {
    
    
      font-size: 18px;
      color: rgb(39, 130, 248);
    }
    .row {
    
    
      margin-top: 10px;
      font-size: 14px;
    }
  }
}
.search {
    
    
  margin-top: 10px;
  width: 150px;
  height: 40px;
}
</style>

効果

ここに画像の説明を挿入します

搜索刷新后
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/sd1sd2/article/details/132297543