背景

echarts官网的漏斗图和excel里漏斗图不太一样,记录本人尝试配置漏斗图的过程(次要是两侧label的实现)


echarts官网漏斗图示例

尝试一

思考只用一个漏斗图示例,默认是label的position为left,右侧label基于markLine实现

const chartFactWidth = 400; // 漏斗图的宽度const chartMarginLeft = 200; // 漏斗图间隔左侧间隔const chartCenterX = chartFactWidth / 2 + chartMarginLeft;const minSize = 40;const maxSize = chartFactWidth;const percentSize = (maxSize - minSize) / 100.0;const chartFactValueWidth = maxSize - minSize;option = {  title: {    text: 'Funnel'  },  tooltip: {    trigger: 'item',    formatter: '{a} <br/>{b} : {c}%'  },  toolbox: {    feature: {      dataView: { readOnly: false },      restore: {},      saveAsImage: {}    }  },  series: [    {      name: 'Funnel',      type: 'funnel',      left: chartMarginLeft,      top: '10%',      bottom: '10%',      width: chartFactWidth,      min: 10,      max: 100,      minSize: minSize,      maxSize: maxSize,      sort: 'descending',      gap: 0,      label: {        show: true,        position: 'left',        formatter: function (params) {          return (            '{a|转化率}' + '\n ' + '{b|' + params.data.percent * 100 + '%}'          );        },        rich: {          a: {            color: 'darkgray',            lineHeight: 30          },          b: {            height: 20          }        }      },      labelLayout(params) {        console.log(params.rect.x + params.rect.width, params.rect.width);        return {          x: 50, //params.rect.x ,//50,          y: params.rect.y + params.rect.height / 2,          verticalAlign: 'middle',          align: 'left'        };      },      labelLine: {        lineStyle: {          width: 1,          type: 'solid',          color: '#ccc'        }      },      itemStyle: {        borderWidth: 0      },      emphasis: {        label: {          fontSize: 20        }      },      data: [        {          value: 100,          name: '量1',          percent: Math.round((90 / 100) * 100, 2) / 100        },        {          value: 90,          name: '量2',          percent: Math.round((60 / 90) * 100, 2) / 100        },        {          value: 60,          name: '量3',          percent: Math.round((50 / 60) * 100, 2) / 100        },        {          value: 50,          name: '量4',          percent: Math.round((20 / 50) * 100, 2) / 100        },        {          value: 20,          name: '量5',          percent: Math.round((10 / 50) * 100, 2) / 100        }      ],      markLine: {        lineStyle: {          width: 1,          type: 'solid',          color: '#ccc'        },        symbol: [],        data: [          [            {              name: '量1\n 值100',              x: chartCenterX + minSize / 2 + (maxSize - minSize) / 2,              y: '10%'            },            {              x: '90%',              y: '10%'            }          ],          [            {              name: '量2\n 值90',              x: chartCenterX + minSize / 2 + ((maxSize - minSize) / 2) * 0.9,              y: '26%'            },            {              x: '90%',              y: '26%'            }          ],          [            {              name: '量3\n 值60',              x:                chartCenterX +                minSize / 2 +                ((maxSize - minSize) / 2) * 0.9 * 0.67,              y: '42%'            },            {              x: '90%',              y: '42%'            }          ],          [            {              name: '量4\n 值50',              x:                chartCenterX +                minSize / 2 +                ((maxSize - minSize) / 2) * 0.9 * 0.67 * 0.83,              y: '58%'            },            {              x: '90%',              y: '58%'            }          ],          [            {              name: '量5\n 值20',              x:                chartCenterX +                minSize / 2 +                ((maxSize - minSize) / 2) * 0.9 * 0.67 * 0.83 * 0.4,              y: '74%'            },            {              x: '90%',              y: '74%'            }          ],          [            {              name: '量6\n 值0',              x: chartCenterX + minSize / 2,              y: '90%'            },            {              x: '90%',              y: '90%'            }          ]        ]      }    }  ]};

问题

  • Q1:markLine的起始点要本人计算,目前上述示例起始点计算不精确的;(这个回头空了再看了,各位同学亲们如果感兴趣能够看下)
  • Q2:(思路)如果所需的漏斗图没有gap(图形间距),且右侧的线能够和自身图形色彩一样,那能够间接把中心点作为起始点即可,这个计划也能够用(绝对计划四,少绘制一个漏斗图)。

尝试二

基于上述尝试一,因为每个右上角的点要自行计算,所以左右线实现形式调换。思考只用一个漏斗图示例,默认(右侧)是label的position为rightTop,左侧label基于markLine实现,而后还要自行补充一条右侧markLine。

const topPercent = 5;const heightPercent = 90;const data = [  { value: 80, name: 'Visit' },  { value: 40, name: 'Cart' },  { value: 20, name: 'Order' },  { value: 100, name: 'Show' }];const itemCount = data.length;const itemHeightPercent = heightPercent / itemCount;option = {  tooltip: {    trigger: 'item',    formatter: '{a} <br/>{b} : {c}%'  },  series: [    {      name: 'Funnel',      type: 'funnel',      left: '20%',      top: topPercent + '%',      bottom: 100 - topPercent - heightPercent + '%',      width: '60%',      min: 0,      max: 100,      minSize: '0%',      maxSize: '100%',      sort: 'descending',      gap: 2,      zLevel: 2,      label: {        show: true,        position: 'rightTop',        formatter: '{b}\n{c}'      },      labelLayout: function (params) {        return {          x: '90%',          y: params.rect.y        };      },      labelLine: {        length: 10,        lineStyle: {          width: 1,          type: 'solid'        }      },      markLine: {        symbol: 'none',        lineStyle: {          type: 'solid'        },        label: {          formatter: function (params) {            return params.name;          },          rich: {            a: {              color: 'darkgray',              lineHeight: 30            },            b: {              height: 20            }          }        },        data: [          [            {              x: '50%',              y: topPercent + itemHeightPercent * 0.5 + '%',              name: '{a|转化率}\n{b|' + (80 * 100.0) / 100 + '%}'            },            {              x: '10%',              y: topPercent + itemHeightPercent * 0.5 + '%',              name: '{a|转化率}\n{b|' + (80 * 100.0) / 100 + '%}'            }          ],          [            {              x: '50%',              y: topPercent + itemHeightPercent * (1 + 0.5) + '%',              name: '{a|转化率}\n{b|' + (40 * 100.0) / 80 + '%}'            },            {              x: '10%',              y: topPercent + itemHeightPercent * (1 + 0.5) + '%'            }          ],          [            {              x: '50%',              y: topPercent + itemHeightPercent * (2 + 0.5) + '%',              name: '{a|转化率}\n{b|' + (20 * 100.0) / 40 + '%}'            },            {              x: '10%',              y: topPercent + itemHeightPercent * (2 + 0.5) + '%'            }          ],          [            {              x: '50%',              y: topPercent + itemHeightPercent * (3 + 0.5) + '%',              name: '{a|转化率}\n{b|' + (0 * 100.0) / 20 + '%}'            },            {              x: '10%',              y: topPercent + itemHeightPercent * (3 + 0.5) + '%'            }          ],          [            {              x: '50%',              y: topPercent + itemHeightPercent * 4 + '%',              name: 'ReOrder'            },            {              x: '90%',              y: topPercent + itemHeightPercent * 4 + '%'            }          ]        ]      },      itemStyle: {        borderColor: '#fff',        borderWidth: 1      },      emphasis: {        label: {          fontSize: 20        }      },      data: data    }  ]};

问题

  • Q1:右侧最初一条是markLine,其动画和原series的labelLine的动画是否能对立;
  • Q2:markLine没有看到设置层级的属性,如果markLine能设置在漏斗图的底部,那左侧线完结点设置为中心点的话(如上图)能不便些;
  • Q3:这个计划画线是可行的,思路是先渲染漏斗图和右侧label,在labelLayout里获取理论item的rect的x、y地位,再依据后面获取到的地位数据计算绘制出左侧markLine。(但这个问题解决了,问题1还是得解决才比拟残缺)

尝试三

思考用2个漏斗图实例(重叠),右侧rightTop,最上面那条线用markLine绘制

const data = [  { value: 60, name: 'Visit' },  { value: 40, name: 'Inquiry' },  { value: 20, name: 'Order' },  { value: 80, name: 'Click' },  { value: 100, name: 'Show' }];option = {  title: {    text: 'Funnel'  },  tooltip: {    trigger: 'item',    formatter: '{a} <br/>{b} : {c}%'  },  toolbox: {    feature: {      dataView: { readOnly: false },      restore: {},      saveAsImage: {}    }  },  legend: {    data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']  },  series: [    {      name: 'Funnel',      type: 'funnel',      left: '25%',      top: '10%',      bottom: '10%',      width: '50%',      min: 0,      max: 100,      minSize: '0%',      maxSize: '100%',      sort: 'descending',      gap: 2,      label: {        show: true,        position: 'left'      },      labelLayout: function (params) {        return {          x: '15%'        };      },      labelLine: {        length: 10,        lineStyle: {          width: 1,          type: 'solid'        }      },      itemStyle: {        borderColor: '#fff',        borderWidth: 1      },      emphasis: {        label: {          fontSize: 20        }      },      data: data,      zlevel: 2    },    {      name: 'Funnel',      type: 'funnel',      left: '25%',      top: '10%',      bottom: '10%',      width: '50%',      min: 0,      max: 100,      minSize: '0%',      maxSize: '100%',      sort: 'descending',      gap: 2,      label: {        show: true,        position: 'rightTop',        animation: false,        color: '#666'      },      labelLayout: function (params) {        return {          x: '85%'        };      },      labelLine: {        length: 10,        lineStyle: {          width: 1,          type: 'solid',          color: '#ccc'        }      },      markLine: {        symbol: 'none',        lineStyle: {          type: 'solid',          color: '#ccc'        },        label: {          color: '#333',          animation: false        },        data: [          [            {              x: '50%',              y: '90%',              name: 'ReOrder'            },            {              x: '85%',              y: '90%',              name: 'ReOrder'            }          ]        ]      },      itemStyle: {        borderColor: '#fff',        borderWidth: 1      },      emphasis: {        show: false      },      data: data,      zlevel: 1    }  ]};

问题

  • Q1:与上述”尝试二“一样的问题,markLine绘制的label和line的动画成果 和 hover成果须要调整和lableline的一样

尝试四

思考用2个漏斗图示例(重叠),一个label的position为left,一个为rightTop。右侧有label的漏斗图多一层级,满足右侧line的性能,再暗藏多的那层级的item。

const data1 = [  { value: 60, name: 'Visit' },  { value: 40, name: 'Inquiry' },  { value: 20, name: 'Order' },  { value: 80, name: 'Click' },  { value: 100, name: 'Show' }];const data2 = [  { value: 60, name: 'Visit' },  { value: 40, name: 'Inquiry' },  { value: 20, name: 'Order' },  {    value: 0,    name: 'ReOrder',    itemStyle: {      borderColor: '#fff',      borderWidth: 1,      opacity: 0    },    labelLine: {      opacity: 1    },    label: {      opacity: 1    }  },  { value: 80, name: 'Click' },  { value: 100, name: 'Show' }];const chart1_heightPercent = 80; //漏斗图2高度比例const chart2_heightPercent =  (chart1_heightPercent / data1.length) * data2.length; //漏斗图2高度比例option = {  tooltip: {    trigger: 'item',    formatter: '{a} <br/>{b} : {c}%'  },  series: [    {      name: 'Funnel',      type: 'funnel',      left: '20%',      top: 60,      bottom: 60,      width: '60%',      height: chart1_heightPercent + '%',      min: 0,      max: 100,      minSize: '20%',      maxSize: '100%',      sort: 'descending',      gap: 2,      label: {        show: true,        position: 'left',        formatter: '{b}\n{c}%'      },      labelLayout: function (params) {        return {          x: '10%'        };      },      labelLine: {        length: 60,        lineStyle: {          width: 1,          type: 'solid'        }      },      itemStyle: {        borderColor: '#fff',        borderWidth: 1,        normal: {          opacity: 0.8        }      },      emphasis: {        label: {          fontSize: 20        }      },      data: data1,      zlevel: 2    },    {      name: 'Funnel',      type: 'funnel',      left: '20%',      top: 60,      bottom: 60,      width: '60%',      height: chart2_heightPercent + '%',      min: 0,      max: 100,      minSize: '20%',      maxSize: '100%',      sort: 'descending',      gap: 2,      label: {        show: true,        position: 'rightTop'      },      labelLine: {        length: 80,        lineStyle: {          width: 1,          type: 'solid'        }      },      itemStyle: {        borderColor: '#fff',        borderWidth: 1,        normal: {          opacity: 0.5        }      },      emphasis: {        label: {          fontSize: 20        }      },      data: data2,      zlevel: 1    }  ]};

这个计划绝对比拟“完满”,外围是虚造多一个层级的数据,让n层级漏斗 和 n+1层级漏斗 完满重叠,从而满足item有labelline,item分界线也有lableline。

总结

echarts图表配置项比拟多,大多都要一个个属性去配置测试。

官网漏斗图样例比较简单,且短时间内没有搜到和excel相似的例子,网上也不少人在发问。

本文是我跟着本人的思路去做的尝试,计划四也是基于后面的尝试后,思考能不计算markLine地位、不思考markLine/labelLine的动画统一这些问题,还是抉择了“重叠”的计划,而后再想着怎么天然的多一条标记线,最初思考调整(不同层级数的)两个漏斗图配置参数使其重叠。

这是我第一篇对外输入的在线开发笔记,很开心的。

解决方案个别都不止一种,文中我也有不少还没解决的问题和思路,也想求教各位同学亲们的,如果感兴趣,或者其余想法,欢送大家留言一起分享或探讨的,谢谢!~

文献参考

  • echarts漏斗图的标示线和标识文字对齐
  • echarts官网配置项手册