关于如何在react+腾讯地图gl版上实现文本输入

520 阅读2分钟

近期在地图类的项目上有一个需求,需要在地图上完成多种标记,线,文本的绘制,类似官方示例在地图上浮动一个工具栏,点击工具栏后可以切换当前的激活类型,然后点击地图后即可以自动生成所需的内容,工具栏如下图所示

像是线的绘制跟标记点的绘制就不再赘述,可以参考相关文档腾讯地图绘制几何图形

其中文本的需求就是可以点击地图后生成输入框,输入完成后保存当前文本信息并保持位置不变,看了文档后决定使用自定义DOM覆盖物(DOMOverlay)这块来实现,以下是实现过程

  1. 实现一个自定义DOM覆盖物类,继承DOMOverlay类
class InputOverlay extends TMap.DOMOverlay {
  constructor(options) {
    super(options)
  }

  onInit(options: any) { //实现这个接口来定义构造阶段的初始化过程,此方法在构造函数中被调用,接收构造函数的options参数作为输入
    this._map = options.map
    this._position = options.position
  }

  createDOM() { // 实现这个接口来创建自定义的DOM元素,此方法在构造函数中被调用(在初始化过程之后),需要返回html字符串
    let inputWrapper = document.createElement('input')
    inputWrapper.style.cssText = `color: #fff;background: rgba(0,0,0,0.58);border: none;position: absolute;border-radius: 2px;padding: 2px 6px;width: 100px`
    this._dom = inputWrapper
    return inputWrapper
  }

  updateDOM() { // 实现这个接口来更新DOM元素的内容及样式,此方法在地图发生平移、缩放等变化时被调用
    let pixel = this._map.projectToContainer(this._position)
    let left = pixel.getX() - this._dom.clientWidth / 2 + 'px' //本例水平居中
    let top = pixel.getY() + 'px'
    this._dom.style.transform = `translate3d(${left}, ${top}, 0px)`
  }
}
  1. 地图实例中使用
// 绘制路线模式下,点击地图时设置Input
  const setInput = (e: any) => {
    if (!currentMap) return // currentMap是当前的地图实例

    let myInput = new InputOverlay({
      map: currentMap,
      position: e.latLng,
    })
  }

实现效果

到这里还没结束,如果你点击输入框尝试输入你会发现点击后立刻生成一个相同的输入框出来,导致无法输入,这是因为当前地图实例还没解绑监听事件,你点击输入框,事件冒泡后传到map上,于是就触发了地图的点击监听,所以我们需要修改自定义DOM覆盖物类,只要在输入框的focus事件中,解绑监听事件,并且在输入框blur事件中,重新监听即可.

  1. 修改自定义类
class InputOverlay extends window.TMap.DOMOverlay {
  constructor(options) {
    super(options)
  }

  onInit(options: any) {
    this._map = options.map
    this._position = options.position
    this._onFocus = options.onFocus
    this._unFocus = options.unFocus
  }

  createDOM() {
    let inputWrapper = document.createElement('input')
    inputWrapper.style.cssText = `color: #fff;background: rgba(0,0,0,0.58);border: none;position: absolute;border-radius: 2px;padding: 2px 6px;width: 50px`
    inputWrapper.addEventListener('focus', this._onFocus)
    inputWrapper.addEventListener('blur', () => {
      inputWrapper.disabled = true
      this._unFocus()
    })
    this._dom = inputWrapper
    return inputWrapper
  }

  updateDOM() {
    let pixel = this._map.projectToContainer(this._position)
    let left = pixel.getX() - this._dom.clientWidth / 2 + 'px' //本例水平居中
    let top = pixel.getY() + 'px'
    this._dom.style.transform = `translate3d(${left}, ${top}, 0px)`
  }
}

实体类的使用

const setInput = (e: any) => {
  if (!currentMap) return // currentMap是当前的地图实例

  let myInput = new InputOverlay({
    map: currentMap,
    position: e.latLng,
    onFocus() {
      currentMap.off('click', setInput)
    },
    unFocus() {
      currentMap.on('click', setInput)
    },
  })
}
  1. 实际效果

至此大功告成