使用D3绘制力导向图遇到的坑

本文介绍了D3的不同版本间绘制力导向图的方法差异,重点讲解了D3V4版本的基本流程,包括创建svg、绘制节点和边以及处理拖拽事件。同时,文章还探讨了在浏览器中因跨域导致的错误和解决方案,以及解决异步赋值问题的方法,如使用JQuery的同步Ajax请求。
摘要由CSDN通过智能技术生成

1. 不同D3版本差异

V3:通过d3.layout.force()将节点、连接线的数据转换成d3力导向图能够使用的数据结构

var force = d3.layout.force().nodes(nodes)
        .links(lines)
        .size([width, height])
        .linkDistance(100)
        .charge(-1200)
        .start()
        .on("tick", function () {
			//具体的方法
        });

V4:通过 d3.forceSimulation()定义关系图,包括设置边link、排斥电荷charge、关系图中心点

var simulation = d3.forceSimulation(nodes)
        .force("link", d3.forceLink(links).distance(200))
        .force("charge",d3.forceManyBody().strength(-100))
        .force("center",d3.forceCenter(width/2, height/2));
        
simulation.on("tick",function(){
	//具体的方法
});

// 生成节点
simulation.nodes(nodes).on("tick", ticked);
//生成边
simulation.force("link")
    .links(links)
    .distance(d => { return 200 });     //每一边的长度

2. D3 V4版本绘制力导向图基本流程

  1. 创建svg绘图:svg是一种矢量图格式,相当于创建一个画图的容器,图中的所有内容都在这个svg中
var svg = d3.select("#svg1")	//select中的内容是需要绑定的html标签
  1. 创建节点、连线、文字、箭头等图形元素:前提是已经导入了节点和边的数据,以数组的形式分别记录
//绘制边
var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links)			//边数组
    .enter()
    .append("line")
    .attr("stroke-width", 2)	//线段宽度,需要再加一个线段颜色
    .style("stroke", '#000000');	//设置填充颜色
//绘制边上文字
var linksText = svg.append("g")
    .selectAll("text")
    .data(links)
    .enter()
    .append("text")
    .text(function(d){return d.type;})	//显示内容是边的类型属性
//箭头
var marker = svg.append("marker")
    .attr("id", "resolved")
    .attr("markerUnits","userSpaceOnUse")
    .attr("viewBox", "0 -5 10 10")//坐标系的区域
    .attr("refX",34)//箭头坐标
    .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 node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes)			//节点数组
    .enter()
    .append("circle")
    .attr("r", function(d) {return 15})	// 设置圆圈半径
    .attr("fill", '#000000')	//设置填充颜色
    .attr("stroke", "none")
    .attr("id", d => d.id)		//设置id
    .call(d3.drag()				//设置拖拽
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended)
    );

//绘制描述节点的文字
var nodesText = svg.append('g')
    .selectAll("text")
    .data(nodes)
    .enter()
    .append("text")
    .style("fill", "black")
    .attr("dx", 20)
    .attr("dy", 8)
    .text(function (d) {return d.name;});	//设置节点文字是节点的名字属性
  1. 创建力导向图对象:使用d3.forceSimulation()定义力导向图,包括设置边link、排斥电荷charge、关系图中心点
var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(d => {return d.id}))
    .force("charge", d3.forceManyBody().strength(-30))	//电荷排斥,strength为负是排斥,为正是吸引
    .force("center", d3.forceCenter(width / 2, height / 2))	//中心位置
    .force("collision", d3.forceCollide(20))    // 碰撞检测
  1. 将节点、关系元素导入力导向图中
// 生成节点
simulation.nodes(nodes).on("tick", ticked);
//生成边
simulation.force("link")
    .links(links)
    .distance(d => { return 200 });     //每一边的长度
  1. 确定节点、边、文字、箭头的位置
// ticked()函数确定link线的起始点x、y坐标 node确定中心点 文本通过translate平移变化
function ticked() {
    link
        .attr("x1", function(d) {return d.source.x;})
        .attr("y1", function(d) {return d.source.y;})
        .attr("x2", function(d) {return d.target.x;})
        .attr("y2", function(d) {return d.target.y;})
        .attr("marker-end", "url(#resolved)");

    linksText
        .attr("x",function(d){return (d.source.x+d.target.x)/2;})
        .attr("y",function(d){return (d.source.y+d.target.y)/2;});

    node
        .attr("cx", function(d) {return d.x;})
        .attr("cy", function(d) {return d.y;});

    nodesText
        .attr("x", function (d) {return d.x;})
        .attr("y", function (d) {return d.y;});
}
  1. 设置拖拽,鼠标移动等事件
// 拖动函数代码
var dragging = false;
// 开始拖动并更新相应的点
function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
    dragging = true;
}
// 拖动进行中
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;
    dragging = false;
}

// 鼠标选中节点事件
$('#svg1').on('mouseenter', '.nodes circle', function(event) {
    //添加事件内容
});

// 鼠标移开节点事件
$('#svg1').on('mouseleave', '.nodes circle', function(event) {
    //添加事件内容
});

3. 跨域问题

现象

使用d3.json()读取json文件数据时,直接用浏览器运行的话会报跨域错误

原因

跨域指的是不同域之间互相请求资源,比如a.com无法操作b.com下的内容。常规来说写了一个前端页面直接就用默认浏览器打开运行了,此时浏览器的地址栏显示的是你的html文件在你电脑上的地址。需要用一个服务器来存放json文件,通俗说就说要用localhost这样的地址去运行你的程序。

解决办法

整体的思路都是将文件和代码放在一个服务器中去请求,以下提供两个思路:

  • 使用nodejs提供本地服务器
  • IDE可能会存在自带的本地服务器,通过该方式运行程序。以vs code为例,可以下载一个叫做Live Server的插件,通过右键选择Open with Live Server.即可
    在这里插入图片描述

4. 异步赋值

现象

使用d3.json()读取数据时,将数据内容赋值到变量中,在d3.json()函数内能正常赋值,函数结束后变量值为undefined或默认值

原因

d3.json()采用异步的方式读取数据,导致赋值了但没有更新,从而在函数外没有赋值成功

解决办法

采用JQuery读取json文件,并且设置参数async为false,这样便是采用同步的方式读取数据。

$.ajax({
    url: path,			//json文件的地址
    type: "GET",
    dataType: "json",
    async: false,		//不采用异步方式
    success: 
    function (data) {	//进行赋值操作
        nodeConfig = data.node
        relationshipConfig = data.relationship
        dataPath = data.dataPath
    }
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值