Недавно я получил запрос о том, что мне нужно использовать карту Goose Factory, чтобы реализовать функцию поиска по картам, аналогичную Lianjia.com, а затем я отправился в Интернет, чтобы найти ее, и в основном я использовал карты Baidu. Поэтому я планирую немного инкапсулировать его самостоятельно, что может быть удобнее при использовании.
01. Анализ
Когда я получил это требование, я тоже растерялся и не знал, как начать, я посмотрел в Интернете и сказал, что это достигается с помощью агрегации точек. Пример, приведенный на официальном сайте, выглядит следующим образом.Не могу подключиться .qq.com/javascript_...
Кажется, это та функция, которую я хочу реализовать.Попытался написать и обнаружил, что этот стиль трудно изменить, и все данные нужно запрашивать за один раз.Если объем данных очень большой, запрос займет много времени. , недостаточно удобен для пользователя, поэтому этот метод не продолжается.
Позже я сам открыл официальный сайт Ляньцзя, взял данные Ляньцзя и изучил распорядок дня Ляньцзя. Поиск карты Lianjia в основном разделен на три уровня. Первый этаж — это городской район, например, Наньшань, Луоху и т. д., второй этаж — район, например, Наньтоу, Научно-технологический парк и т. д., третий этаж — сообщество.
Поскольку на первом и втором уровнях не так много данных, эти два интерфейса одновременно возвращают все данные во внешний интерфейс. Однако объем данных в третьем слое очень огромен, Lianjia использует для возврата некоторых данных, передачи максимальной широты и долготы, отображаемых на странице внешнего интерфейса, и минимальной широты и долготы на серверную часть, а затем на серверную часть. возвращает отфильтрованные данные во внешний интерфейс. (Вы можете использовать инструменты разработки Chrome для захвата адреса интерфейса. Здесь следует отметить, что интерфейс Lianjia имеет форму jsonp, поэтому вам нужно захватить JS)
PS: на платформе Windows вы можете нажать F12, чтобы открыть инструменты разработчика, а на платформе Mac — Command + Option + I.
02. Реализация
Теоретический анализ закончен, на очереди проблема реализации. Глядя на API Tencent Map, я думаю, что он подходит для этого требования только с настраиваемым покрытием. Поскольку пользовательское покрытие является более гибким, мы можем рисовать нужный нам стиль, как при написании HTML.
Во-первых, вам нужно добавить API Tencent Maps, Здесь я рекомендую использовать метод асинхронной загрузки. Поскольку проект использует Vue для разработки одностраничного приложения, возможно, что пользователь не заходит на страницу поиска комнаты на карте, поэтому рекомендуется добавить API Tencent Maps при открытии страницы поиска комнаты. комната на карте.
Асинхронная загрузка должна избежать проблемы повторной загрузки, то есть независимо от того, сколько раз пользователь открывает карту, чтобы найти комнату, API карты остается тем же. Для уменьшения сложности кода здесь не используется шаблон singleton, а конкретный код выглядит следующим образом:
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)
}
}
Вы можете видеть, что асинхронная загрузка предназначена для динамического добавления тегов сценария, src — это адрес API Tencent Maps, а src содержит параметр обратного вызова, указывающий, что функция funName будет вызываться после загрузки js. После добавления API карты объект окна будет иметь объект qq.maps, который мы можем использовать, чтобы определить, был ли добавлен API, чтобы избежать повторного добавления API.
Следующим шагом является реализация пользовательского метода наложения. Или обратитесь к официальной документации (Не удается подключиться.QQ.com/JavaScript_…
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.
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, после чего его можно будет использовать ниже.
TXMap.drawOverlay({
containerId: 'map-box',
data: res.data
})
03. Осознайте эффект
В этом примере я использовал данные Lianjia для создания двух слоев, вы можете изменить их в соответствии со своими потребностями.
адрес проекта вGitHub
Если статья была вам полезна, то нажмите ❤
Из-за моего ограниченного уровня, если есть какие-либо ошибки, пожалуйста, поправьте меня. Если вы обнаружите какие-либо ошибки или проблемы, которые не упоминаются во время работы, оставьте сообщение в комментариях, обсудите вместе, учитесь и развивайтесь вместе!Статья будет опубликована на моем официальном аккаунте в ближайшее время, если вам интересно, то обратите внимание на мой официальный аккаунт - front-end development