阅读 138

全网稀缺,完整链家地图找房的实现(一)

前一段时间我应公司的需求开发了类似链家地图找房的功能,然而我发现现在市面上,对于链家地图找房功能的完整实现相关的文章还是比较稀缺的,亦或是功能还不够完善,出于这个方面,我觉得把自己对于链家地图找房功能的完整实现分享出来还是很有必要的,包括其中的画圈找房,以及如何将整个地图找房拆分成一个个组件。

目前项目已上线,猛戳这里体验~(仅支持pc端)

起步

1、效果预览图

2、准备工作

  • 技术栈

vue全家桶 + vue-baidu-map + BMapLib

其中 vue-baidu-map 是第三方库,已经封装好了部分组件,直接用就行;BMapLib是百度开源库

  • 组件拆分

地图容器组件: <baidu-map></baidu-map>

区域气泡组件(自定义覆盖物): <zoneOverlay></zoneOverlay>

区域边界组件: <bm-boundary></bm-boundary>

周边房源气泡组件(自定义覆盖物): <aroundOverlay></aroundOverlay>

周边房源详情覆盖物组件(自定义覆盖物): <detailOverlay></detailOverlay>

画圈找房区域气泡组件: <bm-polygon><bm-polygon>

画圈找房路径组件: <bm-polyline></bm-polyline>

画圈找房提示组件: <drawToast></drawToast>

周边房源总数提示组件: <dataToast></dataToast>

地图初始化

首先,我们要做的就是地图初始化,这里用到的是 baidu-map 组件,

<div class="map-wrapper">
    <baidu-map id="bm-view" class="bm-view" :center="center" :zoom="zoom" :scroll-wheel-zoom="true" @ready="handler"></baidu-map>
</div>
复制代码

参数说明:

center 表示地图的中心点坐标,例如{ lng: 116.404, lat: 39.915 }

zoom 表示地图的缩放等级

scroll-wheel-zoom 表示是否开启滚轮缩放

ready事件表示地图加载完成后需要的操作,例如设置地图中心点坐标 center,或者是获取 BMap、map 类等等

handler ({ BMap, map }) {
    // lng, lat 表示你要设置的经纬度
    this.$set(this.center, 'lng', lng)
    this.$set(this.center, 'lat', lat)
    console.log(BMap) // just console.log(BMap)
    console.log(map) // just console.log(map)
}
复制代码

在这里,我是先定位获取当前省份的经纬度,通过事件传递,然后设置 center

handler ({BMap, map}) {
  this.initGeo()
},
initGeo () {
  connect.$on('cityGeoOk', data => {
    this.$set(this.center, 'lng', data[0])
    this.$set(this.center, 'lat', data[1])
  })
}
复制代码

至于 vue 跨组件的通信就好比打电话一样,需要一个基站,所以新建一个js文件(这里我命名为 connect)

import Vue from 'vue'

export default new Vue()
复制代码

然后在组件中引入即可,通过 connect.$emit('event', data) 派发一个事件,然后通过 connect.$on('event', data => {}) 侦听事件。

最后,还需要设置一下地图容器 baidu-map 的缩放等级(我设置的是12,具体可自己调整)和高度,不然是看不见效果的

.bm-view{
    height: 100%; /* for example */
}
复制代码

效果如下:

区域气泡显示

在地图初始化之后,接下来就是如何将不同的区域显示在地图上了,这里我用到的是 bm-overlay 组件,并且将其二次封装成 zoneOverlay 组件。

<div class="map-wrapper">
    <baidu-map id="bm-view" class="bm-view" :center="center" :zoom="zoom" :scroll-wheel-zoom="true" @ready="handler">
        <div>
            <zone-overlay
              v-for="(item, index) in zoneGeoPoints" :key="index"
              :position="{lng: item.lng, lat: item.lat}"
              :text="item">
            </zone-overlay>
        </div>
    </baidu-map>
</div>
复制代码

zoneGeoPoints 表示区域数组,从后台接口获取,元素为对象,属性包括区域的经纬度等信息。

// zoneOverlay.vue
<template>
  <bm-overlay
    ref="customOverlay"
    class="zone"
    pane="labelPane"
    @draw="draw">
    <div>
      <p>{{text.name}}</p>
      <p>{{text.houseCnt}}套</p>
    </div>
  </bm-overlay>
</template>

<script>
export default {
  props: ['text', 'position', 'active'],
  watch: {
    position: {
      handler () {
        this.$refs.customOverlay.reload()
      },
      deep: true
    }
  },
  methods: {
    draw ({el, BMap, map}) {
      const {lng, lat} = this.position
      const pixel = map.pointToOverlayPixel(new BMap.Point(lng, lat))
      el.style.left = pixel.x - 42 + 'px'
      el.style.top = pixel.y - 42 + 'px'
    }
  }
}
</script>

<style lang="stylus" scoped>
  .zone
    transition: background-color .15s ease-in-out
    display: flex
    align-items: center
    width: 84px
    height: 84px
    background-color: rgba(58,126,255,0.9)
    overflow: hidden
    text-overflow: ellipsis
    white-space: nowrap
    color: #fff
    font-size: 12px
    text-align: center
    padding: 10px
    position: absolute
    border-radius: 50%
    box-shadow: 0 0 4px #999
    box-sizing: border-box
    &:hover
      z-index: 1
      background-color: rgba(240,65,52,.9)
      color: #fff
    div
      display: flex
      flex-wrap: wrap
      overflow: hidden
      text-overflow: ellipsis
      white-space: nowrap
      justify-content: space-between
      p
        overflow: hidden
        text-overflow: ellipsis
        white-space: nowrap
        width: 100%
        text-align: center
        line-height: 16px
</style>
复制代码

el.style.left = pixel.x - 42 + 'px'

el.style.top = pixel.y - 42 + 'px'

是为了让气泡覆盖物正中心在其坐标点的位置,因为css的宽高均为84px,效果如下:

区域气泡的交互

区域气泡显示之后,现在让我们为他们增加点交互吧~ 就是鼠标滑入某个区域气泡的时候,该气泡高亮,滑出的时候恢复,一开始我用的是mouseover和mouseleave事件,让 zone-overlay 动态绑定class,但是我发现气泡背景色的变化没有缓动效果,

<zone-overlay
  v-for="(item, index) in zoneGeoPoints" :key="index"
  :position="{lng: item.lng, lat: item.lat}"
  :text="item"
  :class="zoneIndex === index ?'active':''"
  @mouseover.native="selectZone(item, index)"
  @mouseleave.native="cancelZone">
</zone-overlay>
<script>
export default {
    data () {
        return {
            zoneIndex: ''
        }
    },
    methods: {
        selectZone (item, index) {
            this.zoneIndex = index
        },
        cancelZone () {
            this.zoneIndex = ''
        }
    }
}
</script>
复制代码

修饰符native是为了让组件变成普通html标签,不然不会触发mouseover和mouseleave事件,效果如下:

为了增加缓动效果,后来我换用了css的hover伪类,取消鼠标事件,为了让大家看的清晰点,这里我设置了1s的缓动时间,

// zoneOverlay.vue
.zone
    transition: background-color 1s ease-in-out
    &:hover
      z-index: 1
      background-color: rgba(240,65,52,.9)
      color: #fff
复制代码

效果如下:

再来点交互吧

我们希望当地图的缩放等级大于某一个值时,区域气泡消失,而小于该值时,区域气泡又出现,就像链家一样,在这个过程中我们就要实时获取地图当前的缩放等级 zoom,vue-baidu-map 给我们提供了一个api syncCenterAndZoom,具体使用如下:

<div class="map-wrapper">
    <baidu-map id="bm-view" class="bm-view" :center="center" :zoom="zoom" :scroll-wheel-zoom="true" @ready="handler" @zoomend="syncCenterAndZoom">
        <div v-if="showZone">
            <zone-overlay
              v-for="(item, index) in zoneGeoPoints" :key="index"
              :position="{lng: item.lng, lat: item.lat}"
              :text="item">
            </zone-overlay>
        </div>
    </baidu-map>
</div>
复制代码
// 双向绑定 zoom
// ZOOMBOUNDARY 为常量,表示区域气泡消失或显示的 zoom 临界值
// const ZOOMBOUNDARY = 15
syncCenterAndZoom (e) {
  this.zoom = e.target.getZoom()
  this.showZone = this.zoom < ZOOMBOUNDARY
}
复制代码

效果如下:

区域边界的显示

区域边界用到的是 bm-boundary 组件,当鼠标划入某个区域气泡时,该区域的边界出现,而当鼠标滑出某个区域气泡时,该区域的边界消失,

<bm-boundary v-if="showBoundary" :name="zoneBoundary" :strokeWeight="2" strokeColor="blue" fillColor="skyblue" :fillOpacity="0.4"></bm-boundary>
复制代码

参数说明:

name 表示区域(行政区)的名字,例如上海市黄浦区,北京市朝阳区

strokeWeight 表示区域边界的边框宽度

strokeColor 表示区域边界的边框颜色

fillColor 表示区域边界的填充颜色

fillOpacity 表示区域边界的填充颜色透明度

<template>
    <div class="map-wrapper">
        <baidu-map id="bm-view" class="bm-view" :center="center" :zoom="zoom" :scroll-wheel-zoom="true" @ready="handler" @zoomend="syncCenterAndZoom">
            <bm-boundary v-if="showBoundary" :name="zoneBoundary" :strokeWeight="2" strokeColor="blue" fillColor="skyblue" :fillOpacity="0.4"></bm-boundary>
            <div v-if="showZone">
                <zone-overlay
                  v-for="(item, index) in zoneGeoPoints" :key="index"
                  :position="{lng: item.lng, lat: item.lat}"
                  :text="item"
                  @mouseover.native="selectZone(item, index)"
                  @mouseleave.native="cancelZone">
                </zone-overlay>
            </div>
        </baidu-map>
    </div>
</template>

<script>
export default {
    data () {
        return {
            showBoundary: false
        }
    },
    methods: {
        selectZone (item, index) {
          this.zoneBoundary = `${this.posCity}${item.name}` // 行政区名字,只供参考
          this.showBoundary = true
        },
        cancelZone () {
          this.zoneBoundary = ''
          this.showBoundary = false
        }
    }
}
</script>
复制代码

效果如下:

至此,有关区域的实现和交互基本结束~

由于地图找房的功能稍微复杂,所以我将其分为几个部分,这样也能够更清楚如何一步步实现此功能,至于后续部分还请大家慢慢等待啦~

最后在贴一次项目体验地址: 猛戳我体验~(仅支持pc端)

写得不好的地方请轻喷~感谢阅读

关注下面的标签,发现更多相似文章
评论