0 需求
依次点击区-镇-小区,展示当前位置的数据列表 ;移动地图(movestart事件),隐藏详情。
1 准备工作?
1.1?引入百度地图并配置ak
jspopularGL | 百度地图API SDK
1.2??根据IP定位获取当前定位
import axios from 'axios';
export const getCurrentCity = () => {
const localCity=JSON.parse(localStorage.getItem('hkzf_city'))
if (!localCity) {//localStorage 中是否有定位城市,没有,保存并返回
return new Promise((resolve, reject) => {
const curCity = new window.BMapGL.LocalCity();
curCity.get(async res => {
try {//成功
const result = await axios.get(`http://localhost:8080/area/info?name=${res.name}`)
localStorage.setItem('hkzf_city', JSON.stringify(result.body))
resolve(res.body)//返回结果为promise:如果直接return,外层函数无法接收到
} catch(e) {//失败
reject(e)
}
})
})
}
return Promise.resolve(localCity)//有,直接返回:此处的 Promise 不会失败
}
2 代码实现
import React from 'react'
import styles from './index.module.scss'
import NavHeader from '../../components/NavHeader'
import axios from 'axios'
import { Link } from 'react-router-dom'
import { Toast } from 'antd-mobile'
export default class Map extends React.Component {
// 覆盖物样式
labelStyle = {
cursor: 'pointer',
border: '0px solid rgb(255, 0, 0)',
padding: '0px',
whiteSpace: 'nowrap',
fontSize: '12px',
color: 'rgb(255, 255, 255)',
textAlign: 'center'
}
state = {
housesList: [],
isShowList: false
}
componentDidMount () {
this.initMap();
}
initMap () {
// 1初始化地图实例
const { label, value } = JSON.parse(localStorage.getItem('hkzf_city'));//当前城市name,id
var map = new window.BMapGL.Map("container");//在 react 脚手架中全局对象需要使用 window 来访问,否则,会造成 ESLint 校验错误
this.map = map;
// 2将地址转换为坐标
var myGeo = new window.BMapGL.Geocoder(); //创建地址解析器实例
myGeo.getPoint(label, (point) => {// 将地址解析结果显示在地图上,并调整地图视野
if (point) {
// 2.1设置中心点
map.centerAndZoom(point, 11);
// 2.2添加地图控件
map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
var scaleCtrl = new window.BMapGL.ScaleControl();// 添加比例尺控件
map.addControl(scaleCtrl);
var zoomCtrl = new window.BMapGL.ZoomControl();// 添加缩放控件
map.addControl(zoomCtrl);
// 2.3渲染覆盖物
this.renderOverlays(value)
} else {
alert('您选择的地址没有解析到结果!');
}
}, label)
// 给地图绑定移动事件
map.addEventListener('movestart', () => {
console.log('movestart')
if (this.state.isShowList) {
this.setState({
isShowList: false
})
}
})
}
// 渲染覆盖物
async renderOverlays (id) {
try {
Toast.show({
icon: 'loading',
content: '加载中…',
duration: 0,
})
// 获取数据
const res = await axios.get(
`http://localhost:8080/area/map?id=${id}`
)
Toast.clear()
const data = res.data.body
// 获取类型及缩放级别
const { nextZoom, type } = this.getTypeAndZoom()
// 渲染数据
this.map.clearOverlays();
data.forEach(item => {
this.createOverlays(item, nextZoom, type)
})
} catch (e) {
Toast.clear()
}
}
// 计算要绘制的覆盖物类型和下一个缩放级别
getTypeAndZoom () {
const zoom = this.map.getZoom();
let nextZoom, type
if (zoom >= 10 && zoom < 12) {// 区
nextZoom = 13
type = 'circle'
} else if (zoom >= 12 && zoom < 14) {// 镇
nextZoom = 15
type = 'circle'
} else if (zoom >= 14 && zoom < 16) {// 小区
type = 'rect'
}
return { nextZoom, type }
}
// 创建覆盖物
createOverlays (item, nextZoom, type) {
const {
coord: { longitude, latitude },
label: areaName,
count, value
} = item;
// 将经纬度转化为坐标
var pointArea = new window.BMapGL.Point(longitude, latitude);
if (type === 'circle') {
this.createCircle(pointArea, areaName, count, value, nextZoom)
} else {
this.createRect(pointArea, areaName, count, value)
}
}
// 创建区、镇覆盖物
createCircle (pointArea, areaName, count, value, nextZoom) {
// 1创建文本标注覆盖物实例
var content = "文本覆盖物";
var labelTXT = new window.BMapGL.Label(content, {
position: pointArea,// 设置标注的地理位置
offset: new window.BMapGL.Size(-35, -35)// 设置标注的偏移量
})
// 2设置结构
labelTXT.setContent(`
<div class="${styles.bubble}">
<p class="${styles.name}">${areaName}</p>
<p>${count}</p>
</div>
`)
// 3设置样式
labelTXT.setStyle(this.labelStyle)
this.map.addOverlay(labelTXT);// 将标注添加到地图中
// 4监听标注事件
labelTXT.addEventListener('click', (e) => {
this.renderOverlays(value)
this.map.centerAndZoom(pointArea, nextZoom);
});
}
// 创建小区覆盖物
createRect (pointArea, areaName, count, value) {
var content = "";
var labelTXT = new window.BMapGL.Label(content, {
position: pointArea,
offset: new window.BMapGL.Size(-50, -28)
})
labelTXT.setContent(`
<div class="${styles.rect}">
<span class="${styles.housename}">${areaName}</span>
<span class="${styles.housenum}">${count}套</span>
<i class="${styles.arrow}"></i>
</div>
`)
labelTXT.setStyle(this.labelStyle)
this.map.addOverlay(labelTXT);
labelTXT.addEventListener('click', (e) => {
// 获取小区房源数据
this.getHouseList(value);
const { x, y, width, height } = e.target.domElement.getBoundingClientRect()
console.log(x, y, width, height)
// 将被点击的房源移动到中心位置
this.map.panBy(
window.innerWidth / 2 - x,
(window.innerHeight - 330) / 2 - y
)
});
}
// 获取小区房源数据
async getHouseList (id) {
try {
Toast.show({
icon: 'loading',
content: '加载中…',
duration: 0,
})
const res = await axios.get(`http://localhost:8080/houses?cityId=${id}`)
Toast.clear()
this.setState({
housesList: res.data.body.list,
isShowList: true
})
} catch (e) {
Toast.clear()
}
}
// 渲染小区房源数据
renderHousesList () {
return this.state.housesList.map(item => (
<div className={styles.house} key={item.houseCode}>
<div className={styles.imgWrap}>
<img
className={styles.img}
src={`http://localhost:8080${item.houseImg}`}
alt=""
/>
</div>
<div className={styles.content}>
<h3 className={styles.title}>{item.title}</h3>
<div className={styles.desc}>{item.desc}</div>
<div>
{item.tags.map(tag => (
<span
className={[styles.tag, styles.tag1].join(' ')}
key={tag}
>
{tag}
</span>
))}
</div>
<div className={styles.price}>
<span className={styles.priceNum}>{item.price}</span> 元/月
</div>
</div>
</div>
))
}
render () {
return (
<div className={styles.map}>
<NavHeader>地图找房</NavHeader>
<div id="container" className={styles.container}></div>
{/* 房源列表 */}
<div
className={[
styles.houseList,
this.state.isShowList ? styles.show : ''
].join(' ')}
>
<div className={styles.titleWrap}>
<h1 className={styles.listTitle}>房屋列表</h1>
<Link className={styles.titleMore} to="/home/list">
更多房源
</Link>
</div>
<div className={styles.houseItems}>
{/* 房屋结构 */}
{this.renderHousesList()}
</div>
</div>
</div>
)
}
}
3?代码分析
3.1?封装流程
3.2?代码逻辑?
初始化渲染:创建map实例,以当前定位为中心点渲染地图(使用IP定位),并添加控件;
渲染覆盖物:
? ? ? ? 获取数据:接口需返回的数据信息包括:位置id,位置name,当前位置的经纬度;
? ? ? ? 渲染数据:创建文本标注实例,设置结构和样式后添加到地图上。
点击覆盖物:
? ? ? ? 圆形:放大地图;渲染下一级数据。
? ? ? ? 矩形:移动地图;渲染列表数据。
? ? ? ? 注意:事件对象中e的clientX=undefined,需要借助getBoundingClientRect()将数据移动到中心点位置。
3.3?中心点移动分析
|