背景

使用echarts图表时,需要将tooltip提示框中的内容设置一些样式或者添加一个链接可以点击,这种需求在pc端很好实现只需要在tooltip配置项的formatter函数中返回拼接的html模板字符串即可。但是在小程序中此种做法会失效因为小程序端无法解析标签元素,也就无法添加一些点击事件。因此就需要自定义tooltip提示框才可解决此问题。

自定义tooltip提示框

思路

  • 需要在页面中准备一个tooltip提示框容器,里面用来编写echarts图表提示内容。
  • 将echarts图表和自定义的tooltip提示框放入同一个父容器中,父容器设置相对定位,tooltip提示框设置绝对定位。
  • 利用echarts图表的点击事件获取点击图表位置,然后动态的将自定义tooltip提示框定位到点击位置附近。
  • tooltip提示框默认隐藏,当点击图表对应位置时将其显示。

实际应用场景

有一个时序演化图,点击对应位置的柱状图,需要给出某段柱状图信息提示,同时提示框中有个跳转链接可以跳转到指定位置。在这种场景下就需要自定义tooltip提示框来实现。

实现步骤

准备一个自定义tooltip提示框容器

  • 实现代码
                <scroll-view style="width: 750rpx;" scroll-x="true" bindscroll='scrollCanvas'>
                    <view style="width:1800rpx;height: 880rpx;" class="echarts-wrap">
                        <ec-canvas id="mychart-dom-bar" style="transform: translateX(-{{canvasLen}}px);" canvas-id="mychart-bar" ec="{{ecState}}">
                        </ec-canvas>
                                            <view hidden="{{!showTooltip}}" class="tooltipContainer" style="left:{{tooptipLeft}}px;top:{{tooptipTop}}px;">
                    </view>
                </scroll-view>
  • 解释
    echarts-wrap类position需要设置为relative,提示框容器需要设置绝对定位,showTooltip变量用来控制容器的显示和隐藏,tooptipLeft和tooptipTop属性用来设置提示框容器的定位位置。

为图表添加点击事件控制自定义提示框显示与隐藏

  • 描述
    为echarts图表添加点击事件,当点击图表中的某段柱状图时获取点击位置x,y坐标然后将提示框设为显示同时根据点击位置坐标动态设置提示框绝对定位位置。
  • 实现代码
        ecState: {
            onInit: function (canvas, width, height) {
                chart = echarts.init(canvas, null, {
                    width: width,
                    height: height,
                    devicePixelRatio: dpr
                });
                chartBar=chart
                canvas.setChart(chart);
                chart.setOption(chartData3, true);
                chart.on('click', (e) => {    
                    that.debounce(that.handleBarClick,e,300)
                  })
                  chart.getZr().on('mouseout',(e)=>{
                      console.log('鼠标离开了')
                      that.setData({
                        showTooltip:false
                      })
                      if(mytimer!=null){
                          clearTimeout(mytimer)
                      }
                  })
                 
                return chart;
            }
        },
  • 解释
    在echarts初始化配置项中为echarts实例添加手指点击和离开事件,对自定义tooltip提示框的显示和位置进行处理,处理函数为handleBarClick。
  • 处理tooltip位置和显隐的关键代码
  that.setData({
         showTooltip:true
})
 let tooltipContainerWidth=''
        let tooltipContainerHeight=''
        wx
        .createSelectorQuery()
        .select(".tooltipContainer")
        .boundingClientRect(rect => { })
        .exec(res => {
          if (res[0] != null) {
            tooltipContainerWidth = res[0].width;
            tooltipContainerHeight = res[0].height;
            console.log(tooltipContainerHeight)
            let y=e.event.offsetY;
            console.log(x,y)
            if(y<tooltipContainerHeight){
                that.setData({
                    tooptipTop: y + 10
                })
            }else{
                that.setData({
                    tooptipTop: y - tooltipContainerHeight - 10
                })
            }
          }
        });
      // 获取左侧坐标
      let x = e.event.offsetX;
      if (x < tooltipContainerWidth) {
          that.setData({
            tooptipLeft:x + 20
          })
      } else {
          that.setData({
            tooptipLeft : x - tooltipContainerWidth- 120
          })
      }

该处理函数中可以获取点击事件参数e中的一些属性用于发起ajax向后端请求数据渲染与之关联的图表

注意踩坑点

1.this指向问题

由于echarts的点击事件是在data中添加的,此时是无法获取小程序全局this的,因此也就无法调用this.setData()方法,需要在生命周期onReady中获取this:
先定义一个全局变量let that;在onReady中执行let that = this
2.自定义tooltip提示框频繁切换性能问题

  • 介绍
    当频繁点击柱状图时,自定义tooltip提示框会频繁切换和隐藏显示,而此时提示框的显示与隐藏和位置定位都需要调用this.setData()方法来设定,但是小程序中短时间内频繁使用this.setData方法会造成页面卡顿的问题,这样就会造成提示框不能及时出现错乱最终页面卡死的现象。
  • 解决方案
    (1)利用防抖的方式控制触发处理自定义提示框显示与隐藏定位的处理函数执行频率从而减少this.setData的调用频率。

(2)控制提示框的显示与隐藏使用hidden属性不要使用wx:if属性

  • 实现
    防抖函数的定义
    // 防抖处理
    debounce(fn,e,wait){
        if(mytimer!=null){
            clearTimeout(mytimer)
          }
              mytimer=setTimeout(function(){
                  fn(e) 
            },wait)
    }

echarts图表点击事件中调用防抖函数

                chart.on('click', (e) => {    
                    that.debounce(that.handleBarClick,e,300)
                  })
  • 解释
    这里的mytimer是延时器id需要定义为全局变量,在页面销毁和手指离开是销毁掉,防抖延时设置太大或太小都会影响tooltip提示框性能,尤其注意防抖函数中传入的fn参数:具体功能函数不能带()否则会立即执行达不到防抖效果。

3.自定义tooltip弹框兼容双端问题

  • 描述
    自定义弹框在苹果手机部分机型会出现弹框被echarts图表覆盖的问题。
  • 解决方案
    这种现象是层级问题引起的,需要使用cover-view组件去定义弹框可解决echarts层级的问题,经过测试发现cover-view组件在安卓机上会导致定位出现问题,但是在苹果机上对cover-view动态绝对定位没有问题。而使用view组件定义弹窗在安卓机上层级是在echarts之上的,在苹果机上层级则会出问题。经过以上测试总结一套方案:判断手机机型是android还是ios,如果是android系统则使用view组件定义弹窗,如果是ios系统则使用cover-view组件定义弹窗。
  • 实现
        let res = wx.getSystemInfoSync();
        this.setData({
            platform:res.platform
        })

在onReady钩子函数中判断系统类型并写入到data中,wxml中根据手机类型用view或者cover-view创建弹框
备选:
对于层级问题其实使用cover-view即可,只不过由于echarts图表被scroll-view包裹可以滚动,因此动态对提示框绝对定位会异常,这里前端秀姐给出了一个方案:使用bindscroll监听scroll-view滚动位置,然后动态计算提示框绝对定位位置时把滚动条滚动距离计算在内。此方案从理论上是可行的感兴趣可以尝试一下。

拓展

点击tooltip提示框中某个链接如何跳转到指定位置

  • 方案
    这里使用scroll-view组件内置的功能实现跳转到指定位置。
  • 代码
    为scroll-view绑定跳转属性
   <scroll-view scroll-y="{{true}}" style="height: 100vh;" scroll-with-animation="{{true}}" scroll-into-view="{{redictId}}">
<view id="one"></view>
</scroll-view>

触发跳转事件时执行跳转代码

handleClick(){
this.setData({
redictId:'one'
})
}
  • 解释
    此种方式必须要在外层包裹一个scroll-view组件方能生效

echarts配置更改后保留当前图表操作状态

  • 描述
    当小程序echarts图表中某些配置变动后需要调用chart.setOption(option,true)方法才可以更新变动后的数据到图表中,但是会

造成图表直接重新渲染,这样图表中有缩放操作就会出现问题,当用户缩放图表后更新数据,echarts图表又重新渲染并不是缩放后的状态体验感很差。

  • 解决
    这种问题在更新echarts图表配置时执行以下代码即可:
chartBar.setOption(chartData3, {lazyUpdate:true});
  • 解释
    这里设置lazyUpdate属性为true表示将最新数据与旧数据进行合并不立即更新图表

setOption其他配置项可参考这里

参考

小程序echarts 自定义tooltip提示框及显示隐藏兼容苹果iOS
ECharts 中的事件和行为

最后修改:2022 年 11 月 09 日
如果觉得我的文章对你有用,请随意赞赏