溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue3中如何使用高德地圖api

發(fā)布時間:2023-03-10 09:04:34 來源:億速云 閱讀:248 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“vue3中如何使用高德地圖api”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

前置工作

在開發(fā)之前我們需要了解 vue3 中接入高德地圖的幾個步驟

  • 首先安裝包并引入

npm i @amap/amap-jsapi-loader --save
import AMapLoader from '@amap/amap-jsapi-loader'

  • 使用官方介紹的方式進(jìn)行加

vue3中如何使用高德地圖apivue2vue3 是有區(qū)別的,這里我們使用的是 vue3 ,但這里 vue3 的方式還是選項式,不是組合式的,我自己寫的時候使用的是組合式的,且集成了 ts, 我后面發(fā)布完整 .vue 文件的時候 會去掉標(biāo)簽上的 ts,因為類型還沒有完善,等后面完善了再貼更改以后得。為什么要使用 shallowRef 官方也給出了說明原因。

示例模塊

這里我直接把我前面,寫過的 地圖業(yè)務(wù)需求的業(yè)務(wù)邏輯拿過來的,沒有使用框架,直接在一個 html 文件當(dāng)中引入,鏈接大家可以點擊下面進(jìn)行查看:
高德地圖jsApi的使用
高德地圖jsApi的點和線配置
高德地圖jsApi的右鍵設(shè)置
高德地圖jsApi的點位新增
高德地圖jsApi的圖例
使用vue3 的時候,實例化的方式, this 的問題, 以及插入字符串模板的時候 事件響應(yīng)的方式都需要更改,還是很麻煩的

模塊的引入

  • 首先導(dǎo)入的方式,和官網(wǎng)一樣,后面我會貼完整代碼, 這里我們使用 plugins 加載插件, 其他配置如 Loca, 直接進(jìn)行配置, 這里需要注意版本問題, 寫成  ‘2.0’ 是不行的,初始化函數(shù)在 onmounted 生命周期中執(zhí)行。

  • AMap存儲 這里我做了很多存儲,大家知道 .value  的語法是 vue3 獲取 ref 的語法,我下面使用到的 都是ref,后面完整代碼可以查看, 這里掛載的時候直接存一下,因為很多工具方法都會只用到他,這里后期業(yè)務(wù)邏輯我會抽離到 pinia中去,所以不需要在初始化函數(shù)中寫全部的業(yè)務(wù)邏輯。

  • 模版樣式不生效問題, 我們在使用的時候, 就像我之前寫的文章,點位新增的時候,我們會插入 content 字符串模版,替換點樣式,這里有兩種方案修改樣式,一種是 插入 DOM ,不使用字符串,然后在 DOM 上通過 style 直接修改樣式,另一種就是使用模版的時候直接給 class 類名,但是這種樣式如果我們給 vuestyle 加了 scoped 就不會生效,這里大家可以自己靈活選擇用哪種,我這里暫時先使用模版的方式,去掉了 scoped

  • 圖例, 圖例這里除了導(dǎo)入的時候,需要配置一下,使用上來說變化不大,樣式的修改還是復(fù)用了我之前的邏輯。

import AMapLoader from '@amap/amap-jsapi-loader'

const initMap = () => {
  AMapLoader.load({
    key: 'b59c490f61a694b9d7576dd864f74d6e', // 申請好的Web端開發(fā)者Key,首次調(diào)用 load 時必填
    version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時默認(rèn)為 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', {
        //設(shè)置地圖容器id
        viewMode: '3D', //是否為3D地圖模式
        zoom: 5, //初始化地圖級別
        center: lnglat, //初始化地圖中心點位置
      })
      map.value.clearMap() // 清除地圖覆蓋物
      // 地圖是否可拖拽和縮放
      map.value.setStatus({
        dragEnable: true, // 是否可拖拽
        zoomEnable: true, // 是否可縮放
      })

      initWindow()
      // 添加一些分布不均的點到地圖上,地圖上添加三個點標(biāo)記,作為參照
      coordData.forEach(function (marker) {
        setMarker(marker)
      })

      let renderLine = setLine(coordData)
      // 設(shè)置線
      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]) // 繪制線
      //創(chuàng)建右鍵菜單
      menuInstance.value = new ContextMenu(map.value)
      let loca = new Loca.Container({
          map:map.value,
      });
      window._loca = loca;

      // 圖例, 圖例可以實例化多個,使用定位來設(shè)置位置
      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()
})

右鍵菜單

右鍵菜單, 右鍵菜單這里官方給我們的示例是使用一個 函數(shù) 進(jìn)行實例化,里面使用了 this, 所以這個我單獨拿出來,首先我們看一下官方的 demo

vue3中如何使用高德地圖api

  • 這里使用了一個函數(shù),但這個函數(shù)還不是類,但是他卻在里面使用了this,實話來講,這種寫法確實不是很優(yōu)秀,可擴展性很差,不夠健壯,但沒辦法,誰讓我們用了人家的東西呢是吧, 在 vue3 中這么用就不可以了,首先 vue3 里面使用 this 就不是官方建議的, 另外這里面還修改了函數(shù)原型上的方法,其實我得代碼里面一共有兩種右鍵菜單,如下:

vue3中如何使用高德地圖api

vue3中如何使用高德地圖api
一種是在指定點位上打開,另一種是在非點位的空白處打開,指定點位處打開的其實叫信息窗體,只不過是通過右鍵的方式觸發(fā),那個沒有上面這個右鍵菜單麻煩。

  • 首先來說 this 問題, 這里的 this 實際上就是把我們的實例化對象掛載到上面而已,vue3 中沒辦法像 vue2 那樣使用 this, 但也提供給我們了 api 來獲取當(dāng)前組件的實例化對象, 然后我沒用使用函數(shù), 使用了一個類,類構(gòu)造這個方法, 模版也不適用字符串模版,因為這里字符串模版的事件綁定寫死了,我們使用 DOM 來動態(tài)綁定事件,代碼如下:

const { ctx } = getCurrentInstance()
const _this = ctx
//自定義菜單類
class ContextMenu {
  constructor(map) {
    var me = _this
    //地圖中添加鼠標(biāo)工具M(jìn)ouseTool插件
    _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自定義右鍵菜單內(nèi)容
    _this.contextMenu = new AMap.value.ContextMenu({
      isCustom: true,
      content: fragment,
    })
    //地圖綁定鼠標(biāo)右擊事件——彈出右鍵菜單
    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>添加選點請輸入坐標(biāo)</h4>
      <label>
        經(jīng)度:
        <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) // 存儲當(dāng)前選中點 DOM
let currentData = reactive({}) // 當(dāng)前選重點信息
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 = {}
}
// 設(shè)置線信息
function setLines(lnglat, color, weight) {
  return new AMap.value.Polyline({
    path: lnglat,
    // showDir:true ,// 設(shè)置線方向
    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()
    })
  })
}

// 抽離點位信息設(shè)置
function setMarker(marker) {
  //創(chuàng)建右鍵菜單
  var contextMenu = new AMap.value.ContextMenu()
  //右鍵放大
  contextMenu.addItem(
    '放大一級',
    function () {
      map.value.zoomIn()
    },
    0,
  )
  //右鍵縮小
  contextMenu.addItem(
    '縮小一級',
    function () {
      map.value.zoomOut()
    },
    1,
  )
  contextMenu.addItem('設(shè)置起點', function () {
    console.log('設(shè)置起點', marker, markerd.dom)
    changeStyle(markerd.dom, marker)
    contextMenu.close() // 關(guān)閉右鍵菜單
  })
  contextMenu.addItem('與起點連線', function () {
    if (!currentPonit) {
      alert('請選擇起點')
      contextMenu.close()
      return
    } else {
      // 這里其實可以根據(jù)數(shù)據(jù)判定線類型了,因為第二個選中點的信息+和第一個選中點的信息都有了,但是過濾方法會比較復(fù)雜
      let path = [currentData.position, marker.position]
      const polyline1 = setLines(path, '#3366bb', 5)
      map.value.add([polyline1])
      clearPoint()
    }
    contextMenu.close() // 關(guān)閉右鍵菜單
  })
  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, // 點標(biāo)記是否可見
    position: [marker.position[0], marker.position[1]],
  })

  let inputO = window.localStorage.getItem(marker.name + 'inputOut')
  let inputP = window.localStorage.getItem(marker.name + 'inputPro')
  // 左鍵點擊的信息窗體, 寬度會在碰觸到容器邊緣的時候自適應(yīng)的縮小
  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>位置:經(jīng)度${marker.position[0]},緯度${marker.position[1]}</div>`
  markerd.data = marker
  markerd.on('click', markerClick)
  if (marker.name === '新疆') {
    // 觸發(fā)上面的點擊事件
    markerd.emit('click', { target: markerd })
  }
  //綁定鼠標(biāo)右擊事件——彈出右鍵菜單
  markerd.on('rightclick', function (e) {
    contextMenu.open(map.value, e.lnglat)
  })
  return markerd
}

//自定義菜單類
class ContextMenu {
  constructor(map) {
    var me = _this
    //地圖中添加鼠標(biāo)工具M(jìn)ouseTool插件
    _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自定義右鍵菜單內(nèi)容
    _this.contextMenu = new AMap.value.ContextMenu({
      isCustom: true,
      content: fragment,
    })
    //地圖綁定鼠標(biāo)右擊事件——彈出右鍵菜單
    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端開發(fā)者Key,首次調(diào)用 load 時必填
    version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時默認(rèn)為 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', {
        //設(shè)置地圖容器id
        viewMode: '3D', //是否為3D地圖模式
        zoom: 5, //初始化地圖級別
        center: lnglat, //初始化地圖中心點位置
      })
      map.value.clearMap() // 清除地圖覆蓋物
      // 地圖是否可拖拽和縮放
      map.value.setStatus({
        dragEnable: true, // 是否可拖拽
        zoomEnable: true, // 是否可縮放
      })

      initWindow()
      // 添加一些分布不均的點到地圖上,地圖上添加三個點標(biāo)記,作為參照
      coordData.forEach(function (marker) {
        setMarker(marker)
      })

      let renderLine = setLine(coordData)
      // 設(shè)置線
      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]) // 繪制線
      //創(chuàng)建右鍵菜單
      menuInstance.value = new ContextMenu(map.value)
      let loca = new Loca.Container({
          map:map.value,
      });
      window._loca = loca;

      // 圖例, 圖例可以實例化多個,使用定位來設(shè)置位置
      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>

  • 這里的業(yè)務(wù)邏輯還不完善, 輸入部分的交互邏輯沒有完成, 這個文件直接引入自己的項目,安裝一下上面說過的依賴, 就可以使用,不過這里數(shù)據(jù)源需要自己根據(jù)自己的數(shù)據(jù)來構(gòu)造就可以了,我引入的事 data 中的一組假數(shù)據(jù),在這里給大家兩組看一下

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,
      },
    ],
  },
 ]

  • 后面我會把業(yè)務(wù)邏輯抽離到 pinia 中, 并且完善ts類型。

“vue3中如何使用高德地圖api”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI