angular中echarts通过getZr()获取点击空白处与点击markpoint事件

1,076 阅读4分钟

需求

设计说要实现这样一个折线图,点击每个折点都可以触发查看轨迹的事件,点击【越界预警】的标标也可以触发查看轨迹的事件。

实现

折线图

使用echarts实现折线图,使用echarts的markpoint来标记那些标标:

let option = {
  tooltip: {
    trigger: 'axis',
    axisPointer: { // 坐标轴指示器配置项。
      type: 'shadow', // 'line' 直线指示器  'shadow' 阴影指示器  'none' 无指示器  'cross' 十字准星指示器。
    },
  }],
  color: ['#cd901e'],
  grid: {
    x: 55,
    y: 55,
    x2: 35,
    y2: 30
  },
  xAxis: [{
    type: 'category',
    data: times//整理好的显示在x轴的时间线,一维数组,注意顺序和y轴一致
  }],
  yAxis: [{
    type: 'value',
    splitLine: {
      show: true,
    }
  }],
  series: [
    {
      symbol: 'circle',
      name: '速度',
      data: speeds,//整理好的显示在y轴的速度,一维数组,注意顺序和x轴一致
      type: 'line',
      markPoint: {
        symbol: this.alartIcon, // 使用自定义SVG图形(这里是直接把png转为base64写代码里了)
        symbolSize: [83, 39], // 设置标记点的大小
        symbolOffset: [40, -25],
        data: alarts,//标记点的值与坐标,对象数组,类似这样[{name: 【time】,coord: [【time】, 【speed】]}]
        triggerEvent: true
      },
      // 选中状态样式
      select: {
        itemStyle: {
          color: 'orange',
          borderColor: 'orange',
          borderWidth: 8
        }
      },
      selectedMode: "single"
    }
  ]
}
this.lineChart.setOption(option)

点击事件

折线图实现了之后就开始弄点击事件,最开始没想那么多,官网上有监听onclick的方法,就直接拿来用了,但是体验非常糟糕,因为折线图的点击事件必须要点击到那个一丁丁点大的小圆点上才能触发,超级无敌难点到,不敢想象用户去点却点不到的样子,后来选择了官方文档没有说但是大家都在用的getZr()方法来监听点击空白区域的事件。

最终的监听点击事件的代码:

this.lineChart.getZr().off('click')//先解绑点击事件
// this.lineChart.off('click')//先解绑点击事件
const that = this
//点击坐标轴等其他地方的事件(因为折线图的那个点太难点到了,改为监听点击空白部分)
//如果加上this.lineChart.getZr().off('click')的话就绑不上普通的onclick事件,改为全部都通过getZr()来监听
this.lineChart.getZr().on('click', async function (params: any) {
  const pointInPixel = [params.offsetX, params.offsetY]
  if (
    that.lineChart.containPixel('grid', pointInPixel)//点击的坐标在折线图内
    && !(params.target && params.target.style.hasOwnProperty('image')//且点到的东西不是markPoint
         && params.target.style.image)
  ) {
    console.log('click了其他地方一次!')
    // 获取到点击的 x轴 下标  转换为逻辑坐标
    const xIndex = that.lineChart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)[0]
    /*…………………………………………………………一些业务逻辑代码……………………………………………………*/
    that.lineChart.dispatchAction({
      type: 'select',
      dataIndex: xIndex
    })
  }
  else if (
    that.lineChart.containPixel('grid', pointInPixel)//点击的坐标在折线图内
    && (params.target && params.target.style.hasOwnProperty('image')//且点到的东西是markPoint
        && params.target.style.image)
  ) {
    // console.log('预警时间:', params.target.__ec_inner_6.dataModel._data._idList[0])       
    //__ec_inner_6这个字段名不确定,有可能变成__ec_inner_5,还变过__ec_inner_8,遍历字段名查找正确的字段
    let targetKey = ''
    for (let key in params.target) {
      if (key.includes('__ec_inner_')) {
        targetKey = key
        break
      }
    }
    const targetTime = params.target[targetKey].dataModel._data._idList[0]
    const clickTime = new Date(targetTime)
    /*…………………………………………………………一些业务逻辑代码……………………………………………………*/
    //获取高亮节点的值
    const coord = params.target[targetKey].dataModel.__hostSeries.option.markPoint.data[0].coord[0]
    that.lineChart.dispatchAction({
      type: 'select',
      name: coord
    })
  }
});

踩过的坑

这里说一下当时踩过的坑:

getZr()无法直接监听点击markpoint

因为getZr()方法在官方文档里没有,所以只能按照网上搜到的来,网上的是通过获取点击的x轴下标转换为逻辑坐标的方式来监听点击的是哪一个,但是我们的设计中有要点击markpoint触发事件的地方,无法通过x轴下标转换为逻辑坐标的方式获取到点击的到底是哪一个markpoint。当时的解决方法是同时使用this.lineChart.getZr().on('click')this.lineChart.on('click'),一个负责监听点击空白区域,另一个负责监听点击markpoint,在各自的事件中判断是否执行后续代码,但这么做又埋了新的坑,后面马上说到。

忘记清除点击事件

最开始忘记清除点击事件,导致每执行一次方法就会绑一次监听点击事件,代码整个都乱掉了。后来加上了this.lineChart.getZr().off('click')this.lineChart.off('click'),重复绑定的问题解决了,甚至onclick直接绑不上了……经过反复测试,应该是只要写了this.lineChart.getZr().off('click')这一句,this.lineChart.on('click')就绑不上了,估计应该是echarts自己埋的坑。

寻觅getZr()方法点击markpoint触发的事件中的属性信息

实在是没有办法了,又倒回来寻找getZr()方法中的markpoint的属性信息,把getZr()方法的param打印出来瞅瞅:

好像找到了耶!!!!!藏得太深了!!!!!!有种王子拿着水晶鞋到处找灰姑娘的感觉哈哈哈哈哈哈哈!

params.target.__ec_inner_1.dataModel._data._idList[0]打出来瞅瞅。

哦豁,报错了,报undefined没有_data的属性,怎么可能,明明我刚才还看到了的!再打,发现__ec_inner_1这个字段会变,应该是【_ec_inner】这些字符加上一个随机数。

那就遍历一下找到正确的字段名然后再去找后续的值吧。

//__ec_inner_1这个字段名不确定,有可能变成__ec_inner_5,还变过__ec_inner_8,遍历字段名查找正确的字段
let targetKey = ''
for (let key in params.target) {
  if (key.includes('__ec_inner_')) {
    targetKey = key
    break
  }
}
const targetTime = params.target[targetKey].dataModel._data._idList[0]

然后就成功了!!能通过getZr()方法获取到点击的markpoint的属性了!