欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用d3实现类似脑图,股权穿透图,关系图谱

程序员文章站 2022-05-09 09:02:41
...

前言

最近工作需要完成一个股权穿透图,找了好多文档发现都不满足需求,最终选择d3.js来实现,包含子集的收缩展开,交互以及其他功能。之前由于没做过类似关系图以及不了解d3,踩了很多坑,我会尽可能将代码描述清楚(毕竟花了两个大夜),建议提前了解svg的简单使用和运行机制,d3的基本使用。
使用d3不像其他图表,d3的使用*度极大,因此可以完成绝大多数图表需求,时间充足的话,可以逐步完成代码,理解实现思路后做其他修改就很简单了。话不多说,先贴图。
使用d3实现类似脑图,股权穿透图,关系图谱

我尽可能简化了代码,主要看实现思路,后续增加或删减功能在此基础上都算方便。

我会将源码贴出来,可以直接使用。记录一下我遇到的坑。

配置方法

getTreeConfig方法里包含内部需要配置项,可以调整数值查看效果。

getTreeConfig() {
    let treeConfig = {margin: {top: 10, right: 5, bottom: 0, left: 30}}
    treeConfig.chartWidth = this.d3.select('#svgWrap')[0][0].clientWidth;
    treeConfig.chartHeight = this.d3.select('#svgWrap')[0][0].clientHeight;
    treeConfig.centralHeight = treeConfig.chartHeight / 2;
    treeConfig.centralWidth = treeConfig.chartWidth / 2;
    treeConfig.linkLength = 230;//连线长度
    treeConfig.duration = 500; //动画时间
    treeConfig.lineHeight = 60;

    treeConfig.boxWidth = 120;
    treeConfig.boxHeight = 40;
    return treeConfig;
}

rect框+内部文本

let node = g.selectAll('g.' + node_class).data(nodes, d => d.id || (d.id = ++id))
    let nodeEnter = node.enter().insert('g','g')
        .attr('class', node_class)
        .attr('transform', d => `translate(${source.x0},${source.y0})`)//设置初始动画位置

        let tagAnode = nodeEnter.append("a")
            .attr("class","tag")
            .attr("xlink:title", d => {
                return "上市公司150家"
            })
            .style("cursor", function (d) {
                return "not-allowed"
            })
            .on('click', selectTag)
         tagAnode.append("svg:rect")
            .attr("x", 0)
            .attr("y", 0)
            .attr("width", config.boxWidth)
            .attr("height", config.boxHeight)
            .attr("rx", 6)

rect在g标签的内部,为了加交互效果,添加了一层a标签,svg避免不了元素位置计算,没有echarts框架那么简洁,所有涉及计算的地方需了解自己的实现逻辑,理解每个dom的添加方式很重要,理解每个dom的添加方式很重要,理解每个dom的添加方式很重要
代码中insert方法是将新元素插入到之前g标签之前,解决svg无法使用z-index的问题,可将insert方法替换成append()查看效果,此时收缩时动画时覆盖在父级之上的。

连线的计算

function drawPath(d) {
    let x1 = forUpward ? d.source.x : d.source.x + config.boxWidth,
        y1 = d.source.y + config.boxHeight/2,
        x3 = forUpward ? d.target.x + config.boxWidth : d.target.x - 10,
        y3 = d.target.y + 20
        return `M${x1},${y1} L${(x1 + x3) / 2},${y1} L${(x1 + x3) / 2},${y3} L${x3},${y3}`
}

forUpward 在代码中为正负1,来计算左右两边的x轴位置,x1,y1,x3,y3为起始和结束坐标,根据两个坐标可*发挥连线的过程,如果需要用曲线,建议查看d3的d3.svg.diagonal。

动画

let nodeExit = node.exit().transition()
    .duration(config.duration)
    .attr('transform', d => `translate(${source.x},${source.y})`)
    .remove();

所有动画都需要关注初始状态和结束状态,在调试代码过程中动画出现问题,基本上都是两个状态的问题。d3中exit()方法标识退出,transition定义动画方式,删除元素需remove掉。

结尾

业务和需求相关的代码不像单一方法,拿来即用,了解运行机制后定制化修改。
下载地址:d3股权穿透下载