最近項目須要實現地圖功能,以便於實現對房源周邊配套設施的檢索。內容以下css
其實百度官方有對應的api,可是對於一個網站來講這樣的樣式不免有些難看html
這樣的結果顯然不能知足當下的需求,因此我決定利用官方給的web服務接口加上覆蓋物實現對應的效果。前端
因爲咱們用的vue框架,因此我採用了基於百度地圖封裝的插件:vue-baidu-map 實現,對應版本號是:0.21.18,項目安裝依賴以下:vue
npm install vue-baidu-map --save
而後在main.js中注入依賴便可:web
import BaiduMap from 'vue-baidu-map' Vue.use(BaiduMap, { ak: '申請的百度祕鑰' })
這裏的ak須要去官方申請祕鑰,地址在這裏,須要注意的是,申請完成以後會給你兩個祕鑰,咱們須要給調起服務主機的ip加到白名單裏,否則會有訪問權限錯誤,因爲如今是開發環境,因此我設置的是通配符*,也就是全部的主機,可是正是環境就不推薦這麼作了,緣由是前端代碼容易泄露AK,被其餘網站非法調用。npm
打開控制檯,步驟以下:json
白名單設置好之後,就能開始調用web服務了;api
首先要注意一個問題,因爲同源策略,咱們直接調用百度官方的接口會出現跨域問題,關於什麼是跨域,這裏很少解釋。那麼前端處理跨域最理想的方案是jsonp,也就是利用script天生的優點(他能夠動態獲取src屬性),那麼vue項目不一樣於傳統html,因此這裏我用到了一個插件,也就是vue-jsonp,他是一個基於promise封裝的請求庫,使用方法也很簡單,具體以下:跨域
首先是安裝:數組
npm install vue-jsonp --save
安裝完成以後注入要vue實例中:
import jsonp from "vue-jsonp" Vue.use(jsonp)
使用起來也很方便:
this.$jsonp('url',params).then(()=> { // 成功以後的回調 }).catch(() => { // 失敗以後的回調 })
下面就正是開始搭建地圖了,首先開始寫地圖容器,按照vue-baidu-map官方的方法構建便可,注意要開啓滾輪操做,還要設置地圖展開的深度zoom
<template> <div class="map-plus"> <!-- 地圖容器 --> <baidu-map class="baidu-map" id="map" :double-click-zoom="false" :min-zoom="10" :max-zoom="18" :scroll-wheel-zoom="true" :center="center" :zoom="zoom" @ready="handler"> </baidu-map> </div> </template> <script> export default { methods: { // 地圖組件渲染完畢時觸發 handler({ BMap, map }) { this.center.lng = 118.186882019043; this.center.lat = 39.63203811645508; this.zoom = 16; } } }; </script> <style lang="scss"> .map-plus { position: relative; } #map { width: 1100px; height: 508px; margin-top: 200px; margin-left: 50px; } </style>
這裏我想主要說一下覆蓋物的封裝:
首先因爲左右是一個聯動的過程,這裏個人思路是按照索引,也就是設置一個激活的索引,若是和當前點擊的索引同樣的話實現激活效果,還有一點體驗性的問題,因爲我想點擊某個漂浮物的時候讓該漂浮物回到地圖中間,因此每次點擊我根據百度服務給的經緯度賦值給當前地圖的中心,覆蓋物的封裝以下:
<template> <bm-overlay ref="customOverlay" class="happy-layer" pane="labelPane" @draw="draw"> <div class="content" :class="{active: busActiveIndex == title+1}" @click="handleClick"> {{title+1}} </div> </bm-overlay> </template> <script> export default { props: [ // 位置 'position', // 是否激活 'active', // 內容 'title', // 激活的索引 'busActiveIndex' ], watch: { position: { handler () { this.$refs.customOverlay.reload() }, deep: true } }, methods: { handleClick () { this.$emit('change',this.title) }, draw ({el, BMap, map}) { const {lng, lat} = this.position const pixel = map.pointToOverlayPixel(new BMap.Point(lng, lat)) el.style.left = pixel.x - 60 + 'px' el.style.top = pixel.y - 20 + 'px' } } } </script> <style lang="scss"> .happy-layer { position: absolute; z-index: 99; &:hover { z-index: 999; } } .content { padding: 10px 15px; font-size: 14px; color: #fff; background-color: #00c170; cursor: pointer; border-radius: 5px; &:hover { background-color: red; } } .active { background-color: red; } </style>
調用他就比較簡單了,只要把從服務端拿到的結果遍歷一下綁定到組件上就好了
<happy-layer v-for="(item,index) in layerList" :key=index :position="{lng: item.location.lng , lat: item.location.lat}" :title=index :busActiveIndex="busActiveIndex" @change="overLayClick"></happy-layer>
關於獲取周邊配套,我用到兩個方法,一個負責接收參數調用服務,另外一個負責獲取數據
// 獲取周邊接口 getRound: function (key) { let ak = "F7XhtYsBvOZeQUbrmCCuy0KGNVCZApB8"; return this.$jsonp(`http://api.map.baidu.com/place/v2/search?query=${key}&location=39.63203811645508,118.186882019043&radius=2000&output=json&ak=${ak}`) }, // 獲取周邊信息 getMsg: function(a) { this.getRound(a).then(res => { this.layerList = [] res.results.forEach(el => el.location && this.layerList.push(el)); }); },
最後就剩下列表的搭建了,這個就不說了,直接上源碼吧:
地圖容器父組件:
<template> <div class="map-plus"> <!-- 地圖容器 --> <baidu-map class="baidu-map" id="map" :double-click-zoom="false" :min-zoom="10" :max-zoom="18" :scroll-wheel-zoom="true" :center="center" :zoom="zoom" @ready="handler"> <!-- 覆蓋物組件 --> <happy-layer v-for="(item,index) in layerList" :key=index :position="{lng: item.location.lng , lat: item.location.lat}" :title=index :busActiveIndex="busActiveIndex" @change="overLayClick"></happy-layer> </baidu-map> <!-- 右側搜索 --> <div class="search-wrap"> <!-- 標籤 --> <div class="tags"> <div class="tag_item" :class="{'tag-active': activeIndex == index}" v-for="(item,index) in tagsArr" :key="index" @click="tagsClick(index,item)">{{item.name}}</div> </div> <!-- 列表 --> <div class="list"> <div class="list-top" v-for="(bus,indexs) in layerList" :key="indexs" @click="chooseListItem(bus,indexs)"> <div class="title" :class="{'active-bus': busActiveIndex == indexs+1}"> <span class="indexs">{{indexs+1}}</span> <span>{{bus.name}}</span> </div> <div class="bus-num"> {{bus.address}} </div> </div> </div> </div> </div> </template> <script> export default { data() { return { active: false, // 左邊中心 center: { lng: 0, lat: 0 }, // 深度 zoom: 3, // 標籤激活索引 activeIndex: 0, // 漂浮物激活索引 busActiveIndex:1, // 數據結果 layerList: [], // 標籤數組 tagsArr: [ { name: "公交" }, { name: "教育" }, { name: "醫療" }, { name: "購物" }, { name: "生活" }, { name: "娛樂" } ] }; }, mounted() { this.getMsg('公交'); }, methods: { // 地圖組件渲染完畢時觸發 handler({ BMap, map }) { this.center.lng = 118.186882019043; this.center.lat = 39.63203811645508; this.zoom = 16; }, // 標籤激活 tagsClick: function(index, item) { this.activeIndex = index this.getMsg(item.name) }, // 獲取周邊接口 getRound: function (key) { let ak = 你申請的祕鑰; return this.$jsonp(`http://api.map.baidu.com/place/v2/search?query=${key}&location=39.63203811645508,118.186882019043&radius=2000&output=json&ak=${ak}`) }, // 獲取周邊信息 getMsg: function(a) { this.getRound(a).then(res => { this.layerList = [] res.results.forEach(el => el.location && this.layerList.push(el)); }); }, // 覆蓋物點擊激活 overLayClick: function (num) { this.busActiveIndex = num + 1 }, // 選擇列表某一項 chooseListItem: function (bus,indexs) { this.busActiveIndex = indexs+1 this.center = bus.location } } }; </script> <style lang="scss"> .map-plus { position: relative; } #map { width: 1100px; height: 508px; margin-top: 200px; margin-left: 50px; } .search-wrap { width: 393px; height: 460px; border: 1px solid #eee; background-color: #fff; position: absolute; left: 736px; top: 220px; box-shadow: 0 0 10px #ccc; border-top: 3px solid #1fb19e; .list { height: 400px; overflow-y: scroll; .list-top { color: #333; padding: 20px 20px 10px 20px; cursor: pointer; .indexs { padding: 2px 6px; font-size: 12px; border-radius: 50%; background-color: #00c170; color: #fff; } &:hover { color: #00c170; } .bus-num { font-size: 14px; margin: 5px 0 0 24px; color: #9c9fa1; line-height: 18px; } } } .active-bus { color: #00c170; } .tags { line-height: 45px; height: 45px; background-color: #f1f1f1; display: flex; justify-content: space-between; .tag_item { width: 66px; text-align: center; cursor: pointer; } .tag-active { background-color: #fff; } } } </style>
覆蓋物子組件:
<template> <bm-overlay ref="customOverlay" class="happy-layer" pane="labelPane" @draw="draw"> <div class="content" :class="{active: busActiveIndex == title+1}" @click="handleClick"> {{title+1}} </div> </bm-overlay> </template> <script> export default { props: [ // 位置 'position', // 是否激活 'active', // 內容 'title', // 激活的索引 'busActiveIndex' ], watch: { position: { handler () { this.$refs.customOverlay.reload() }, deep: true } }, methods: { handleClick () { this.$emit('change',this.title) }, draw ({el, BMap, map}) { const {lng, lat} = this.position const pixel = map.pointToOverlayPixel(new BMap.Point(lng, lat)) el.style.left = pixel.x - 60 + 'px' el.style.top = pixel.y - 20 + 'px' } } } </script> <style lang="scss"> .happy-layer { position: absolute; z-index: 99; &:hover { z-index: 999; } } .content { padding: 10px 15px; font-size: 14px; color: #fff; background-color: #00c170; cursor: pointer; border-radius: 5px; &:hover { background-color: red; } } .active { background-color: red; } </style>