記事ディレクトリ
序文
この記事では、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>
効果
搜索刷新后