前些天有读者问,ECharts 的提示框(tooltip)内,能不能添加一个饼图?
我之前倒是看到过用饼图作为散点图数据点的例子,感觉应该有办法……但是提示框和那个不太一样,估计需要研究一下——所以先回复说,这个之前没有尝试过,我有空试一下。
实现思路(一段曲折的过程)
当天晚上有事,没来及看,转天上班途中,先看了眼配置项手册,在确认没有现成功能可用的同时,注意到了一个关键点:提示框是一个 DOM 节点,也就是有办法作为 ECharts 的容器。
浮层的渲染模式,默认以 'html 即额外的 DOM 节点展示 tooltip;此外还可以设置为 'richText' 表示以富文本的形式渲染,渲染的结果在图表对应的 Canvas 中(目前 SVG 尚未支持富文本),这对于一些没有 DOM 的环境(如微信小程序)有更好的支持。
「ECharts 配置项手册」
https://echarts.apache.org/zh/option.html#tooltip.renderMode
另外,其实从「tooltip.formatter」默认的换行符是「
」而不是「」这一个特点上,也可以推测得到的。
有了这个关键点,大体的思路就有了:
- 通过回调函数返回一个带 id 属性的 div,比如
- 以这个 div 为容器,初始化 ECharts 实例;
- 根据触发提示框的 params 属性,准备相应的饼图配置项,渲染对应的饼图
但是还存在一个问题需要解决,这个问题有 2 个难点:
- 在提示框首次弹出之前,带 id 的 div(回调函数返回的「饼图容器」)是不存在的,而且每次触发提示框显示/移动,这个 div 会被覆盖,也就是渲染好的「canvas」元素会消失——所以每次触发 tooltip,都需要重新渲染饼图;
- 从ECharts API 看,提示框的显示、隐藏,并没有事件可供监听,也没办法把这个动作加到「tooltip.formatter」的回调函数中,因为「问题 1」的覆盖,发生在函数返回结果之后。
为了解决这个问题,我想到了 2 种尝试的思路:
- 通过监听「events.finished」事件,主图表渲染动作完成后,如果存在提示框饼图的容器(div),则触发饼图的重新渲染;
- 通过回调函数的嵌套,在「tooltip.formatter」的回调函数中,再嵌一个 callback,加一定延时后渲染饼图。
然后我就开始尝试有可能最简单的第 1 种,结果发现提示框中的饼图时有时无……然后经过各种分析,打点、测试、翻源码(其实没咋看懂= =b),明白了大致原因:
提示框(tooltip)的移动,不触发「events.finished」事件,也就是当提示框指示的数据项/数据轴没发生改变时,提示框发生了「move」,而不是先「hide」再「show」
- 「tooltip.formatter」的回调函数执行,提示框层(div)的位置变了,提示框里的饼图没了(回调函数 return 了新的「饼图容器」);
- 「events.finished」事件没有发生,新的饼图没有补上……
第 1 种思路尝试失败-_-b
然后开始试图尝试第 2 种,但作为一个先接触 ECharts 后接触 JavaScript 的 JS 小白,我毫无悬念地、稀里糊涂地失败了
各位前端大神们,有兴趣的话,可以亲自尝试一下,我就不班门弄斧了……
被 callback 虐了半天的自己,郁闷了 10 多分钟……然后终于,自暴自弃地,想到了简单暴力的第 3 种方法:
- 通过「setInterval」每隔一段时间(比如 10 毫秒),判断是否存在提示框饼图的空容器(div),如存在则触发饼图的重新渲染。
// 准备一个饼图的 optionlet pieOption = { series: { type: "pie", labelLine: { show: false }, label: { show: false } }, animation: false};option = { title: { text: '读者提问,提示框能否显示饼图' }, xAxis: { data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] }, yAxis: {}, tooltip: { trigger: 'axis', formatter: params => { // 根据触发提示框的数据,准备饼图数据 let pieData = []; for (let i in params) { pieData.push({ name: params[i].seriesName, value: params[i].value, color: params[i].color }); } // 将饼图数据存入饼图 option.series.data pieOption.series.data = pieData; return `${params[0].axisValue}
就是这段,每隔 10 毫秒,检测一次提示框饼图容器的内部是否为空,如果是,就重新渲染一遍饼图,这次算是非常不优雅的成功了-_-o
点击「了解更多」查看 ECharts Gallery 例子