react-ts接入web端腾讯地图、定位、搜索(简易案例)

1,447 阅读3分钟

在使用 typescript 或者javascript 前端接入腾讯地图的时候,看了官方文档可能会有一点疑问(如果没搞过的话),实际上很简单,下面以 typescript 为例,介绍一下怎么用的

话不多说,先上一张多点地图图

image.png

案例demo

地图

以地图为例,首先获取地图的使用对象,由于地图是js代码,因此类型问题,涉及到的都标记any即可

//声明该对象方便使用,也可以不声明,直接resolve返回
var TMap: any

//获取代码地图对象,这样就获取到了
//实际也可以在html中直接导入,个人感觉按需导入更好一些
export const TMapGL = (key: string): Promise<any> => {
  return new Promise(function(resolve, reject) {
    //加载一段脚本
    var script = document.createElement('script')
    script.type = 'text/javascript'
    //引入脚本连接src,需要拼接key,可以先试用案例的key
    //由于为get请求,实际可以网页打开查看返回的代码,可以看到,其在 window 上挂载了 TMap 对象
    //而这个TMap对象就是官方案例用到的参数
    script.src = 'https://map.qq.com/api/gljs?v=1.exp&key=' + key
    script.onerror = (err) => reject(err)
    script.onload = (e) => {
      //加载脚本完毕之后,TMap就被挂载到window上了,注意由于js挂载上面的,ts会报错
      //因此需要强转才能调用,费则会报错
      TMap = (window as any).TMap
      resolve(e)
    }
    document.head.appendChild(script)
  })
}

下面简单介绍一下地图使用,以及生成多个点测试一下海量点(腾讯不支持海量点,因此上万就很卡了,这里也就几千,太多建议高德,另外腾讯有点聚合功能支持,可以根据需要更改)

ps:腾讯地图创建到节点时不会检查是否存在,因此需要额外注意(严格模式下由于方法会执行两遍,会出现两个地图,测试功能时,只能操作里面的地图,因此测试功能时出现误差,取消严格模式即可,即去掉React.StrictMode)

const initMap = () => {
    //定义地图中心点坐标
    TMapGL(mapkey).then(() => {
      console.log('TMap', TMap)
      let center = new TMap.LatLng(39.160001, 117.156150)
      let myOptions = {
        zoom: 18,
        center
      };
      let dom = document.getElementById('my-map')
      //创建地图,绑定dom
      let map = new TMap.Map(dom, myOptions);
      //Map实例创建后,通过on方法绑定点击事件
      map.on("click", onClickMap)
      
      //同时也可以监听缩放相关,通过getZoom方法获取当前缩放指数,以控制 label的显示隐藏
      //可以通过提前生成 label 的方式,在不同缩放等级时控制 label 显隐即可
      map.on('zoom', onMapZoom);
      myMap.current = map

      //创建并初始化MultiMarker
      mapLayer.current = new TMap.MultiMarker({
        map: map,  //指定地图容器
        //样式自定义
        styles: {
            //创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
            "marker": new TMap.MarkerStyle({ 
                "width": 25,  // 点标记样式宽度(像素)
                "height": 25, // 点标记样式高度(像素)
                "src": `${process.env.PUBLIC_URL}/logo192.png`,  //图片路径,不设置会使用腾讯地图默认的红标
                //焦点在图片中的像素位置,一般大头针类似形式的图片以针尖位置做为焦点,圆形点以圆心位置为焦点
                "anchor": { x: 16, y: 32 }  
            }) 
        },
        //点标记数据数组
        geometries: [{//第1个点标记,默认
            "id": "0",
            "styleId": 'marker',
            "position": center,
            "properties": {
                "title": "defaultMarker"
            }
        }]
      });

      //可以给marker添加点击事件,注意此事件不会阻止向下继续冒泡
      mapLayer.current.on("click", function(e: any) {
        console.log('onClickMarker', e)
      })
      //当移入时 geometry.properties 里面会有我们的 item 信息,否则没有
      //可以通过该属性控制 label 显示隐藏
      mapMarkersLayer.current.on("hover", onMarkersHover)
      
      mapLabelsLayer.current = new TMap.MultiLabel({
        id: 'label-layer',
        map: map, //设置折线图层显示到哪个地图实例中
        //文字标记样式
        styles: {
            'label': new TMap.LabelStyle({
                'color': '#1b90ff', //颜色属性
                'size': 14, //文字大小属性
                'offset': { x: 0, y: -38 }, //文字偏移属性单位为像素
                'angle': 0, //文字旋转属性
                'alignment': 'center', //文字水平对齐属性
                'verticalAlignment': 'middle' //文字垂直对齐属性
            })
        },
        //文字标记数据
        geometries: []
      });
    }).catch(err => {
      console.log('err', err)
    })
}

const onMapZoom = (e: any) => {
    //地图缩放更新后,我们个根据zoom是否显示 marker、label
    myMap.current.getZoom()
}

//地图点击事件回调
const onClickMap = (e: any) => {
    console.log('click', e)
    addMorePoints(e)
}

//生成多个点,以测量大量数据
const addMorePoints = (e: any) => {
    let lat = e.latLng.getLat();
    let lng = e.latLng.getLng();
    console.log("您点击的的坐标是:"+ lat + "," + lng);

    let markers: any[] = []
    for(let i = 1; i < 1000; i++) {
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat + Math.random(), lng + Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat - Math.random(), lng - Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat + Math.random(), lng - Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat - Math.random(), lng + Math.random()),  //点标记坐标位置
      })
    }
    console.log('markers', markers)
    // mapLayer.current.add(markers)
    mapLayer.current.setGeometries(markers)
}

//注意id,map所在canvas会嵌入进去
return (
    <div id="my-map" style={{width: '100vw', height: '100vh'}}></div>
  );

定位

定位功能也与地图类似,只不过不需要 key 罢了,一般都是通过网络定位

var qq: any

export const TLocationGL = (): Promise<any> => {
  return new Promise(function(resolve, reject) {
    var script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = 'https://mapapi.qq.com/web/mapComponents/geoLocation/v/geolocation.min.js'
    script.onerror = (err) => reject(err)
    script.onload = (e) => {
      //加载完成之后赋值,可以根据需要更名
      qq = (window as any).qq
      resolve(e)
    }
    document.head.appendChild(script)
  })
}

//简易使用定位组件
const initLocation = () => {
    TLocationGL().then(res => {
      console.log('qq', (window as any).qq)
      var geolocation = new qq.maps.Geolocation(mapkey, "web-map-demo");
      //单次相对精准定位方法,三个参数,分别是成功回调,失败回调,参数设置,后面两个参数可以不填写
      //第三个参数: {timeout: number, failTipFlag: boolean} 
      //分别表示超时时间,默认10s(自己更新时参数为ms),失败后是否提示打开定位,
      geolocation.getLocation((position: any) => {
        console.log('position', position)
        myMap.current.setCenter(new TMap.LatLng(position.lat, position.lng))
      })
      //连续监听定位回调方法
      geolocation.watchPosition(getPosition);
      geolocation.clearWatch() //使用后退出页面,需要主动清除监听
    }).catch(err => {
      console.log('err', err)
    })
}

搜索

如果要用到搜索服务,其为附加服务,默认没有导入,那么需要在导入地图的src中需要额外加入参数 &libraries=service

const TMapGL = (key: string): Promise<any> => {
    if (TMap) return Promise.resolve()
    return new Promise(function(resolve, reject) {
        var script = document.createElement('script')
        script.type = 'text/javascript'
        //如果需要用到一些附加服务信息,需要添加 &libraries=service
        script.src = 'https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=' + key
        script.onerror = (err) => reject(err)
        script.onload = (e) => {
            TMap = (window as any).TMap
            resolve(e)
        }
        document.head.appendChild(script)
    })
}

使用方便,这里面就以地点搜索服务的 searchRegion为例

const searchByText = (keyword: string, cityName: string = '天津') => {
    //初始化service附加服务的 Search 搜索类,这里需要调用 Search 的构造方法
    let search = new TMap.service.Search({pageSize: 20})

    //调用Search中的 searchRegion 搜索函数,其他也也是类似
    search.searchRegion({
        keyword,
        cityName,
        autoExtend: true //当前范围没搜索到,自动慢慢扩张到全城市
    }).then((res : any) => {
        console.log('搜索结果', res)
    }).catch((err: any) => {
        console.log(err)
    })
}

全部代码

全部代码如下所示,可以尝试一下

const mapkey = "OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77" //官方案例的key,换成自己的

var TMap: any
var qq: any

export const TMapGL = (key: string): Promise<any> => {
  return new Promise(function(resolve, reject) {
    var script = document.createElement('script')
    script.type = 'text/javascript'
    script.text = 'TMap'
    script.src = 'https://map.qq.com/api/gljs?v=1.exp&key=' + key
    script.onerror = (err) => reject(err)
    script.onload = (e) => {
      TMap = (window as any).TMap
      resolve(e)
    }
    document.head.appendChild(script)
  })
}

export const TLocationGL = (): Promise<any> => {
  return new Promise(function(resolve, reject) {
    var script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = 'https://mapapi.qq.com/web/mapComponents/geoLocation/v/geolocation.min.js'
    script.onerror = (err) => reject(err)
    script.onload = (e) => {
      qq = (window as any).qq
      resolve(e)
    }
    document.head.appendChild(script)
  })
}

function App() {
  const myMap = useRef<any>(null)
  const mapLayer = useRef<any>(null)

  useEffect(() => {
    initMap()
    initLocation()
  }, [])

  const initLocation = () => {
    TLocationGL().then(res => {
      console.log('qq', (window as any).qq)
      var geolocation = new qq.maps.Geolocation(mapkey, "web-map-demo");
      geolocation.watchPosition(getPosition);
    }).catch(err => {
      console.log('err', err)
    })
  }

  const getPosition = (position: any) => {
    console.log('position', position)
    myMap.current.setCenter(new TMap.LatLng(position.lat, position.lng))
  }

  const initMap = () => {
    //定义地图中心点坐标
    TMapGL(mapkey).then(() => {
      console.log('TMap', TMap)
      let center = new TMap.LatLng(39.160001, 117.156150)
      let myOptions = {
        zoom: 18,
        center
      };
      let dom = document.getElementById('my-map')
      //创建地图,绑定dom
      let map = new TMap.Map(dom, myOptions);
      //Map实例创建后,通过on方法绑定点击事件
      map.on("click", onClickMap)
      myMap.current = map

      //创建并初始化MultiMarker
      mapLayer.current = new TMap.MultiMarker({
        map: map,  //指定地图容器
        //样式自定义
        styles: {
            //创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
            "marker": new TMap.MarkerStyle({ 
                "width": 25,  // 点标记样式宽度(像素)
                "height": 25, // 点标记样式高度(像素)
                "src": `${process.env.PUBLIC_URL}/logo192.png`,  //图片路径,不设置会使用腾讯地图默认的红标
                //焦点在图片中的像素位置,一般大头针类似形式的图片以针尖位置做为焦点,圆形点以圆心位置为焦点
                "anchor": { x: 16, y: 32 }  
            }) 
        },
        //点标记数据数组
        geometries: [{//第1个点标记
            "id": "0",
            "styleId": 'marker',
            "position": center,
            "properties": {
                "title": "defaultMarker"
            }
        }]
      });
    }).catch(err => {
      console.log('err', err)
    })
  }

  const onClickMap = (e: any) => {
    console.log('click', e)
    // addOnePoint(e)
    addMorePoints(e)
  }

  const addOnePoint = (e: any) => {
    let lat = e.latLng.getLat();
    let lng = e.latLng.getLng();
    console.log("您点击的的坐标是:"+ lat + "," + lng);

    let markers = [{
      "id": Math.random() * 100000 % 100000 + '',   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
      "styleId": 'marker',  //指定样式id
      // "position": new TMap.LatLng(lat + Math.random() / 100, lng + Math.random() / 100),  //点标记坐标位置
      "position": e.latLng,  //点标记坐标位置
      "properties": {
        "title": "defaultMarker"
      }
    }]
    mapLayer.current.add(markers)
  }
  
  //搜索服务
  const searchByText = (keyword: string, cityName: string = '天津') => {
    //初始化service附加服务的 Search 搜索类,这里需要调用 Search 的构造方法
    let search = new TMap.service.Search({pageSize: 20})

    //调用Search中的 searchRegion 搜索函数,其他也也是类似
    search.searchRegion({
        keyword,
        cityName,
        autoExtend: true //当前范围没搜索到,自动慢慢扩张到全城市
    }).then((res : any) => {
        console.log('搜索结果', res)
    }).catch((err: any) => {
        console.log(err)
    })
  }

  const addMorePoints = (e: any) => {
    let lat = e.latLng.getLat();
    let lng = e.latLng.getLng();
    console.log("您点击的的坐标是:"+ lat + "," + lng);

    let markers: any[] = []
    for(let i = 1; i < 1000; i++) {
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat + Math.random(), lng + Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat - Math.random(), lng - Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat + Math.random(), lng - Math.random()),  //点标记坐标位置
      })
      markers.push({
        "id": '' + i + '' + lat + '' + lng,   //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
        "styleId": 'marker',  //指定样式id
        "position": new TMap.LatLng(lat - Math.random(), lng + Math.random()),  //点标记坐标位置
      })
    }
    console.log('markers', markers)
    // mapLayer.current.add(markers)
    mapLayer.current.setGeometries(markers)
  }

  return (
    <div id="my-map" style={{width: '100vw', height: '100vh'}}></div>
  );
}