vue 高德地圖Loca.GeoJSONSource、Loca.PolygonLayer繪制3D樓房、AMap.LabelMarker文字標(biāo)注、Loca.ScatterLayer繪制水波擴(kuò)散效果

創(chuàng)建地圖

假設(shè)已經(jīng)正確引入了高德地圖,這里使用2.0版本,注意了,1.4.x版本的使用和2.x版本的使用方式不一樣。有很多地方不兼容哦。

話(huà)說(shuō)3D效果這一塊,高德是真比不上百度地圖哦,要不是項(xiàng)目一直用的高德地圖,怕影響數(shù)據(jù),就真想換百度地圖了。百度地圖有很多地方,地級(jí)市縣都有3D效果了,但是高德沒(méi)有。高德只有省會(huì)城市有3D樓

引入高德地圖還需要加上Loca版本,plugin插件里面也要包含Map3D插件。可以參考這里 vue 使用amap-jsapi-loader加載高德地圖

然后我們創(chuàng)建地圖。注意這次我們是要繪制3D樓房,所以初始化地圖時(shí)必須指定 viewMode: '3D'

這里還加個(gè)小小的效果,旋轉(zhuǎn)地圖動(dòng)畫(huà)

友情提醒:水波效果圖,高德地圖里面叫呼吸圖,不知道是哪位大佬起的名字。話(huà)說(shuō)叫水波紋不是更形象嗎?叫呼吸圖,真的想不出來(lái)是者效果。

<template>
    <div>
        <el-switch v-model="isRotate" active-color="#FC982A" @change="rotateChange"/>
        <div class="amap" id="amap"></div>
    </div>
</template>
<script>
import reqMapList from '../../../api'
import transparentImg from '../../assets/transparent.svg'
import {mapState} from 'vuex'
import texture from "../../assets/images/breath_red.png";
    export default{
        data(){
            return{
                 center:[116.438202,40.138265],
                 isRotate: false,//是否旋轉(zhuǎn)
                 }
        },
        computed:{
            ...mapState(['lngLatArr'])
        },
          mounted() {
            if(window.AMap && window.Loca){
              this.loadMap()
              return
            }
            this.awaitAmapLoading()
          },
          methods: {
          //定時(shí)加遞歸,確保有AMap和Local實(shí)例后再加載地圖
            awaitAmapLoading(){
              let interval=setInterval(()=>{
                if(window.AMap&& window.Loca){
                  clearTimeout(interval)
                  this.loadMap()
                }
              },500)
            },
            async loadMap() { // 加載地圖 需要在掛載后
              let {center} = this
              let map = new AMap.Map('amap', {
                center,
                zooms: [2, 20],
                zoom: 16,
                viewMode: '3D',
                pitch: 50,
                rotation: 35,
              })
              let loca = new Loca.Container({
                map,
              })
              map.setRotation(395,false,4000)
              this.map = map           
              let {mapAllList} = await reqMapList ()
              this.add3DBuild(loca) //繪制3d樓函數(shù)
              this.drawRipple(loca, mapAllList) //繪制水波
              this.addLabelMarker(mapAllList)//繪制文字標(biāo)注
            },
            //旋轉(zhuǎn)切換
            rotateChange(value) {
              let {map} = this
              if (!value) {
                clearInterval(this.mapInterval)
                let rotate = map.getRotation()
                let remainderRotate = (rotate - 35) % 360
                let backRotate = rotate - remainderRotate
                map.setRotation(backRotate,false,8000*remainderRotate/360)
                return
              }
              let n=0
              map.setRotation(map.getRotation()+395,false,8000)
              let currentRotation=map.getRotation()+395
              this.mapInterval=setInterval(()=>{
                n++
                map.setRotation(currentRotation+395*n,false,8000)
              },8000)       
            },
            addLabelMarker(mapAllList){
              let icon = {
                // 圖標(biāo)類(lèi)型,現(xiàn)階段只支持 image 類(lèi)型
                type: 'image',
                // 圖片 url
                image: transparentImg,
                // 圖片尺寸
                size: [30, 30],
                // 圖片相對(duì) position 的錨點(diǎn),默認(rèn)為 bottom-center
                anchor: 'center',
              }
              let text = {
                // 文字方向,有 icon 時(shí)為圍繞文字的方向,沒(méi)有 icon 時(shí),則為相對(duì) position 的位置
                direction: 'right',
                // 在 direction 基礎(chǔ)上的偏移量
                // 文字樣式
                style: {
                  // 字體大小
                  fontSize: 12,
                  // 字體顏色
                  fillColor: '#248BFE',
                }
              }
              this.addLabelMarker(mapAllList,icon,text)
            },
        //添加3d建筑物
            add3DBuild(loca){
                let {lngLatArr}=this
                let features=lngLatArr.reduce((result,current)=>{
                    result.push({
                        "type": "Feature",
                        "geometry": {
                            "type": "Polygon",
                            "coordinates": [
                                current
                            ]
                        }
                    })
                    return result
                },[])
                loca.pointLight = {
                    color: 'rgb(100,100,100)',
                    position: [112.053413,27.778557, 200],
                    intensity: 3,
                    // 距離表示從光源到光照強(qiáng)度為 0 的位置,0 就是光不會(huì)消失。
                    distance: 50000,
                }
                let geo = new Loca.GeoJSONSource({
                    data:{
                        "type": "FeatureCollection",
                        features
                    }
                })
                let pl = new Loca.PolygonLayer({
                    zIndex: 120,
                    opacity:1,
                    shininess: 10,
                    hasSide: true,
                })
                pl.setSource(geo)
                pl.setStyle({
                    topColor:'#dddddd',
                    sideColor: '#cccccc',
                    height:300,
                    altitude: 1,
                })
                loca.add(pl)
            },
            //繪制波紋
            drawRipple(loca,mapAllList){
                let geoFeatures=mapAllList.reduce((result,current)=>{
                    let {lon,lat,address}=current
                    if(lon && lat){
                        result.push({
                            "type": "Feature",
                            "geometry": {
                                "type": "Point",
                                "coordinates": [lon, lat]
                            },
                            "properties": {
                                "name": address,
                            }
                        })
                    }
                    return result
                },[])
                let geoLevelF = new Loca.GeoJSONSource({
                    data: {
                        "type": "FeatureCollection",
                        "features":geoFeatures
                    },
                })
                let breathRed = new Loca.ScatterLayer({
                    loca,
                    zIndex: 9130,
                    opacity: 1,
                    visible: true,
                    zooms: [2, 22],
                })
                breathRed.setSource(geoLevelF)
                breathRed.setStyle({
                    unit: 'meter',
                    size: [100, 100],
                    borderWidth: 0,
                    texture,//動(dòng)畫(huà)圖片,后面會(huì)做說(shuō)明
                    duration: 500,
                    animate: true,
                })
                // 啟動(dòng)渲染動(dòng)畫(huà)
                loca.animate.start()
            },
            //添加文字標(biāo)注
            addLabelMarker(mapAllList,icon,text){
                let labelsMarkerArr=mapAllList.reduce((result,current)=>{
                    let {name,lon,lat}=current
                    if(lon && lat){
                        let labelMarker = new AMap.LabelMarker({
                            name: name, // 此屬性非繪制文字內(nèi)容,僅最為標(biāo)識(shí)使用
                            position: [lon, lat],
                            zIndex: 16,
                            // 將第一步創(chuàng)建的 icon 對(duì)象傳給 icon 屬性
                            icon,
                            // 將第二步創(chuàng)建的 text 對(duì)象傳給 text 屬性
                            text:{
                                ...text,
                                content:name
                            },
                        })
                        labelMarker.on('click', function(e){
                           console.log(e)
                        })
                        result.push(labelMarker)
                    }
                    return result
                },[])

                let labelsLayer = new AMap.LabelsLayer({
                    zooms: [3, 20],
                    zIndex: 1000000,
                    // 該層內(nèi)標(biāo)注是否避讓
                    collision: true,
                    // 設(shè)置 allowCollision:true,可以讓標(biāo)注避讓用戶(hù)的標(biāo)注
                    allowCollision: true,
                })
                // 批量添加 labelMarker
                labelsLayer.add(labelsMarkerArr)
                this.map.add(labelsLayer)
            }
          },
          beforeDestroy() {
            clearInterval(this.interval)
            clearInterval(this.mapInterval)
            this.map=null
          }
    }
</script>

這里說(shuō)下lngLatArr的數(shù)據(jù)結(jié)構(gòu),這是樓房的經(jīng)緯度,由于是自定義畫(huà)的,因此需要自己去獲取。

獲取有一點(diǎn)規(guī)則,首先要順時(shí)針獲取四個(gè)點(diǎn),然后需要畫(huà)閉環(huán)的樓房,所以第五個(gè)點(diǎn)和第一個(gè)點(diǎn)是同一個(gè)坐標(biāo)。

lngLatArr是一個(gè)三維數(shù)組。說(shuō)道這里真忍不住吐槽下高德地圖的文檔,反正我沒(méi)找到有關(guān)于數(shù)據(jù)結(jié)構(gòu)的說(shuō)明,做的時(shí)候全靠猜,靠試。

如果自己項(xiàng)目里沒(méi)有配置有逝去經(jīng)緯度的頁(yè)面,可以搜索高德地圖拾取經(jīng)緯度,去高德地圖相關(guān)頁(yè)面獲取,頁(yè)面地址 https://lbs.amap.com/tools/picker

示例結(jié)構(gòu)如下:

 blLngLatArr:Object.freeze([
        [
            [116.42095,40.138003],
            [116.445755,40.14148],
            [116.422666,40.131703],
            [116.441549,40.126584],
            [116.42095,40.138003],
        ],
        [
            [116.463093,40.144236],
            [116.476139,40.144761],
            [116.465668,40.140234],
            [116.476396,40.140168],
            [116.463093,40.144236],
        ],   
    ]),

繪制水波紋最后一定要啟動(dòng)動(dòng)畫(huà),即loca.animate.start(),奇怪的是,不加這行也有動(dòng)畫(huà),只不過(guò),這動(dòng)畫(huà)怪怪的,運(yùn)行兩下就不動(dòng)了。

texture 是動(dòng)畫(huà)圖片,這里繪制的水波紋圖效果,實(shí)際上是不斷變化圖片的位置形成的。我這里直接用了官方的示例圖片,如下!


breath_red.png

優(yōu)化

雖然這樣是可以了,但是你會(huì)發(fā)現(xiàn),切換頁(yè)面以后,再切換回來(lái),3D圖沒(méi)有了,水波圖也沒(méi)有了!!
這已經(jīng)是一個(gè)很?chē)?yán)重的bug了,但是官方問(wèn)答貌似沒(méi)有很明確的說(shuō)明。

原因是:loca實(shí)例只能創(chuàng)建一個(gè),雖然這里創(chuàng)建loca實(shí)例時(shí)只是使用了局部變量,但是高德地圖生成了全局的loca實(shí)例。當(dāng)我們切換頁(yè)面再切回來(lái)時(shí),又去創(chuàng)建一個(gè)實(shí)例,這時(shí)就有兩個(gè)了,然后就是看到的效果,圖出不來(lái)。

最終的解決方案是,當(dāng)前頁(yè)面離開(kāi)時(shí),銷(xiāo)毀loca實(shí)例。銷(xiāo)毀不是直接置為Null就可以,而是要調(diào)loca的destroy方法。

然后說(shuō)下水波紋圖,同樣是,需要移除水波紋圖層,不然會(huì)報(bào)錯(cuò):<span style="color:red;">cannot read property 'getZoom' of null</span>。

最終解決方案如下:
先將loca和breathRed掛載到this上,然后銷(xiāo)毀他們

 beforeDestroy(){
    this.loca.remove(this.breathRed)
    this.loca.destroy()
 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容