记录下自己使用D3 V5写出一个简单的关系导图

2,163 阅读4分钟

D3 V5开发力导向图

D3开始我在网上看到的资料都是V3居多的,公司项目需要用到v5的版本,找了好多资料,发现V3和V5的api的区别还是很大的,这里主要说下我用到的关系力导图。再说下我遇到的问题,和如何解决的。

有点差异的api

V3下初始化一个力

  var force = d3.layout.force()//layout将json格式转化为力学图可用的格式
            .nodes(d3.values(nodes))//设定节点数组
            .links(links)//设定连线数组
            .size([width, height])//作用域的大小
            .linkDistance(180)//连接线长度
            .charge(-1500)//顶点的电荷数。该参数决定是排斥还是吸引,数值越小越互相排斥
            .on("tick", tick)//指时间间隔,隔一段时间刷新一次画面
            .start();//开始转换

V5下初始化一个力导图

  const simulation = d3.forceSimulation(nodes) //初始化一个力导向图
        .force("link", d3.forceLink(links).id((d) => d.id).distance(200))//同上的设定连线数组 (ps:links一定要是一个数据类型)distance为连接线的长度
        .force("charge", d3.forceManyBody().strength(-100))//charge设置引力
        .force("center", d3.forceCenter(width / 2, height / 2));

d3的调用方法和jquery很相似,基本都是链式调用,很方便。选择器有两种 d3.select()

d3.selectAll()

d3.select获取到的是符合条件的第一个元素.

d3.selectAll返回的是符合条件的所有元素。

d3.forceSimulation(nodes) //这里是将这个nodes的数据进行转换,增加坐标等等,转换的结果如下:

id: "IFIND" index: 2 type: "resolved" vx: -0.000645327002392039 vy: -0.0016542362952343182 x: 958.4040054469376 y: 284.15898682862536 大概是这样的一个数据结构。

初始化完成数据后,需要添加连接线,箭头和需要的气泡还是什么可以自己增加,下边我大概贴下自己的使用的连接线、文字和气泡和箭头。

//箭头append到svg上
  var marker = svg.append("marker")
                .attr("id", "resolved")
                //.attr("markerUnits","strokeWidth")//设置为strokeWidth箭头会随着线的粗细发生变化
                .attr("markerUnits", "userSpaceOnUse")
                .attr("viewBox", "0 -5 10 10")//坐标系的区域
                .attr("refX", 32)//箭头坐标
                .attr("refY", -1)
                .attr("markerWidth", 12)//标识的大小
                .attr("markerHeight", 12)
                .attr("orient", "auto")//绘制方向,可设定为:auto(自动确认方向)和 角度值
                .attr("stroke-width", 2)//箭头宽度
                .append("path")
                .attr("d", "M0,-5L10,0L0,5")//箭头的路径
                .attr('fill', '#000000');//箭头颜色

增加下连接线。

   var edges_line = svg.selectAll(".edgepath") //class用来占位
            .data(links) //增加数据绑定
            .enter() //导入数据
            .append("path")
            .style("stroke", function (d) {
                var lineColor;
                //根据关系的不同设置线条颜色
                if (d.rela == "上位产品" || d.rela == "上游" || d.rela == "下位产品") {
                    lineColor = "#A254A2";
                } else if (d.rela == "主营产品") {
                    lineColor = "#B43232";
                }
                return lineColor;
            }).style("pointer-events", "none")
            .style("stroke-width", 2)//线条粗细
            .attr("marker-end", "url(#resolved)") //根据箭头id设置箭头显示
            .attr('d', function (d) { ; return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y })   //拼接下线条路径
            .attr('class', 'edgepath')
            .attr('id', function (d, i) { return 'edgepath' + i; });//根据箭头标记的id号标记箭头

设置显示气泡

        var circle = svg.append("g").selectAll("circle")
            .data(nodes)//表示使用force.nodes数据
            .enter().append("circle") 
            .style("fill", function (node) {
                var color;//圆圈背景色
                var link = links[node.index];
                if (node.name == link.source.name && link.rela == "主营产品") {
                    color = "#F6E8E9";
                } else {
                    color = "#F9EBF9";
                }
                return color;
            })
            .style('stroke', function (node) {
                var color;//圆圈线条的颜色
                var link = links[node.index];
                if (node.name == link.source.name && link.rela == "主营产品") {
                    color = "#B43232";
                } else {
                    color = "#A254A2";
                }
                return color;
            })
            .attr("r", 28)//设置圆圈半径
            .on("click", function (node) {
                //单击时让连接线加粗
                edges_line.style("stroke-width", function (line) {
                    console.log(line);
                    if (line.source.name == node.name || line.target.name == node.name) {
                        return 4;
                    } else {
                        return 0.5;
                    }
                });
                //d3.select(this).style('stroke-width',2);
            })
            .call(d3.drag().on("start", dragstarted)  //开始drag函数
                .on("drag", dragged) //drag中函数
                .on("end",dragended));//将当前选中的元素传到drag函数中,使顶点可以被拖动

drag函数

 function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }
        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }

        function dragended(d) {
            if (!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }

对text元素设置颜色 填充等属性

var text = svg.append("g").selectAll("text")
            .data(nodes)
            //返回缺失元素的占位对象(placeholder),指向绑定的数据中比选定元素集多出的一部分元素。
            .enter()
            .append("text")
            .attr("dy", ".35em")
            .attr("text-anchor", "middle")//在圆圈中加上数据  
            .style('fill', function (node) {
                var color;//文字颜色
                var link = links[node.index];
                if (node.name == link.source.name && link.rela == "主营产品") {
                    color = "#B43232";
                } else {
                    color = "#A254A2";
                }
                return color;
            }).attr('x', function (d) {
                // console.log(d.name+"---"+ d.name.length);
                var re_en = /[a-zA-Z]+/g;
                //如果是全英文,不换行
                if (d.id.match(re_en)) {
                    d3.select(this).append('tspan')
                        .attr('x', 0)
                        .attr('y', 2)
                        .text(function () { return d.id; });
                }
                //如果小于四个字符,不换行
                else if (d.id.length <= 4) {
                    d3.select(this).append('tspan')
                        .attr('x', 0)
                        .attr('y', 2)
                        .text(function () { return d.id; });
                } else {
                    var top = d.id.substring(0, 4);
                    var bot = d.id.substring(4, d.id.length);

                    d3.select(this).text(function () { return ''; });

                    d3.select(this).append('tspan')
                        .attr('x', 0)
                        .attr('y', -7)
                        .text(function () { return top; });

                    d3.select(this).append('tspan')
                        .attr('x', 0)
                        .attr('y', 10)
                        .text(function () { return bot; });
                }
                //直接显示文字    
                /*.text(function(d) { 
                return d.name; */
            });

设置tick函数

function tick() {
            //path.attr("d", linkArc);//连接线
            circle.attr("transform", (d) => {
                return `translate(${d.x},${d.y})`
            });//圆圈
            text.attr("transform", d => {
                return `translate(${d.x},${d.y})`
            });//顶点文字
             edges_line.attr("x", function (d) { return (d.source.x + d.target.x) / 2; });
            edges_line.attr("y", function (d) { return (d.source.y + d.target.y) / 2; });


            edges_line.attr('d', function (d) {
                var path = 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
                return path;
            });
        }

增加了这些基本就可以完成一个基本的力导向图。 完整版源码获取 pan.baidu.com/s/1VZvVjuW1… 提取码:dnu3