类名 common/videoGIS/VideoMapView.js
import { VideoMetaData, ViewEventType, Zondy } from '../base'
import { GeometryType } from '../base/enum'
import fbMouseEventType from '../base/enum/FabricMouseEventType'
import { GeometryEngine, LineString, Point, Polygon } from '../base/geometry'
import { VideoMapLayer } from '../document/layer'
import { defaultValue, defined, Log, VideoPixelCoordTransforms } from '../util'
import BaseView from '../view/BaseView'
import { adjustChildSizeToParent } from './Utils'

/**
 * @event VideoMapView#地图视图加载完毕事件
 * @property {Object} event 事件对象
 * @example <caption><h7>视频地图视图加载完毕事件</h7></caption>
 * view.on('loaded', (event) => {
 *   console.log("视频地图加载完成事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标点击事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'click'] 事件类型
 * @property {Object} [event.absolutePointer] 鼠标在视图的绝对位置
 * @property {Object} [event.pointer] 鼠标在视图的像素位置
 * @property {Object} [event.target] 视图上被点击到的几何图形
 * @property {Boolean} [event.isClick = false] 是否是点击事件
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标点击事件</h7></caption>
 * view.on('click', (event) => {
 *   console.log("点击事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标按下事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'mouse-down'] 事件类型
 * @property {Object} [event.absolutePointer] 鼠标在视图的绝对位置
 * @property {Object} [event.pointer] 鼠标在视图的像素位置
 * @property {Object} [event.target] 视图上被点击到的几何图形
 * @property {Boolean} [event.isClick = false] 是否是点击事件
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标按下事件</h7></caption>
 * view.on('mouse-down', (event) => {
 *   console.log("按下事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标抬起事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'mouse-up'] 事件类型
 * @property {Object} [event.absolutePointer] 鼠标在视图的绝对位置
 * @property {Object} [event.pointer] 鼠标在视图的像素位置
 * @property {Object} [event.target] 视图上被点击到的几何图形
 * @property {Boolean} [event.isClick = false] 是否是点击事件
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标抬起事件</h7></caption>
 * view.on('mouse-up', (event) => {
 *   console.log("抬起事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标双击事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'double-click'] 事件类型
 * @property {Object} [event.absolutePointer] 鼠标在视图的绝对位置
 * @property {Object} [event.pointer] 鼠标在视图的像素位置
 * @property {Object} [event.target] 视图上被点击到的几何图形
 * @property {Boolean} [event.isClick = false] 是否是点击事件
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标双击事件</h7></caption>
 * view.on('double-click', (event) => {
 *   console.log("双击事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标移动事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'mouse-move'] 事件类型
 * @property {Object} [event.absolutePointer] 鼠标在视图的绝对位置
 * @property {Object} [event.pointer] 鼠标在视图的像素位置
 * @property {Object} [event.target] 视图上的几何图形
 * @property {Boolean} [event.isClick = false] 是否是点击事件
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标移动事件</h7></caption>
 * view.on('mouse-move', (event) => {
 *   console.log("移动事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标移出事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'mouse-out'] 事件类型
 * @property {Object} [event.target] 视图上的几何图形
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标移出事件</h7></caption>
 * view.on('mouse-out', (event) => {
 *   console.log("移出事件:", event)
 * })
 */

/**
 * @event VideoMapView#鼠标移入事件
 * @property {Object} event 事件对象
 * @property {ViewEventType} [event.type = 'mouse-over'] 事件类型
 * @property {Object} [event.target] 视图上的几何图形
 * @property {Object} [event.e] 鼠标原事件对象
 * @example <caption><h7>鼠标移入事件</h7></caption>
 * view.on('mouse-over', (event) => {
 *   console.log("移入事件:", event)
 * })
 */

/**
 * 视频地图视图类 <br/>
 * 支持MP4,HLS,RTMP视频格式。
 * @moduleEX ViewModule
 * @extends Event
 * @fires VideoMapView#地图视图加载完毕事件
 * @fires VideoMapView#鼠标点击事件
 * @fires VideoMapView#鼠标双击事件
 * @fires VideoMapView#鼠标按下事件
 * @fires VideoMapView#鼠标抬起事件
 * @fires VideoMapView#鼠标移动事件
 * @fires VideoMapView#鼠标移出事件
 * @fires VideoMapView#鼠标移入事件
 * @param {Object} options 构造参数
 * @param {Map}  [options.map = '']  图层管理器
 * @param {String} [options.viewId = ''] 图层容器ID
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#useInteriorHTMLVideoElement'>[1、初始化视频地图视图]</a><br/>
 * <a href='#pixelCoordToGeoCoord'>[2、像素坐标转地理坐标]</a><br/>
 * <a href='#geoCoordToPixelCoord'>[3、地理坐标转像素坐标]</a><br/>
 *
 * @example <caption><h7>创建视频地图视图示例</h7></caption>
 * // ES5引入方式
 * const { Map, VideoMapView } = Zondy
 * // ES6引入方式
 * import { Map, VideoMapView } from "@mapgis/webclient-common"
 * // 新建一个map容器
 * const videoMap = new Map()
 * videoMapView = new VideoMapView({
 *   viewId: 'videoMapView-container',
 *   map: videoMap
 * })
 * // 传入视频标定数据
 * videoMapView.init(data)
 */
class VideoMapView extends BaseView {
  constructor(options) {
    super(options)
    options = defaultValue(options, {})
    if (!defined(window.fabric)) {
      Log.error('The fabric library is lost.')
      return
    }

    if (typeof this._viewId === 'string') {
      this._videoMapViewContainer = document.getElementById(this._viewId)
    } else {
      this._videoMapViewContainer = this._viewId
    }

    this._videoPixelCoordTransforms = null
    this._videoMetaData = null

    this._fabricCanvas = null

    this.graphics = new Proxy(this.graphics, {
      get: (obj, prop) => {
        // 对graphic的添加做监听
        if (prop === 'add') {
          return (item) => {
            this._drawGraphic(item)
            // 监听geometry
            item.geometry = new Proxy(item.geometry, {
              set: (geoObj, geoProp, geoValue) => {
                geoObj[geoProp] = geoValue
                // 对geometry的coordinates的修改做监听
                if (geoProp === 'coordinates') {
                  this._updateGraphic(item)
                }
                return true
              }
              // get: (geoObj, geoProp) => {
              //   if (geoProp === 'insertPoint') {
              //     this._updateGraphic(item)
              //   } else if (geoProp === 'setPoint') {
              //     this._updateGraphic(item)
              //   } else if (geoProp === 'removePoint') {
              //     this._updateGraphic(item)
              //   }
              //   return geoObj[geoProp]
              // }
            })
            return obj[prop](item)
          }
        }
        // 对graphic的删除做监听
        else if (prop === 'remove') {
          return (item) => {
            this._deleteGraphic(item)
            return obj[prop](item)
          }
        }
        return obj[prop]
      }
    })
    this._points = new Map()
    this._lines = new Map()
    this._polygons = new Map()
    this._texts = new Map()
    this._tempFabrics = []

    this._videoPixelScale = 0

    this._fabricContainer = null
    this._videoMapLayer = null
  }

  /**
   * 像素坐标与地理坐标转换对象
   * @readonly
   * @member {VideoPixelCoordTransforms} VideoMapView.prototype.videoPixelCoordTransforms
   */
  get videoPixelCoordTransforms() {
    return this._videoPixelCoordTransforms
  }

  /**
   * 初始化视频地图视图<br/>
   * 支持播放MP4,HLS,RTMP格式视频,支持传入HTMLVideoElement标签,支持传入videojs实例化对象。HLS与RTMP视频流播放时会有延迟。
   * @function VideoMapView.prototype.init
   * @param {VideoMetaData} videoMetaData 视频元数据<br/>
   * 视频标定数据使用的地图数据与运行时的地图数据要保持一致。如:标定数据是基于天地图的影像数据得到的,则运行时也需要使用天地图的影像数据。<br/>
   * 参考示例:<br/>
   * <a href='#useInteriorHTMLVideoElement'>[1、由视频地图内部自己创建HTMLVideoElement示例]</a><br/>
   *
   * 支持传入外部的video播放器<br/>
   * 1. 传入的视频播放器的宽高需要和传给VideoMapView的div容器的宽高保存一致,否则会出现草图编辑区域与视频区域无法重合的问题。<br/>
   *
   * 2. 视频可进行同比例的缩放,不可拉伸变形。如400\*300的视频可以等比例缩放为200\*150,不可拉伸为300\*300。<br/>
   *
   * 3. 参考示例:<br/>
   * 外部video播放器播放MP4格式视频,传HTMLVideoElement。<br/>
   * <a href='#playMP4'>[2、传入HTMLVideoElement,视频为MP4格式]</a><br/>
   * 外部video播放器播放HLS格式视频流,传HTMLVideoElement。<br/>
   * <a href='#playHLS'>[3、传入HTMLVideoElement,视频为HLS格式]</a><br/>
   * 外部video播放器播放RTMP格式视频流,传入videojs对象。<br/>
   * <a href='#playRTMP'>[4、传入videojs对象,视频为RTMP格式]</a><br/>
   *
   * @example <caption><h7 id = useInteriorHTMLVideoElement>1. 由视频地图内部自己创建videoHTML示例</h7></caption>
   * // ES5引入方式
   * const { Map, VideoMapView } = Zondy
   * // ES6引入方式
   * import { Map, VideoMapView } from "@mapgis/webclient-common"
   * // 新建一个map容器
   * const videoMap = new Map()
   * videoMapView = new VideoMapView({
   *   viewId: 'videoMapView-container',
   *   map: videoMap
   * })
   *
   * videoMapView.init(mockData)
   *
   * @example <caption><h7 id = playMP4>2. 传入HTMLVideoElement,视频为MP4格式</h7></caption>
   * const videodom = document.getElementById('videolayer')
   * videodom.play()
   * // 使用HTMLVideoElement替换视频源
   * mockData.videoSource = videodom
   * videoMapView.init(mockData)
   *
   * @example <caption><h7 id = playHLS>3. 传入HTMLVideoElement,视频为HLS格式</h7></caption>
   * const videodom = document.getElementById('videolayer')
   * const player = videojs(videodom)
   * player.play()
   * // 使用HTMLVideoElement替换视频源
   * mockData.videoSource = player.el().querySelector('video')
   *
   * videoMapView.init(mockData)
   *
   * @example <caption><h7 id = playRTMP>4. 传入videojs对象,视频为RTMP格式</h7></caption>
   * const videodom = document.getElementById('videolayer')
   * const player = videojs(videodom)
   * player.play()
   * // 使用videojs对象替换视频源
   * mockData.videoSource = player
   * videoMapView.init(mockData)
   */
  init(videoMetaData) {
    const param = defaultValue(videoMetaData, {})

    if (!this._videoPixelCoordTransforms) {
      this._videoPixelCoordTransforms = new VideoPixelCoordTransforms()
    }
    this._videoMetaData = new VideoMetaData(param)

    this._videoPixelCoordTransforms.init(
      this._videoMetaData.videoRegistrationParam
    )

    const url = this._videoMetaData.videoSource.videoUrl
    const source = url || this._videoMetaData.videoSource
    const videoLayer = new VideoMapLayer({
      source,
      container: this._videoMapViewContainer
    })
    this._map.add(videoLayer)
  }

  /**
   * 像素坐标转地理坐标,传入视频地图视图上的像素坐标
   * @function VideoMapView.prototype.pixelCoordToGeoCoord
   * @param {Point} pixelCoord 像素坐标,单位为像素值
   * @returns {Point} 地理坐标,经纬度表示
   * @example <caption><h7 id = pixelCoordToGeoCoord>像素坐标转地理坐标示例</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const geoCoord = videoMapView.pixelCoordToGeoCoord(new Point({ coordinates: [0, 0] }))
   */
  pixelCoordToGeoCoord(pixelCoord) {
    const pixel = new Point({
      coordinates: [
        pixelCoord.coordinates[0] / this._videoPixelScale,
        pixelCoord.coordinates[1] / this._videoPixelScale
      ]
    })
    const geoCoord = this._videoPixelCoordTransforms.pixelCoordToGeoCoord(pixel)
    return geoCoord
  }

  /**
   * 地理坐标转像素坐标,得到的像素坐标会受视频缩放影响
   * @function VideoMapView.prototype.geoCoordToPixelCoord
   * @param {Point} geoCoord 地理坐标,经纬度表示
   * @returns {Point} 像素坐标,单位为像素值
   * @example <caption><h7 id = geoCoordToPixelCoord>地理坐标转像素坐标示例</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const pixelCoord = videoMapView.geoCoordToPixelCoord(new Point({ coordinates: [longitude, latitude] }))
   */
  geoCoordToPixelCoord(geoCoord) {
    const pixelCoord =
      this._videoPixelCoordTransforms.geoCoordToPixelCoord(geoCoord)
    const pixelCoordPoint = new Point({
      coordinates: [
        pixelCoord.coordinates[0] * this._videoPixelScale,
        pixelCoord.coordinates[1] * this._videoPixelScale
      ]
    })
    return pixelCoordPoint
  }

  _bindMouseEventToVideoMapViewContainer() {}

  _modifyVideoMapViewPosition() {}

  /**
   * 控制绘制层是否响应鼠标事件
   * @private
   * @param {String} value
   */
  _enabledMouseEvent(value) {
    if (typeof value === 'string') {
      this._fabricContainer.style.pointerEvents = value
    }
  }

  setWidth(value) {
    this._videoMapViewContainer.style.width = `${value}px`
    const videoWidth = this._videoMapLayer.width
    const videoHeight = this._videoMapLayer.height
    const { width, height, left, top, scale } = adjustChildSizeToParent(
      videoWidth,
      videoHeight,
      this._videoMapViewContainer
    )
    this._videoPixelScale = scale

    this._fabricContainer.style.top = `${top}px`
    this._fabricContainer.style.left = `${left}px`
    this._fabricContainer.style.width = `${width}px`
    this._fabricContainer.style.height = `${height}px`
  }

  setHeight(value) {
    this._videoMapViewContainer.style.height = `${value}px`
    const videoWidth = this._videoMapLayer.width
    const videoHeight = this._videoMapLayer.height
    const { width, height, left, top, scale } = adjustChildSizeToParent(
      videoWidth,
      videoHeight,
      this._videoMapViewContainer
    )
    this._videoPixelScale = scale

    this._fabricContainer.style.top = `${top}px`
    this._fabricContainer.style.left = `${left}px`
    this._fabricContainer.style.width = `${width}px`
    this._fabricContainer.style.height = `${height}px`
  }

  /**
   * @description 添加图层
   * @param {VideoMapLayer} videoMapLayer
   * @private
   */
  _addLayer(videoMapLayer) {
    this._videoMapLayer = videoMapLayer
    // 内部创建时videoHtml才有
    const videoHtml = videoMapLayer.videoHtml
    const videoWidth = videoMapLayer.width
    const videoHeight = videoMapLayer.height
    // 针对传入的video位置还需要考虑
    const { width, height, left, top, scale } = adjustChildSizeToParent(
      videoWidth,
      videoHeight,
      this._videoMapViewContainer
    )
    this._videoPixelScale = scale
    // console.log(this._videoPixelScale)

    if (videoHtml) {
      videoHtml.style.left = `${left}px`
      videoHtml.style.top = `${top}px`
      videoHtml.style.width = `${width}px`
      videoHtml.style.height = `${height}px`
    }

    if (!this._fabricCanvas) {
      this._fabricContainer = document.createElement('div')
      this._fabricContainer.id = 'fabricContainer'
      this._fabricContainer.style.position = 'absolute'
      this._fabricContainer.style.top = `${top}px`
      this._fabricContainer.style.left = `${left}px`
      this._fabricContainer.style.width = `${width}px`
      this._fabricContainer.style.height = `${height}px`
      this._videoMapViewContainer.appendChild(this._fabricContainer)

      this._fabricContainer.style.pointerEvents = 'none'

      const canvas = document.createElement('canvas')
      canvas.id = 'fabricCanvas'
      canvas.style.width = '100%'
      canvas.style.height = '100%'
      this._fabricContainer.appendChild(canvas)
      this._fabricCanvas = new fabric.Canvas(canvas.id)
      this._fabricCanvas.setWidth(width)
      this._fabricCanvas.setHeight(height)

      this._initViewEvent()
    }
  }

  _addPixelCoords(geometry) {
    if (!geometry._pixelCoords) {
      if (geometry.coordinates[0] instanceof Array) {
        const _pixelCoords = []
        this._traversalCoordinates(geometry.coordinates, _pixelCoords)
        geometry._pixelCoords = _pixelCoords
      } else {
        const point = new Point({
          coordinates: [geometry.coordinates[0], geometry.coordinates[1]]
        })
        const _pixelCoord = this.geoCoordToPixelCoord(point)
        geometry._pixelCoords = {
          x: _pixelCoord.coordinates[0],
          y: _pixelCoord.coordinates[1]
        }
      }
    }
  }

  _traversalCoordinates(coordinates, pixelCoords) {
    if (coordinates[0] instanceof Array) {
      coordinates.forEach((coord) => {
        this._traversalCoordinates(coord, pixelCoords)
      })
    } else {
      const point = new Point({
        coordinates: [coordinates[0], coordinates[1]]
      })
      const pixelCoord = this.geoCoordToPixelCoord(point)
      pixelCoords.push({
        x: pixelCoord.coordinates[0],
        y: pixelCoord.coordinates[1]
      })
    }
  }

  /**
   * 绘制一个graphic
   * @private
   * @param {Feature} graphic
   */
  _drawGraphic(graphic) {
    this._addPixelCoords(graphic.geometry)
    if (graphic.geometry.type === GeometryType.point) {
      const symbol = graphic.symbol
      const radius = symbol.size
      const color = symbol.color.toCssRGBString()
      const geometry = graphic.geometry
      const pointGraphic = new fabric.Circle({
        left: geometry._pixelCoords.x - radius,
        top: geometry._pixelCoords.y - radius,
        radius,
        fill: color,
        stroke: symbol.outline.color.toCssRGBString(),
        strokeWidth: symbol.outline.width,
        opacity: symbol.color.opacity,
        selectable: false
      })
      this._fabricCanvas.add(pointGraphic)
      this._points.set(graphic.id, pointGraphic)
    } else if (graphic.geometry.type === GeometryType.lineString) {
      const symbol = graphic.symbol
      const width = symbol.width
      const color = symbol.color.toCssRGBString()
      const geometry = graphic.geometry
      const lineGraphic = new fabric.Polyline(geometry._pixelCoords, {
        fill: 'transparent',
        // opacity: symbol.color.opacity,
        stroke: color,
        strokeWidth: width,
        selectable: false
      })
      this._fabricCanvas.add(lineGraphic)
      this._lines.set(graphic.id, lineGraphic)
    } else if (graphic.geometry.type === GeometryType.polygon) {
      const symbol = graphic.symbol
      const color = symbol.color.toCssRGBString()
      const strokeWidth = symbol.outline.width
      const geometry = graphic.geometry
      const left = geometry._pixelCoords[0].x
      const top = geometry._pixelCoords[0].y
      const points = geometry._pixelCoords.map((value) => ({
        x: value.x - left,
        y: value.y - top
      }))
      const polygonGraphic = new fabric.Polygon(
        [
          { x: 0, y: 0 },
          { x: 0, y: 0 },
          { x: 0, y: 0 }
        ],
        {
          left,
          top,
          fill: color,
          strokeWidth,
          stroke: symbol.outline.color.toCssRGBString(),
          opacity: symbol.color.opacity,
          cornerColor: 'blue',
          objectCaching: false,
          selectable: false
        }
      )
      this._fabricCanvas.add(polygonGraphic)
      polygonGraphic.set({ points })
      this._fabricCanvas.renderAll()
      this._polygons.set(graphic.id, polygonGraphic)
    }
  }

  _updateGraphic(graphic) {
    this._addPixelCoords(graphic.geometry)
    if (graphic.geometry.type === GeometryType.lineString) {
      this._fabricCanvas.remove(this._lines.get(graphic.id))
      this._lines.delete(graphic.id)

      const symbol = graphic.symbol
      const width = symbol.width
      const color = symbol.color.toCssRGBString()
      /** @type {LineString} */
      const geometry = graphic.geometry
      if (geometry._pixelCoords.length < 2) {
        return
      }
      const lineGraphic = new fabric.Polyline(geometry._pixelCoords, {
        fill: 'transparent',
        stroke: color,
        strokeWidth: width,
        selectable: false
      })

      this._tempFabrics.forEach((geo) => {
        this._fabricCanvas.remove(geo)
      })
      this._tempFabrics = []

      if (graphic.isShowSegmentLength) {
        for (let i = 1; i < geometry.coordinates.length; i++) {
          const line = new LineString({
            coordinates: [geometry.coordinates[i - 1], geometry.coordinates[i]]
          })

          const length = GeometryEngine.geodesicLength(line)

          const text = `${length.toFixed(2)}m`
          const textStyle = graphic._textStyle
          const textColor = textStyle.color.toCssRGBString()
          const textpos = {
            x:
              (geometry._pixelCoords[i].x + geometry._pixelCoords[i - 1].x) / 2,
            y: (geometry._pixelCoords[i].y + geometry._pixelCoords[i - 1].y) / 2
          }
          const textbox = new fabric.Textbox(text, {
            left: textpos.x,
            top: textpos.y,
            fill: textColor,
            width: 50,
            fontSize: textStyle.font.size,
            selectable: false
          })
          this._tempFabrics.push(textbox)
          this._fabricCanvas.add(textbox)
        }
      }
      this._fabricCanvas.add(lineGraphic)
      this._lines.set(graphic.id, lineGraphic)
    } else if (graphic.geometry.type === GeometryType.polygon) {
      const polygonGraphic = this._polygons.get(graphic.id)
      if (polygonGraphic) {
        /** @type {Polygon} */
        const geometry = graphic.geometry
        const left = geometry._pixelCoords[0].x
        const top = geometry._pixelCoords[0].y
        const points = geometry._pixelCoords.map((value) => ({
          x: value.x - left,
          y: value.y - top
        }))
        polygonGraphic.set({ points })
        this._fabricCanvas.renderAll()
        if (geometry._pixelCoords.length < 4) {
          return
        }

        this._tempFabrics.forEach((geo) => {
          this._fabricCanvas.remove(geo)
        })
        this._tempFabrics = []

        if (graphic.isShowArea) {
          const center = geometry._pixelCoords.map((value) => [
            value.x,
            value.y
          ])
          const centerGeo = new Polygon({
            coordinates: [center]
          })

          const area = GeometryEngine.planarArea(centerGeo)
          const text = `${area.toFixed(2)}㎡`
          const textStyle = graphic._textStyle
          const textColor = textStyle.color.toCssRGBString()
          const textbox = new fabric.Textbox(text, {
            left: centerGeo.centroid.coordinates[0] - 15,
            top: centerGeo.centroid.coordinates[1] - 7.5,
            fill: textColor,
            width: 50,
            fontSize: textStyle.font.size,
            selectable: false
          })
          this._tempFabrics.push(textbox)
          this._fabricCanvas.add(textbox)
          this._fabricCanvas.bringToFront(textbox)
          this._fabricCanvas.renderAll()
        }
        // console.log(geometry.calculateArea())
      }
      // this._fabricCanvas.remove(this._polygons.get(graphic.id))
      // this._polygons.delete(graphic.id)

      // /** @type {SimpleFillSymbol} */
      // const symbol = graphic.symbol
      // const color = symbol.color.toCssRGBString()
      // const strokeWidth = symbol.outline.width
      // const geometry = graphic.geometry
      // const left = geometry._pixelCoords[0].x
      // const top = geometry._pixelCoords[0].y
      // const points = geometry._pixelCoords.map(value => ({ x: value.x - left, y: value.y - top }))
      // console.log(points)
      // const polygonGraphic = new fabric.Polygon(points, {
      //   left: left,
      //   top: top,
      //   fill: color,
      //   strokeWidth: strokeWidth,
      //   stroke: symbol.outline.color.toCssRGBString(),
      //   cornerColor: 'blue',
      //   objectCaching: false
      // })
      // this._fabricCanvas.add(polygonGraphic);
      // this._polygons.set(graphic.id, polygonGraphic)
    }
  }

  /**
   * @private
   * @param {Geometry} graphic
   */
  _deleteGraphic(graphic) {
    let geometry
    if (this._points.has(graphic.id)) {
      geometry = this._points.get(graphic.id)
    } else if (this._lines.has(graphic.id)) {
      geometry = this._lines.get(graphic.id)
    } else if (this._polygons.has(graphic.id)) {
      geometry = this._polygons.get(graphic.id)
    }

    this._tempFabrics.forEach((geo) => {
      this._fabricCanvas.remove(geo)
    })
    this._tempFabrics = []

    if (geometry) {
      this._fabricCanvas.remove(geometry)
    }
  }

  /**
   * 初始化视频地图事件
   * @private
   */
  _initViewEvent() {
    if (!this._fabricCanvas) {
      return
    }
    // 视频地图加载完毕事件
    this.fire(ViewEventType.loaded, {})

    // 注册点击事件
    this._fabricCanvas.on(fbMouseEventType.mouseUp, (options) => {
      if (options.isClick) {
        options.type = ViewEventType.click
        this.fire(ViewEventType.click, options)
      }
    })

    // 注册鼠标抬起事件
    this._fabricCanvas.on(fbMouseEventType.mouseUp, (options) => {
      options.type = ViewEventType.mouseUp
      this.fire(ViewEventType.mouseUp, options)
    })

    // 注册鼠标按下事件
    this._fabricCanvas.on(fbMouseEventType.mouseDown, (options) => {
      options.type = ViewEventType.mouseDown
      this.fire(ViewEventType.mouseDown, options)
    })

    // 注册鼠标移动事件
    this._fabricCanvas.on(fbMouseEventType.mouseMove, (options) => {
      options.type = ViewEventType.mouseMove
      this.fire(ViewEventType.mouseMove, options)
    })

    // 注册双击事件
    this._fabricCanvas.on(fbMouseEventType.doubleClick, (options) => {
      options.type = ViewEventType.doubleClick
      this.fire(ViewEventType.doubleClick, options)
    })

    // 注册鼠标移出事件
    this._fabricCanvas.on(fbMouseEventType.mouseout, (options) => {
      options.type = ViewEventType.mouseOut
      this.fire(ViewEventType.mouseOut, options)
    })

    // 注册鼠标移入事件
    this._fabricCanvas.on(fbMouseEventType.mouseover, (options) => {
      options.type = ViewEventType.mouseOver
      this.fire(ViewEventType.mouseOver, options)
    })

    // 注册鼠标右键按下事件

    // 注册zoom变化事件
    // this._fabricCanvas.on(fbMouseEventType.zoom, (options) => {
    //   this.fire(ViewEventType.zoom, options)
    // })

    // 注册地图移动事件

    // 注册地图大小变化事件
  }
}
export default VideoMapView
Zondy.VideoMapView = VideoMapView
构造函数
成员变量
方法
事件