链家实现的效果
分析
地图找房功能使用点聚合来实现的。官网示例以下:https://lbs.qq.com/javascript_v2/sample/overlay-markercluster.htmljavascript
链家的地图找房主要分为三层。第一层为市区层,好比南山、罗湖等;第二层为片区,好比南头、科技园等;第三层则为小区。html
由于第一层,第二层的数据没有那么多,这两个接口都是把全部的数据一次返回给前端。可是第三层的数据量就很是的巨大了,链家采起的是返回部分数据,将前端页面上显示的最大经纬度以及最小经纬度传给后台,后台再将筛选后的数据返回给前端。(接口地址你们可使用 Chrome 的开发工具进行抓包,这里须要注意的是链家的接口采用 jsonp 的形式,因此须要抓取 JS)前端
实现
首先须要添加腾讯地图的API,这里推荐使用异步加载的方式。由于项目使用 Vue 进行开发的单页应用,有可能用户并无进入地图找房的页面,因此这里建议在打开地图找房的页面时添加腾讯地图的API。java
异步加载须要避免一个重复加载的问题,即无论用户是第几回打开地图找房,地图的 API 都是同一个。 这里为了下降代码复杂度,没有使用单例模式,具体的代码以下:git
const TXMap = { map: undefined, // 地图实例 // 异步加载获取api getApi (funName) { let script = document.createElement('script') script.type = 'text/javascript' script.src = `http://map.qq.com/api/js?v=2.exp&callback=${funName}` document.body.appendChild(script) } }
能够看到异步加载就是动态加入 script 标签,src 为腾讯地图 api 的地址,src 包含一个 callback 参数,表示 js 加载完毕后会调用 funName 这个函数。添加了地图 api 以后,window 对象会有一个 qq.maps 对象,咱们能够用来判断是否已经添加了 api,来避免重复添加 api。github
接下来就是实现自定义覆盖物这个方法了。仍是参照官方文档:https://lbs.qq.com/javascript_v2/doc/overlay.htmlweb
const TXMap = { map: undefined, overlays: [], // 存放全部覆盖物 sourceData: [], // 原始数据 listener: undefined, // 地图缩放或平移的事件监听器 getApi () {}, /* 前面已经声明,此处省略 */ // 实现自定义覆盖物 drawOverlay (options) { let _this = this // 下面有多个 window 对象的方法,避免 this 的指向问题 this.sourceData = options.data // 存放原始数据 // 绘制覆盖物以前,清理以前绘制的覆盖物 this.clearOverlays() // 若是 initMap 方法已经实现,那么咱们能够直接调用,不然须要进行定义 if (window.initMap === undefined) { window.initMap = function () {} // 绘制覆盖物的具体实现 // 地图 api 若是没有引入则调用 getApi 方法,不然直接调用 initMap () window.qq === undefined ? this.getApi('initMap') : window.initMap() } else { window.initMap() } }, // 清除自定义覆盖物 clearOverlays () { let overlay while (overlay = this.overlays.pop()) { overlay.onclick = null // 移除点击事件 overlay.parentNode.removeChild(overlay) // 移除 dom 元素 } }, // 在 Vue 组件的 beforeDestroy 调用,重置地图,移除时间为监听,避免内存泄漏 clearMap () { this.map = undefined if (this.listener) { window.qq.maps.event.removeListener(this.listener) } } }
这个地图找房的架子到此就搭得差很少了,接下来就看看绘制覆盖物的具体实现了,也就是 initMap 这个方法。json
window.initMap = function () { if (_this.map === undefined) { // 地图对象为undefined时, 须要进行地图的绘制 _this.map = new window.qq.maps.Map(document.getElementById(options.containerId), { // 初始化地图中心 center: new window.qq.maps.LatLng(options.lat || 22.702, options.lng || 114.09), // 初始化缩放级别 zoom: options.zoom || 10, // 地图最小缩放级别 minZoom: 10, // 停用缩放控件 zoomControl: false, // 停用地图类型控件 mapTypeControl: false }) // idle 事件, 地图缩放或平移以后触发该事件 _this.listener = window.qq.maps.event.addListener(_this.map, 'idle', () => { // 获取当前地图可视范围的最大最小经纬度 let bounds = _this.map.getBounds() // 获取当前地图的缩放级别 let zoom = _this.map.getZoom() // 调用 Vue 组件对 idle 事件的处理函数 options.callback && options.callback(bounds, zoom) }) } // 自定义覆盖物 if (window.CustomOverlay === undefined) { window.CustomOverlay = function (lat, lng, name, houseCount) { // 调用地图 api 计算出覆盖物的位置 this.position = new window.qq.maps.LatLng(lat, lng) this.name = name // 区域名 this.houseCount = houseCount // 房源数量 } // 继承 Overlay window.CustomOverlay.prototype = new window.qq.maps.Overlay() // 自定义覆盖物构造函数,定义覆盖为的 DOM 结构,DOM 结构,样式你们能够根据需求本身绘制 window.CustomOverlay.prototype.construct = function () { let div = this.div = document.createElement('div') div.className = 'my-overlay' // 覆盖物类名 // 覆盖物 html 结构 this.div.innerHTML = `<p class="count" >${this.houseCount}<span>套</span></p><p class="name">${this.name}</p>` //将dom添加到覆盖物层,overlayMouseTarget的顺序容器 5,此容器包含透明的鼠标相应元素,用于接收Marker的鼠标事件 this.getPanes().overlayMouseTarget.appendChild(div) // 将 div 添加到 overlays,能够用之后续处理 _this.overlays.push(div) // 定义覆盖物的点击事件 let center = this.position this.div.onclick = function () { // 点击以后对地图进行缩放以及平移 let zoom = _this.map.getZoom() if (zoom < 13) { _this.map.setCenter(center) _this.map.setZoom(13) } else if (zoom >= 13 && zoom < 15) { _this.map.setCenter(center) _this.map.setZoom(15) } } } // 实现 draw 接口来绘制 DOM 元素 window.CustomOverlay.prototype.draw = function () { let overlayProjection = this.getProjection() // 获取覆盖物容器的相对像素坐标 let pixel = overlayProjection.fromLatLngToDivPixel(this.position) let divStyle = this.div.style // 根据 DOM 元素调整定位的位置 divStyle.top = pixel.y - 53 + 'px' divStyle.left = pixel.x - 30 + 'px' } } // 根据接口数据绘制覆盖物 if (_this.sourceData.length > 0) { _this.sourceData.map(item => { let customOverlay = new window.CustomOverlay(item.latitude, item.longitude, item.name, item.house_count) customOverlay.setMap(_this.map) }) } }
至此,地图找房对绘制覆盖物方法的封装就完成了,接下来只须要将 TXMap 暴露出去,而后在 Vue 组件中进行引入,以后再向下面的方法使用便可api
TXMap.drawOverlay({ containerId: 'map-box', data: res.data })
实现效果
这个例子用了链家的数据作了两层,你们能够根据本身的须要进行修改。app
项目地址: GitHub
产品动态
腾讯位置服务已经上线3D版地图API-JavaScript API GL。
对应上文功能的3D版地图API文档连接: 点聚合、自定义覆盖物
以上内容转载自前端develop的文章《腾讯地图实现地图找房功能》
做者:前端develop
连接:https://juejin.im/post/6844903510614474759#comment
来源:掘金
著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。