最近接的需求里有个漏斗图,对比了下图表库的漏斗,长得有点不大一样,虽然一样有漏斗,但是剩余部分看着图表库没法组合实现,于是想想自己简单实现一个。
右边部分其实很好简单,主要是左边的漏斗图实现,漏斗图其实就是几个等腰梯形拼装起来,如果是用div+css,想到的是利用border,那么组装起来又稍微麻烦的,干脆用svg来做:
polygon
就可以直接画等腰梯形- 固定坐标的方式使得拼接也没多大问题
那么怎么取梯形的坐标点呢?和数据有什么样的规律?
通常来说漏斗都是上宽下窄,每一个梯形的上下边长,其实对应这每一块节点数据的对比。
如上图,假设即为漏斗第一格数据, 那么AB边长即为漏斗的最大宽度,也映射着第一格数据,而底边CD变长则映射为第二格数据。
那么两条边的比例即可确定了,假设漏斗数据为 a1,a2,a3,a4
那么 AB : CD = a1 : a2
A点坐标为起点,即(0, 0), 假设漏斗图宽为allWidth,那么B点坐标应该就是(allWidth, 0)
C点坐标即计算(CG, AG):
- 漏斗块我们固定高度,所以AG长固定,假定为h
- CG = (AB - CD)/2; 转为计算数据比例则是 CG = allWidth * (1 - a2/a1)/2
D点坐标利用等腰梯形对称特性,坐标为(allWidth - CG, h)
仔细一想,每个梯形的顶边坐标即为上一个梯形的底边坐标,而底边坐标只要计算出差值即CG边长,即可得出左右两坐标的横坐标。
基于这些总结的关系,假设我们的数据是这样的 list = [{number: 100}, {number: 60}, {number: 50}, {number: 10}],我们按下面代码来取出要画的四个polygon的四个顶点坐标 (0,0)为起点
// BASE_WIDTH 为实际边宽, PER_HEIGHT为梯形高
// 取出顶边数据(对应BASE_WIDTH)
const allWidth = list[0].number;
// 先计算每一个差值
const deltas = list
.slice(1)// 排除掉第一个数据即基准值
.concat(list[list.length - 1]) // 结尾补充一个,最后一个梯形没有下一个比较,所以是矩形
.map((item) => {
return ((1 - item.number / allWidth) / 2) * BASE_WIDTH;
});
// 遍历数据
return list.map((item, index) => {
let points;
// 独立处理第一个数据
if (index === 0) {
points = [
[0, 0],// 左上点
[BASE_WIDTH, 0],// 右上点
[BASE_WIDTH - deltas[index], PER_HEIGHT],// 右下点 横坐标即全长-差值
[deltas[index], PER_HEIGHT], // 左下点 横坐标即为差值
];
} else {
points = [
[deltas[index - 1], PER_HEIGHT * index], // 左上点,即上一个梯形的左下点,纵坐标为一个PER_HEIGHT
[BASE_WIDTH - deltas[index - 1], PER_HEIGHT * index],// 右上点 即上一个梯形的右下点
[BASE_WIDTH - deltas[index], PER_HEIGHT * (index + 1)],// 右下点
[deltas[index], PER_HEIGHT * (index + 1)],// 左下点
];
}
return {
...item,
points,
};
});
至此,就整理出了几个梯形对应的顶点坐标了,接下来只要应用一下svg的polygon即可。
左边部分漏斗完成后,就剩下后边的了,显然也可以用polygon来画,而且还更方便和左边的对齐。
整体效果见codepen >> https://codepen.io/shellphon-...
更新: 转clip-path也可以完成类似的梯形功能,这里补充一下关于漏斗的示例:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。