有个页面我估时 3 天(开发 + 联调 + 交互),想着怎么着一天也搞定了。 产品给的设计稿长这样:
半天搞定,等我部署完了以后,效果还不错。
但是,配置很多数据节点的时候,实际效果:
然后改了好几天,我快要被搞疯了,下面说说我的心酸历程。
第一次尝试 echarts graph
官方示例:echarts.apache.org/examples/zh…
后端只返回了 points 和 relations,没有每个点的 x,y 坐标(可能后端开发也不会算),我在前端页面尝试计算每个点的 x,y,尝试让他们均匀分布,试了 N 种算法,最终放弃(花了大半天时间)。
网上不少人也在问,怎么计算 echarts graph 每个点的 x y 坐标
echarts 官方文档有 引力布局(layout=force) ,但是所有点全部揉在一起,效果太差了(下图是官方示例,layout 改成 force)。
series-graph.layout 三个可选属性:
'none'
不采用任何布局,使用节点中提供的 x, y 作为节点的位置。'circular'
采用环形布局,见示例 Les Miserables,布局相关的配置项见 graph.circular'force'
采用力引导布局,见示例 Force,布局相关的配置项见 graph.force
第二次尝试 echarts tree
为啥还用 echarts 呢?我有点自负,自认为 echarts API 已经很熟很熟了(~~~)
确实,这次用树状图,完美的解决了每个点的分布,还有线的问题
但是,给客户演示的时候,说连线要加上箭头!!!! 我尝试了各种 echarts 配置,还是加不了箭头,郁闷死了,github issue 也搜了一把,有人提同样的问题,官方理都不理。
网上也有人问,怎么给 echarts tree 加上箭头
还有另一个更严重的问题,我们这个不是 树状图!!!一个人可能有两个儿子,三个父亲,而不是单向的血缘关系。那就只能是普通的关系图了。
第三次尝试 d3
终于要拿出秘密武器了:D3,宇宙中强大的图形工具。
周末两天没出去玩,没打球,我重新复习了 svg 的基本语法,复习了 d3 的用法,以及研究了几个 d3 扩展。 还找到了一个库:dagre-d3,他基于 dagre,虽然停止维护了:
最终效果是这样的:
加上了 图例,缩放、拖动、tooltip 等功能,还算完美。
感谢 D3,感谢开源世界!
d3、d3-tip、dagre-d3 基本用法
下面是伪代码,d3-tip 和 dagre-d3 官方文档确实很差,怎么引入包都没写清楚。
import d3 from 'd3';
import * as dagreD3 from 'dagre-d3';
import d3Tip from 'd3-tip';
// 创建图形容器
const g = new dagreD3.graphlib.Graph().setGraph({}).setDefaultEdgeLabel(function() { return {}; });
// 绘制所有点
points.forEach((item, index) => g.setNode(item.id, item));
// 绘制所有连线/关系
relations.forEach(item => g.setEdge(item.source, item.target, {}));
const svg = d3.select('svg');
const inner = svg.select('g');
// 设置缩放
const zoom = d3.zoom().on('zoom', inner.attr('transform', d3.event.transform);
svg.call(zoom);
// 设置 tooltip
const tooltip = d3Tip().attr('class', 'd3-tip').offset([10, 0]).html((d) => getTooltip(that.g.node(d))); // 这里省略拼接字符串,业务代码
svg.call(tooltip);
// dagreD3 绘制到 svg 上
const render = new dagreD3.render();
render(inner, g);
// 点击事件
inner.selectAll('g.node').on('click', (id) => {
console.log(id);
});
echarts 和 D3
echarts 是开箱即用的,没有自定义渲染节点等功能,不够灵活。 d3 要学习各种 svg 等语法,上手成本高,对 web 技术、可视化理论、数学逻辑都一定要求。