我在网上找到了一个非常好的示例,介绍了如何使用 D3 可视化 RDF 三元组。输出如下所示:
我正在尝试在 Vue 中重新创建示例,但我没有成功生成正确的可视化,也没有使我的节点可拖动。我确信我错过了
mounted
方法中的几个重要步骤(我有基本的 Vue 知识)。我的整个代码可以在 GitHub 上找到,但我还在下面添加了最重要的代码。任何帮助将非常感激。当前输出如下所示:
这是我的主要代码:
<template>
<div>
<svg></svg>
</div>
</template>
<script>
import * as d3 from "d3";
import {triplesToGraph} from "@/components/functions/triplesToGraph";
export default {
mounted() {
const width = 800;
const height = 500;
const triples = [
{subject:"ex:ThaiLand", predicate:"ex:hasFood", object:"ex:TomYumKung"},
{subject:"ex:TomYumKung", predicate:"rdf:type", object:"ex:SpicyFood"},
{subject:"ex:TomYumKung", predicate:"ex:includes", object:"ex:shrimp"},
{subject:"ex:TomYumKung", predicate:"ex:includes", object:"ex:chilly"},
{subject:"ex:TomYumKung", predicate:"ex:includes", object:"ex:lemon"},
{subject:"ex:lemon", predicate:"ex:hasTaste", object:"ex:sour"},
{subject:"ex:chilly", predicate:"ex:hasTaste", object:"ex:spicy"}
];
var graph = triplesToGraph(triples);
const svg = d3.select("svg").attr("width", width).attr("height", height);
function ticked() {
var links = svg.selectAll(".link")
.data(graph.links)
.enter()
.append("line")
.attr("marker-end", "url(#end)")
.attr("class", "link")
.attr("stroke-width",1)
;//links
// ==================== Add Link Names =====================
var linkTexts = svg.selectAll(".link-text")
.data(graph.links)
.enter()
.append("text")
.attr("class", "link-text")
.text( function (d) { return d.predicate; })
;
// ==================== Add Link Names =====================
var nodeTexts = svg.selectAll(".node-text")
.data(graph.nodes)
.enter()
.append("text")
.attr("class", "node-text")
.text( function (d) { return d.label; })
;
// ==================== Add Node =====================
var nodes = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r",8)
// .call(force.drag)
nodes
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
;
links
.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; })
;
nodeTexts
.attr("x", function(d) { return d.x + 12 ; })
.attr("y", function(d) { return d.y + 3; })
;
linkTexts
.attr("x", function(d) { return 4 + (d.source.x + d.target.x)/2 ; })
.attr("y", function(d) { return 4 + (d.source.y + d.target.y)/2 ; })
;
}
var force = d3.forceSimulation(graph.nodes)
.force("link", d3.forceLink(graph.links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
},
};
</script>
我发现这也是一个非常有用的链接:https://observablehq.com/@d3/force-directed-graph/2?intent=fork
这里发生了一些事情。主要问题是存储库使用的 d3 版本是 d3 v3,而您在存储库中导入的版本是 d3 v7,因此语法非常不同。然后
links
、linkTexts
、nodeTexts
等应在ticked
功能之前启动。然后,您确实提到拖动不起作用,那是因为您评论了该行:
// .call(force.drag)
但是取消注释后,
force.drag
仍然是错误,所以我们需要实现我们自己的可拖动(取自这个示例):
function dragstart() {
d3.select(this).classed("fixed", true);
}
function clamp(x, lo, hi) {
return x < lo ? lo : x > hi ? hi : x;
}
function dragged(event, d) {
d.fx = clamp(event.x, 0, width);
d.fy = clamp(event.y, 0, height);
force.alpha(1).restart();
}
const drag = d3.drag().on("start", dragstart).on("drag", dragged);
然后我们可以取消注释我提到的行并传递
drag
变量:
var nodes = svg
.selectAll(".node")
.data(graph.nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", 8)
.call(drag);
最后,您还可以从您提供的示例中复制粘贴 CSS。
如果您想现场观看,这是codesandbox 中的完整演示: