likes
comments
collection
share

为你的React项目接入高德地图

作者站长头像
站长
· 阅读数 10

最近在写一个项目的时候,需要使用地图来选取点位信息

使用高德地图

在技术选型上面,我们使用Antd推荐的高德地图,并支持React的组件化

npm install --save react-amap

高德地图申请key和密钥

为你的React项目接入高德地图

添加key和安全密钥

给Map添加key

<Map
  amapkey={MAP_KEY}
  plugins={plugins}
  events={this.handleMapEvents}
  zoom={17}
  center={[lng, lat]}
  doubleClickZoom={false}
>

添加安全密钥

我们在开发环境下直接使用script标签添加安全密钥

<script type="text/javascript"> 
    window._AMapSecurityConfig = { securityJsCode:'您申请的安全密钥', } 
</script>

在React脚手架中,我们可以在index.html文件中添加script标签。但是我们使用的是Umi项目,没有index.html,我们可以更改Umi默认模板,详见:修改默认模板

  1. 在pages文件夹下面添加document.ejs文件
  2. 添加密钥

创建地图组件

我们最后实现的效果可能会是这个样子

为你的React项目接入高德地图

我们要实现的部分主要分成三个

  1. 搜索框的实现,包括搜索提示

为你的React项目接入高德地图

  1. 地图展示部分,功能有根据根据经纬度进行定位,通过点击地图来逆编码来获得地址
  2. 搜索结果的基本信息展示

我们在设计组件的时候,可以从功能上进行拆分,并考虑到组件复用的问题,尽量让组件原子化,只干一件事情。还可以将组件分成容器组件(只管理数据)和渲染组件(只管理视图)进行分离,并尽量保持组件的无状态,无state

划分组件

根据上面的分析,我们可以将搜索框单独设置为一个组件,并且可以复用在其他需要搜索地址的地方。将地图的展示单独设置成为一个组件,只需要接受到经纬度就好可以展示相应的信息了。搜索结果也可以单独展示出来就好了。

同时,在搜索框输入地址的时候,我们可以改变地图组件的状态;点击地图中的点位的时候,也可以改变输入框中的值。所以我们可以创建一个容器组件来管理两者互通的数据,即经纬度和地址名称

示意图:

为你的React项目接入高德地图

搜索组件编码

JavaScript API支持搜索服务脱离地图使用,即使用搜索服务不再需要先实例化地图。您可通AMap.plugin方法,加载需要的服务。同时JavaScript API将原有的通过事件监听获得服务查询结果,修改为通过方法的回调函数获得服务查询结果。

参考:lbs.amap.com/api/javascr…

由于我们每次改变输入框中的值,就会触发一次请求,所以我们可以在输入框中加一个防抖函数

import React, { Component } from 'react';
import { Select } from 'antd';

const { Option } = Select;
class SearchAddress extends Component {
  constructor(props) {
    super(props);
    this.state = {
      positionList:[],
    }
    this.handleMapEvents =  {
      created: () => {
        window.AMap.plugin('AMap.PlaceSearch', () => {
          new window.AMap.PlaceSearch({
            pageSize: 10,
            pageIndex: 1,
          });
        })
      },
    }
  }



  onSearch =(val)=>{
    const place = new window.AMap.PlaceSearch({
      pageSize: 10,
      pageIndex: 1,
      city: '绵阳',
    })

    place.search(val,(status, result)=>{
      if(status === 'complete' && result.info === 'OK'){
        const {poiList:{pois}}=result
        if(pois && Array.isArray(pois)) {
          this.setState({
            positionList:pois
          })
        }
      }

    })
  }


  debounce = (fn,time) => {
    let timerId = null;
    return function (val) {
      if(timerId) {
        clearTimeout(timerId);
        timerId = null;
      }
      timerId = setTimeout(() => {
        fn.call(this,val)
      },time)
    }
  }
  
  // 选中Seleect框中触发回调,改变父组件的position和addressName
  onChange = (id) => {
    const { positionList } = this.state;
    const {changeAddressName,changePosition} = this.props;
    for(const item of positionList) {
      const { name:itemName,id:itemId } = item;
      if(itemId === id) {
        const {location : {lng,lat}} = item;
        const position = {lng,lat};
        changePosition(position)
        changeAddressName(itemName)
      }
    }
  }

  render() {
    const {addressName} = this.props
    const {positionList} = this.state;
    return (
      <div>
        <Select
          value={addressName}
          style={{ width: 400 }}
          showSearch
          placeholder="请输入地址"
          onSearch={this.debounce((val) => this.onSearch(val),300)}
          onChange={this.onChange}
          optionFilterProp="children"
        >
          {
            positionList.map( item=>
              <Option key={item.id} value={item.id}>{item.name}</Option>
            )
          }
        </Select>
      </div>
    );
  }
}

export default SearchAddress;

地图组件编码实现

当我们点击地图时,只能通过click事件得到经纬度,而得不到地址名。经纬度对于用户是没有用的, 所以我们使用逆编码来获得地址名称

参考: lbs.amap.com/api/javascr…

我们还可以使用static propTypes和defaultProps来规定props传入值的类型和默认值

static propTypes = {
  position:PropTypes.object,
  plugins: PropTypes.array,
};

static defaultProps = {
  position :{
    lng:104.679127,
    lat:31.467673,
  },
  plugins: ["ToolBar", 'Scale']
};

地图组件:

import React, { Component, Fragment } from 'react';
import {Map, Marker} from 'react-amap';
import PropTypes from 'prop-types';
import { message } from 'antd';
import { MAP_KEY } from '@/utils/Enum';

class AdvancedMap extends Component {
  static propTypes = {
    position:PropTypes.object,
    plugins: PropTypes.array,
  };

  static defaultProps = {
    position :{
      lng:104.679127,
      lat:31.467673,
    },
    plugins: ["ToolBar", 'Scale']
  };

  constructor(props) {
    super(props);
    this.handleMapEvents =  {
      created: () => {
        window.AMap.plugin('AMap.PlaceSearch', () => {
          new window.AMap.PlaceSearch({
            pageSize: 10,
            pageIndex: 1,
          });
        })
        window.AMap.plugin('AMap.Geocoder', () =>{
          new window.AMap.Geocoder({
            city: '0816'
          })
        })
      },
      click: (e) => {
        const { lnglat:lnglatObj,lnglat: {lng,lat} } = e;
        const { changePosition,changeAddressName } = this.props;
        const lnglatArr = [lng,lat]
        const geocoder = new window.AMap.Geocoder({
          city: '0816'
        })
        changePosition(lnglatObj)
        geocoder.getAddress(lnglatArr, (status, result) => {
          if (status === 'complete' && result.info === 'OK') {
            console.log(result,'result');
            const { regeocode :{formattedAddress}} = result;
            changeAddressName(formattedAddress)
            message.success(formattedAddress)
          }
        })
      }
    }
  }


  render() {
    const {position: { lng,lat }, plugins} = this.props;
    return (
      <Fragment>
        <Map
          amapkey={MAP_KEY}
          plugins={plugins}
          events={this.handleMapEvents}
          zoom={17}
          center={[lng, lat]}
          doubleClickZoom={false}
        >
          <Marker position={[lng, lat]} />
        </Map>
      </Fragment>
    );
  }
}

export default AdvancedMap;

父级容器

使用父级容器主要是管理数据(position,addressName),并传递改变数据(position,addressName)的方法给子组件,子组件调用父组件传递的函数,就可以更改父组件的数据(参考父子组件参数)

import React, { Component } from 'react';
import { Col, Input, Row } from 'antd';

import SearchAddress from '@/pages/Map/SearchAddress';
import AdvancedMap from '@/pages/Map/AdvancedMap';

class Index extends Component {
  constructor(props) {
    super(props);
    this.state = {
      position: {
        lng:104.679127,
        lat:31.467673,
      },
      addressName:''
    }
  }

  changePosition = (value) => {
    this.setState({
      position:value
    })
  }

  changeAddressName = (value) => {
    this.setState({
      addressName:value
    })
  }
  
  render() {
    const {position,addressName} = this.state;
    return (
      <div style={{ width: '100%', height: '500px' }}>
        <Row gutter={24}>
          <Col span={8}>
            <SearchAddress
              changePosition={this.changePosition}
              changeAddressName={this.changeAddressName}
              addressName={addressName}
            />
          </Col>
        </Row>
        <br />
        <AdvancedMap
          position={position}
          changePosition={this.changePosition}
          changeAddressName={this.changeAddressName}
        />
      </div>
    );
  }
}

export default Index;

效果

后续还要再完善一下,比如规定好每个组件的输入输出,防止使用的时候出错