共计 7665 个字符,预计需要花费 20 分钟才能阅读完成。
背景
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 官网配置项手册