在react脚手架中实现应用百度地图

391 阅读4分钟

首先你要去百度地图开放平台注册并获取一个appkey(AK码)百度地图开放平台
找到控制台/应用管理/我的应用 屏幕截图 2022-08-11 090147.png 创建ak秘钥成功后copy一下

屏幕截图 2022-08-11 090726.png 最后在我们脚手架中public目录/index.html文件中引入链接,并注入AK码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=此处粘贴你刚刚copy的AK码"></script>
  </body>
</html>

这样准备工作就完成了,接下来的活就是在组件中完成了

import React, { Component } from 'react'
import { NavBar, Toast, Popup } from 'antd-mobile' //从组件库中引入
import axios from 'axios'
class Index extends Component {
    state = { //弹框 和弹框内数据
        visible: false,
        maskData: []
    }
    mapContainer = React.createRef() //创建节点容器
    map = null //全局挂载变量
    cirStyle = {// 圆形覆盖物的样式
        color: 'white',
        borderRadius: '50%',
        borderColor: 'white',
        backgroundColor: '#22BA77',
        fontSize: '16px',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        width: '85px',
        height: '85px',
        fontFamily: '微软雅黑',
        zIndex: 9999
    }
    rectStyle = { // 矩形覆盖物的样式
        color: white;
        borderColor: white;
        background: #22BA77;
        fontSize: 14px;
        display: flex;
        justifyContent: space-around;
        alignItems: center;
        width: 150px;
        height: 40px;
        fontFamily: '微软雅黑';
        zIndex: 9999;
    }

    isCurCity() {//判断本地存储是否有城市
        const city = JSON.parse(localStorage.getItem('hyzf_city') as any) || ''
        if (city) { //如果有的话直接异步返回城市
            return Promise.resolve(city)
        } else { //如果本地没有城市
            return new Promise((resolve) => { //需要借助百度地图提供的API实现本地定位
                const myCurCity = new window['BMap'].LocalCity() //定位
                myCurCity.get(async (result) => {
                    const res = await axios.get('https://api-haoke-web.itheima.net/area/info', {
                        params: { //调接口传参 (这是线上接口)
                            name: result.name
                        }
                    })
                    const { label, value } = res.data.body //返回城市名,及城市ID
                    localStorage.setItem('hyzf_city', JSON.stringify({ label, value })) //存本地
                    resolve({ label, value }) //成功抛出
                })
            })
        }
    }

    async initMap() {//初始化地图
        const { label, value } = await this.isCurCity() //调用方法返回城市名和城市ID
        this.map = new window['BMap'].Map(this.mapContainer.current) //创建地图实例,作用于mapContainer容器
        const myGeo = new window['BMap'].Geocoder() //创建地址解析器实例
        myGeo.getPoint(label, async (point) => { //第一个参数详细地址、第二个参数回调、第三个参数城市
            this.map.centerAndZoom(point, 12) //创建中心点、视图展示层级
            this.map.addControl(new window['BMap'].NavigationControl()); //添加导航空间
            this.map.addControl(new window['BMap'].ScaleControl()); //添加比例标尺空间
            this.renderOverlays(value) //调用渲染覆盖物方法、传入城市ID 
        }, label)
    }

    async renderOverlays(id) {//渲染覆盖物
        Toast.show({ //借用组件库实现加载loading功能 (蒙层)
            icon: 'loading',
            content: '加载中...',
        })
        const res = await axios.get('https://api-haoke-web.itheima.net/area/map', { params: { id } })
        //调用线上接口,传入参数城市ID
        Toast.clear()//接口响应完毕、关闭loading蒙层
        const { type, nextLevel } = this.getTypeAndZoom() //调用方法获取类型及地图展示层级 (1-19)
        res.data.body.forEach(item => { //将拿到的数据遍历 循环调用创建覆盖物的方法,传入类型(圆形/矩形)、地图展示层级、数据的每一项
            this.createOverlays(type, nextLevel, item)
        })
    }

    getTypeAndZoom() {//获取类型及地图展示层级
        const curZoom = this.map.getZoom() //获取当前地图缩放层级
        let type, nextLevel;//声明两变量 类型,层级
        if (curZoom >= 10 && curZoom <= 12) { //根据获取的层级更改应用到实际视图的类型/层级
            type = 'circle' //圆形
            nextLevel = 13
        } else if (curZoom >= 12 && curZoom < 14) {
            type = 'circle'
            nextLevel = 16
        } else {
            type = 'rect' //矩形
            nextLevel = 19
        }
        return { type, nextLevel }//抛出
    }

    createOverlays(type, nextLevel, item) {//创建覆盖物 (类型,层级,数据)
        const { label, value, coord: { latitude, longitude }, count } = item //解构传递过来的每一项数据
        var point = new window['BMap'].Point(longitude, latitude); //将经纬度转换为地理位置
        if (type === 'rect') { //如果类型是矩形的话就调用创建矩形覆盖物的方法
            this.createRect(point, label, count, value)
        } else { //否则调用创建圆形覆盖物的方法
            this.createCircle(point, label, count, value, nextLevel)
        }
    }

    createRect(point, areaName, count, id) {//创建矩形覆盖物方法 (最底层的覆盖物)
        const that = this //标记全局this
        const opts = { //创建含有位置/偏移中心点位置的对象
            position: point,
            offset: new window['BMap'].Size(-50, -28)
        }
        const label = new window['BMap'].Label('', opts) //在定位到的地图位置上添加盒子覆盖物
        label.setContent(`
        <div class='rect-container'>
            <p  class='area'>${areaName}</p>
            <p>${count} 套</p>
        </div>
        `)
        //更改内容为覆盖物节点 添加动态内容
        label.setStyle(this.rectStyle)
        //更改覆盖物样式
        label.addEventListener('click', (e) => { //给渲染出来的每一个覆盖物绑定点击事件
            const { clientX, clientY } = e.changedTouches[0]; //获取节点距离视口左侧距离、顶部距离
            const x = (window.innerWidth) / 2 - clientX; //定义坐标变量
            const y = (window.innerHeight) / 2 - clientY;
            that.map.panBy(x, y); //位移
            that.getHouseDetail(id) //获取最底层的房源数据方法
        })
        this.map.addOverlay(label) //最后在地图中添加覆盖物 传入label
    }

    createCircle(point, areaName, count, id, level) {//创建圆形覆盖物方法(非最底层覆盖物)
        const that = this // 标记全局this
        const opts = {  //创建含有位置/偏移中心点位置的对象
            position: point,
            offset: new window['BMap'].Size(-35, -35)
        }
        const label = new window['BMap'].Label('', opts)//在定位到的地图位置上添加盒子覆盖物
        label.setContent(`
        <div>
            <p>${areaName}</p>
            <p>${count} 套</p>
        </div>
        `)
        //更改内容为覆盖物节点 添加动态内容
        label.setStyle(this.cirStyle)
        //更改覆盖物样式
        label.addEventListener('click', () => {//给渲染出来的每一个覆盖物绑定点击事件
            that.renderOverlays(id) //点击圆形覆盖物时调用渲染覆盖物方法传入城市ID
            that.map.centerAndZoom(point, level) //设置地图中心及缩放层级
            setTimeout(() => { //异步清除当前覆盖物
                that.map.clearOverlays()
            }, 0)
        })
        this.map.addOverlay(label) //最后在地图中添加覆盖物 传入label
    }

    async getHouseDetail(id) { //获取房源数据方法 (城市ID)
        let res = await axios.get('https://api-haoke-web.itheima.net/houses', { params: { cityId: id } })
        //传入城市ID调用线上接口
        if (res.data.status === 200) { //修改弹框内数据后,及时打开弹框,看到房源渲染效果
            this.setState({ maskData: res.data.body.list }, () => {
                this.setState({ visible: true })
            })
        }
    }

    componentDidMount() { //在组件挂载结束钩子里调用初始化地图方法
        this.initMap()
    }

    render() {
        return (
            <div className='map-container'>
                <NavBar className='header-bar' onBack={() => this.props.history.goBack()}>地图找房</NavBar>
                <div ref={this.mapContainer} className='map-container'>
                    
                </div>
                <Popup
                    visible={this.state.visible}
                    onMaskClick={() => {
                        this.setState({ visible: false })
                    }}
                    bodyStyle={{ height: '50vh', overflowY: 'scroll' }}
                >
                    <div className="mask-container">
                        <h2 className="mask-title">
                            房屋列表
                            <span>更多房源</span>
                        </h2>
                        <div className="mask-content">
                            {this.state.maskData.map((item) => {
                                return (<div className='mask-item' key={item.houseCode}>
                                    <img src={require('../../assest/images/house.jpg')} />
                                    <div className='right-item'>
                                        <h3>
                                            {item.title}
                                        </h3>
                                        <p>{item.desc}</p>
                                        <div>
                                            {item.tags.map((item, index) => {
                                                return (<span className='mask-tags' key={index}>{item}</span>)
                                            })}
                                        </div>
                                        <h4>{item.price}<b>元/月</b></h4>
                                    </div>
                                </div>)
                            })}
                        </div>
                    </div>
                </Popup>
            </div>
        )
    }
}

export default Index

这样基本上就可以做一个不错的地图小demo,主要还是要会用百度地图JSAPI,看文档写代码
效果:
1、首先看到上海全部区的房源信息
屏幕截图 2022-08-11 142917.png
2、在看到区中全部城镇的房源信息
屏幕截图 2022-08-11 142936.png
3、最后最底层就是城镇中全部的小区房源
屏幕截图 2022-08-11 143013.png
4、点击矩形覆盖物实现弹框渲染房源
屏幕截图 2022-08-11 143035.png
本文就带来了百度地图在项目中开发的冰山一角,希望能对您能有所帮助