您好,登錄后才能下訂單哦!
本篇內容介紹了“vue3中如何使用高德地圖api”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在開發之前我們需要了解
vue3
中接入高德地圖的幾個步驟
首先安裝包并引入
npm i @amap/amap-jsapi-loader --save
import AMapLoader from '@amap/amap-jsapi-loader'
使用官方介紹的方式進行加
vue2
和 vue3
是有區別的,這里我們使用的是 vue3
,但這里 vue3
的方式還是選項式,不是組合式的,我自己寫的時候使用的是組合式的,且集成了 ts
, 我后面發布完整 .vue
文件的時候 會去掉標簽上的 ts,因為類型還沒有完善,等后面完善了再貼更改以后得。為什么要使用 shallowRef
官方也給出了說明原因。
這里我直接把我前面,寫過的 地圖業務需求的業務邏輯拿過來的,沒有使用框架,直接在一個 html 文件當中引入,鏈接大家可以點擊下面進行查看:
高德地圖jsApi的使用
高德地圖jsApi的點和線配置
高德地圖jsApi的右鍵設置
高德地圖jsApi的點位新增
高德地圖jsApi的圖例
使用vue3
的時候,實例化的方式,this
的問題, 以及插入字符串模板的時候 事件響應的方式都需要更改,還是很麻煩的
首先導入的方式,和官網一樣,后面我會貼完整代碼, 這里我們使用 plugins
加載插件, 其他配置如 Loca
, 直接進行配置, 這里需要注意版本問題, 寫成 ‘2.0’ 是不行的,初始化函數在 onmounted
生命周期中執行。
AMap存儲 這里我做了很多存儲,大家知道 .value
的語法是 vue3
獲取 ref
的語法,我下面使用到的 都是ref
,后面完整代碼可以查看, 這里掛載的時候直接存一下,因為很多工具方法都會只用到他,這里后期業務邏輯我會抽離到 pinia
中去,所以不需要在初始化函數中寫全部的業務邏輯。
模版樣式不生效問題, 我們在使用的時候, 就像我之前寫的文章,點位新增的時候,我們會插入 content
字符串模版,替換點樣式,這里有兩種方案修改樣式,一種是 插入 DOM
,不使用字符串,然后在 DOM
上通過 style
直接修改樣式,另一種就是使用模版的時候直接給 class
類名,但是這種樣式如果我們給 vue
的 style
加了 scoped
就不會生效,這里大家可以自己靈活選擇用哪種,我這里暫時先使用模版的方式,去掉了 scoped
。
圖例, 圖例這里除了導入的時候,需要配置一下,使用上來說變化不大,樣式的修改還是復用了我之前的邏輯。
import AMapLoader from '@amap/amap-jsapi-loader'
const initMap = () => {
AMapLoader.load({
key: 'b59c490f61a694b9d7576dd864f74d6e', // 申請好的Web端開發者Key,首次調用 load 時必填
version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時默認為 1.4.15
plugins: ['AMap.Scale', 'AMap.ToolBar', 'AMap.MouseTool'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
Loca:{
version:'2.0.0'
}
})
.then((res) => {
AMap.value = res
// 上來就顯示的中心點 北京 116.397, 39.918
var lnglat = new res.LngLat(105, 38)
map.value = new res.Map('container', {
//設置地圖容器id
viewMode: '3D', //是否為3D地圖模式
zoom: 5, //初始化地圖級別
center: lnglat, //初始化地圖中心點位置
})
map.value.clearMap() // 清除地圖覆蓋物
// 地圖是否可拖拽和縮放
map.value.setStatus({
dragEnable: true, // 是否可拖拽
zoomEnable: true, // 是否可縮放
})
initWindow()
// 添加一些分布不均的點到地圖上,地圖上添加三個點標記,作為參照
coordData.forEach(function (marker) {
setMarker(marker)
})
let renderLine = setLine(coordData)
// 設置線
let polyline = renderLine.reduce((prev, item, index) => {
let weight = item.type === 1 ? 5 : 3
let color = item.type === 1 ? headColors[0] : headColors[1]
prev.push(setLines(item.current, color, weight))
return prev
}, [])
map.value.add([...polyline]) // 繪制線
//創建右鍵菜單
menuInstance.value = new ContextMenu(map.value)
let loca = new Loca.Container({
map:map.value,
});
window._loca = loca;
// 圖例, 圖例可以實例化多個,使用定位來設置位置
let lengend = new Loca.Legend({
loca: loca,
title: {
label: '管道類型',
fontColor: 'rgba(255,255,255,1)',
fontSize: '16px'
},
style: {
backgroundColor: 'rgba(255,255,255,0.2)',
left: '20px',
bottom: '40px',
fontSize: '12px'
},
dataMap: [
{ label: '省級管道', color: headColors[1] },
{ label: '縣級管道', color: headColors[0] },
],
});
//修改圖例排列方式
document.getElementsByClassName("amap-loca loca-controls")[0].setAttribute('id', 'testid')
var lis = document.querySelectorAll("#testid li");
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
lis[i].setAttribute("class", 'test'
);
}
})
.catch((e) => {
console.log('error', e)
})
}
onMounted(() => {
initMap()
})
右鍵菜單, 右鍵菜單這里官方給我們的示例是使用一個 函數 進行實例化,里面使用了
this
, 所以這個我單獨拿出來,首先我們看一下官方的 demo
這里使用了一個函數,但這個函數還不是類,但是他卻在里面使用了this
,實話來講,這種寫法確實不是很優秀,可擴展性很差,不夠健壯,但沒辦法,誰讓我們用了人家的東西呢是吧, 在 vue3
中這么用就不可以了,首先 vue3
里面使用 this
就不是官方建議的, 另外這里面還修改了函數原型上的方法,其實我得代碼里面一共有兩種右鍵菜單,如下:
一種是在指定點位上打開,另一種是在非點位的空白處打開,指定點位處打開的其實叫信息窗體,只不過是通過右鍵的方式觸發,那個沒有上面這個右鍵菜單麻煩。
首先來說 this
問題, 這里的 this
實際上就是把我們的實例化對象掛載到上面而已,vue3
中沒辦法像 vue2
那樣使用 this
, 但也提供給我們了 api
來獲取當前組件的實例化對象, 然后我沒用使用函數, 使用了一個類,類構造這個方法, 模版也不適用字符串模版,因為這里字符串模版的事件綁定寫死了,我們使用 DOM
來動態綁定事件,代碼如下:
const { ctx } = getCurrentInstance()
const _this = ctx
//自定義菜單類
class ContextMenu {
constructor(map) {
var me = _this
//地圖中添加鼠標工具MouseTool插件
_this.mouseTool = new AMap.value.MouseTool(map)
_this.contextMenuPositon = null
const fragment = document.createElement('div') // 使用 DOM 方式, 方便添加事件
fragment.className = 'info context_menu'
const p = document.createElement('p')
p.addEventListener('click', this.delMarkerMenu)
p.textContent = '移除上次選中信息'
fragment.appendChild(p)
//通過content自定義右鍵菜單內容
_this.contextMenu = new AMap.value.ContextMenu({
isCustom: true,
content: fragment,
})
//地圖綁定鼠標右擊事件——彈出右鍵菜單
map.on('rightclick', function (e) {
me.contextMenu.open(map, e.lnglat)
me.contextMenuPositon = e.lnglat //右鍵菜單位置
})
}
delMarkerMenu() {
// 右鍵菜單上次選中點的信息
clearPoint()
_this.mouseTool.close()
_this.contextMenu.close()
}
}
<!--
* @Description: 地圖
* @Autor: codeBo
* @Date: 2023-03-06 16:10:10
* @LastEditors: gjzxlihaibo@163.com
* @LastEditTime: 2023-03-07 14:59:08
-->
<template>
<div id="root">
<div>
<h4>添加選點請輸入坐標</h4>
<label>
經度:
<input />
</label>
<label>
緯度:
<input />
</label>
<button>輸入完成</button>
<button>清空輸入</button>
</div>
<div id="container"></div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, getCurrentInstance } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import { shallowRef } from 'vue'
import { coordData } from './data'
const map = shallowRef(null)
const { ctx } = getCurrentInstance()
const _this = ctx
const menuInstance = ref() // menu 實例
let AMap = ref() // map 實例
let currentPonit = ref<HTMLElement | null>(null) // 存儲當前選中點 DOM
let currentData = reactive({}) // 當前選重點信息
let sourceInfoWindow = ref()
const headColors = ['#3366bb', '#6622FF']
// 工具方法
// 修改DOM 類名
function changeStyle(res, data) {
if (currentPonit.value !== null) {
currentPonit.value.classList.remove('active')
}
currentPonit.value = res.children[0]
currentData = data
currentPonit.value.classList.add('active')
}
// 清除點信息
function clearPoint() {
if (currentPonit.value) {
currentPonit.value.classList.remove('active')
}
currentPonit.value = null
currentData = {}
}
// 設置線信息
function setLines(lnglat, color, weight) {
return new AMap.value.Polyline({
path: lnglat,
// showDir:true ,// 設置線方向
strokeColor: color, // 線顏色
strokeWeight: weight, // 線寬
strokeOpacity: 0.6, // 透明度
})
}
function markerClick(e) {
console.log('sourceInfoWindow.value', sourceInfoWindow.value, e.target)
sourceInfoWindow.value.setContent(e.target.contents)
sourceInfoWindow.value.open(map.value, e.target.getPosition())
}
function setInput(e, name) {
let text =
e.target.parentElement.parentElement.children[0].innerText.split(
'供給點',
)[0]
let current = coordData.filter((item) => {
return item.name === text
})
window.localStorage.setItem(text + name, e.target.value)
}
const initWindow = () => {
// 信息窗體
let infoWindow = new AMap.value.InfoWindow({
offset: new AMap.value.Pixel(0, -10),
retainWhenClose: true,
})
sourceInfoWindow.value = infoWindow
infoWindow.on('open', function (...arg) {
let inputOut = document.getElementById('inputOut')
let inputPro = document.getElementById('inputPro')
inputOut.addEventListener('change', (e) => {
setInput(e, 'inputOut')
window.location.reload()
})
inputPro.addEventListener('change', (e) => {
setInput(e, 'inputPro')
window.location.reload()
})
})
}
// 抽離點位信息設置
function setMarker(marker) {
//創建右鍵菜單
var contextMenu = new AMap.value.ContextMenu()
//右鍵放大
contextMenu.addItem(
'放大一級',
function () {
map.value.zoomIn()
},
0,
)
//右鍵縮小
contextMenu.addItem(
'縮小一級',
function () {
map.value.zoomOut()
},
1,
)
contextMenu.addItem('設置起點', function () {
console.log('設置起點', marker, markerd.dom)
changeStyle(markerd.dom, marker)
contextMenu.close() // 關閉右鍵菜單
})
contextMenu.addItem('與起點連線', function () {
if (!currentPonit) {
alert('請選擇起點')
contextMenu.close()
return
} else {
// 這里其實可以根據數據判定線類型了,因為第二個選中點的信息+和第一個選中點的信息都有了,但是過濾方法會比較復雜
let path = [currentData.position, marker.position]
const polyline1 = setLines(path, '#3366bb', 5)
map.value.add([polyline1])
clearPoint()
}
contextMenu.close() // 關閉右鍵菜單
})
let content = '<div></div>'
var markerd = new AMap.value.Marker({
map: map.value,
// icon: marker?.icon,
content,
offset: new AMap.value.Pixel(-8, -8),
visible: true, // 點標記是否可見
position: [marker.position[0], marker.position[1]],
})
let inputO = window.localStorage.getItem(marker.name + 'inputOut')
let inputP = window.localStorage.getItem(marker.name + 'inputPro')
// 左鍵點擊的信息窗體, 寬度會在碰觸到容器邊緣的時候自適應的縮小
markerd.contents = `
<div>${marker.name}供給點</div>
<div>出口壓力:<input id="inputOut" value="${
inputO ?? marker?.pointData?.out
}"/>kPa</div>
<div>供給量:<input id="inputPro" value="${
inputP ?? marker?.pointData?.provide
}" />m3</div>
<div>位置:經度${marker.position[0]},緯度${marker.position[1]}</div>`
markerd.data = marker
markerd.on('click', markerClick)
if (marker.name === '新疆') {
// 觸發上面的點擊事件
markerd.emit('click', { target: markerd })
}
//綁定鼠標右擊事件——彈出右鍵菜單
markerd.on('rightclick', function (e) {
contextMenu.open(map.value, e.lnglat)
})
return markerd
}
//自定義菜單類
class ContextMenu {
constructor(map) {
var me = _this
//地圖中添加鼠標工具MouseTool插件
_this.mouseTool = new AMap.value.MouseTool(map)
_this.contextMenuPositon = null
const fragment = document.createElement('div') // 使用 DOM 方式, 方便添加事件
fragment.className = 'info context_menu'
const p = document.createElement('p')
p.addEventListener('click', this.delMarkerMenu)
p.textContent = '移除上次選中信息'
fragment.appendChild(p)
//通過content自定義右鍵菜單內容
_this.contextMenu = new AMap.value.ContextMenu({
isCustom: true,
content: fragment,
})
//地圖綁定鼠標右擊事件——彈出右鍵菜單
map.on('rightclick', function (e) {
me.contextMenu.open(map, e.lnglat)
me.contextMenuPositon = e.lnglat //右鍵菜單位置
})
}
delMarkerMenu() {
// 右鍵菜單上次選中點的信息
clearPoint()
_this.mouseTool.close()
_this.contextMenu.close()
}
}
// 過濾線方法
function setLine(arr) {
return arr.reduce((prev, item) => {
if (item?.line) {
prev.push(...item.line)
}
return prev
}, [])
}
const initMap = () => {
AMapLoader.load({
key: 'b59c490f61a694b9d7576dd864f74d6e', // 申請好的Web端開發者Key,首次調用 load 時必填
version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時默認為 1.4.15
plugins: ['AMap.Scale', 'AMap.ToolBar', 'AMap.MouseTool'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
Loca:{
version:'2.0.0'
}
})
.then((res) => {
AMap.value = res
// 上來就顯示的中心點 北京 116.397, 39.918
var lnglat = new res.LngLat(105, 38)
map.value = new res.Map('container', {
//設置地圖容器id
viewMode: '3D', //是否為3D地圖模式
zoom: 5, //初始化地圖級別
center: lnglat, //初始化地圖中心點位置
})
map.value.clearMap() // 清除地圖覆蓋物
// 地圖是否可拖拽和縮放
map.value.setStatus({
dragEnable: true, // 是否可拖拽
zoomEnable: true, // 是否可縮放
})
initWindow()
// 添加一些分布不均的點到地圖上,地圖上添加三個點標記,作為參照
coordData.forEach(function (marker) {
setMarker(marker)
})
let renderLine = setLine(coordData)
// 設置線
let polyline = renderLine.reduce((prev, item, index) => {
let weight = item.type === 1 ? 5 : 3
let color = item.type === 1 ? headColors[0] : headColors[1]
prev.push(setLines(item.current, color, weight))
return prev
}, [])
map.value.add([...polyline]) // 繪制線
//創建右鍵菜單
menuInstance.value = new ContextMenu(map.value)
let loca = new Loca.Container({
map:map.value,
});
window._loca = loca;
// 圖例, 圖例可以實例化多個,使用定位來設置位置
let lengend = new Loca.Legend({
loca: loca,
title: {
label: '管道類型',
fontColor: 'rgba(255,255,255,1)',
fontSize: '16px'
},
style: {
backgroundColor: 'rgba(255,255,255,0.2)',
left: '20px',
bottom: '40px',
fontSize: '12px'
},
dataMap: [
{ label: '省級管道', color: headColors[1] },
{ label: '縣級管道', color: headColors[0] },
],
});
//修改圖例排列方式
document.getElementsByClassName("amap-loca loca-controls")[0].setAttribute('id', 'testid')
var lis = document.querySelectorAll("#testid li");
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
lis[i].setAttribute("class", 'test'
);
}
})
.catch((e) => {
console.log('error', e)
})
}
onMounted(() => {
initMap()
})
</script>
<style>
#container {
width: 1350px;
height: 900px;
}
#root {
display: flex;
width: 100%;
}
#root > div:first-child {
width: 200px;
margin-right: 10px;
padding: 5px;
box-shadow: 2px 2px 2px 2px #333;
}
#root > div:first-child {
display: flex;
flex-direction: column;
}
.context_menu {
position: relative;
min-width: 12rem;
padding: 0;
background-color: white;
}
.context_menu p {
cursor: pointer;
padding: 0.25rem 1.25rem;
}
.context_menu p:hover {
background: #ccc;
}
.btn {
width: 80px;
margin-top: 10px;
}
.marker-route {
width: 15px;
height: 15px;
background-color: #22ddb8;
border-radius: 10px;
}
.active {
background-color: #f76809;
}
.content {
background-color: rgba(0, 0, 0, 0.3);
padding: 1px;
color: white;
display: flex;
align-items: center;
}
.content span {
display: block;
width: 20px;
height: 20px;
background-color: #3366bb;
margin: 0 10px;
}
.content p {
margin-right: 10px;
}
.test {
height: 30px;
box-sizing: content-box;
padding: 2px 10px;
line-height: 30px;
display: inline;
float: left;
}
.test a {
color: #333 !important;
}
.test span {
width: 80px !important;
margin-left: 10px;
border-radius: 10px;
}
.amap-info-content {
background-color: rgba(255, 255, 255, 0.6);
}
.test_container {
background-color: rgba(255, 255, 255, 0.6);
display: flex;
width: 180px;
flex-direction: column;
padding: 10px 18px 10px 10px;
line-height: 1.4;
overflow: auto;
justify-content: center;
align-items: center;
border: 1px solid rgba(0, 0, 0, 0.2);
}
.input_inner {
margin-right: 5px;
border: 1px solid #333;
border-radius: 2px;
width: 30px;
}
</style>
這里的業務邏輯還不完善, 輸入部分的交互邏輯沒有完成, 這個文件直接引入自己的項目,安裝一下上面說過的依賴, 就可以使用,不過這里數據源需要自己根據自己的數據來構造就可以了,我引入的事 data
中的一組假數據,在這里給大家兩組看一下
export const coordData = [
{
name: '黑龍江',
position: [127, 47],
pointData: {
out: 100,
provide: 10,
},
line: [
{
current: [
[127, 47],
[126, 43],
],
type: 1,
},
],
},
{
name: '吉林',
position: [126, 43],
pointData: {
out: 120,
provide: 11,
},
line: [
{
current: [
[126, 43],
[113, 41],
],
type: 1,
},
],
},
]
后面我會把業務邏輯抽離到 pinia
中, 并且完善ts
類型。
“vue3中如何使用高德地圖api”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。