我显然是javascript的新手,当然也是D3js ......
我无法弄清楚如何将“Point”变成附加到节点/路径的“圆圈”?
我猜它与tick()函数有关,但我找不到启发文档:(
var node = svg.selectAll("path.node")
.data(dataset.nodes)
.enter().append("path").attr("class", "node")
.style("cursor","pointer")
.style("fill", function(d) {
if (d.color) {return d.color;}
else { return "#fff" }
;})
.call(force.drag);
// add the nodes
node.append("circle")
.attr("r", 35);
function tick() {
node.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"Point","coordinates":[d.x, d.y]}}); return p ? p : 'M 0 0' });
link.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"LineString","coordinates":[[d.source.x, d.source.y],[d.target.x, d.target.y]]}}); return p ? p : 'M 0 0' });
}
我在这里制作了一个代码集https://codepen.io/unit60/pen/KqLEPe
非常感谢正确方向的转向......帮助:)
答案 0 :(得分:1)
我想我明白你在问什么。在当前的代码段中,您将circle
和text
嵌套在path
元素下。这不适用于path
的无效子项。而是将它们放在g
(组):
var node = svg.selectAll(".node")
.data(dataset.nodes)
.enter()
.append("g")
.attr("class", "node")
.style("cursor","pointer")
.call(force.drag);
// add the nodes
node.append("circle")
.attr("r", 35)
.style("fill", function(d) {
if (d.color) {return d.color;}
else { return "#fff"; }
});
// add the text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
然后我们需要更新tick
函数以正确放置组(而不是绘制像它一样的路径):
function tick() {
node.attr("transform", function(d) {
// change this to centroid
var p = path.centroid({"type":"Feature","geometry":{"type":"Point","coordinates":[d.x, d.y]}});
return "translate(" + p + ")";
});
link.attr("d", function(d) {
var p = path({"type":"Feature","geometry":{"type":"LineString","coordinates":[[d.source.x, d.source.y],[d.target.x, d.target.y]]}}); return p ? p : 'M 0 0' });
}
}
更新codepen;运行示例:
var dataset = {
nodes: [{
name: "Location 01",
class: "overlay01-link",
color: "red"
}, {
name: "Location 02",
class: "overlay02-link",
color: "orange"
}, {
name: "Location 03"
}, {
name: "Location 04"
}, {
name: "Location 05"
}, {
name: "Location 06"
}, {
name: "Location 07"
}, {
name: "Location 08"
}, {
name: "Location 09"
}, {
name: "Location 10"
}],
edges: [{
source: 0,
target: 1
}, {
source: 0,
target: 2
}, {
source: 0,
target: 3
}, {
source: 0,
target: 4
}, {
source: 1,
target: 5
}, {
source: 2,
target: 5
}, {
source: 2,
target: 5
}, {
source: 3,
target: 4
}, {
source: 5,
target: 8
}, {
source: 5,
target: 9
}, {
source: 6,
target: 7
}, {
source: 7,
target: 8
}, {
source: 8,
target: 9
}]
};
var projections = {
"Albers": d3.geo.albers(),
"Azimuthal Equal Area": d3.geo.azimuthalEqualArea(),
"Azimuthal Eqidistant": d3.geo.azimuthalEquidistant(),
"Conic Conformal": d3.geo.conicConformal(),
"Conic Equal Area": d3.geo.conicEqualArea(),
"Conic Equidistant": d3.geo.conicEquidistant(),
"Eqirectangular": d3.geo.equirectangular(),
"Gnomonic": d3.geo.gnomonic(),
"Mercator": d3.geo.mercator(),
"Orthographic": d3.geo.orthographic(),
"Stereographic": d3.geo.stereographic(),
"Transverse Mercator": d3.geo.transverseMercator(),
};
var config = { "projection": "Orthographic", "clip": false, "friction": .45, "linkStrength": 1, "linkDistance": 320, "charge": 3, "gravity": .1, "theta": .1 };
var gui = new dat.GUI();
//var projectionChanger = gui.add(config, "projection", ['equalarea', 'equidistant', 'gnomonic', 'orthographic', 'stereographic', 'rectangular']);
var projectionChanger = gui.add(config, "projection", Object.keys(projections));
//http://stackoverflow.com/a/3417242
function wrapIndex(i, i_max) {
return ((i % i_max) + i_max) % i_max;
}
projectionChanger.onChange(function(value) {
projection = projections[value]
.scale(height/2)
.translate([(width/2)-125, height/2])
.clipAngle(config["clip"] ? 90 : null)
path.projection(projections[value])
return
if(value == 'rectangular') {
path = d3.geo.path().projection(function(coordinates){
console.log(coordinates[0], coordinates[1])
return [
wrapIndex(coordinates[0], width),
wrapIndex(coordinates[1], height),
];
});
config['clip'] = false
} else {
projection.mode(value)
path = d3.geo.path().projection(projection)
}
force.start()
});
var clipChanger = gui.add(config, "clip").listen();
clipChanger.onChange(function(value) {
projection.clipAngle(value ? 90 : null)
force.start()
});
var fl = gui.addFolder('Force Layout');
fl.open()
var frictionChanger = fl.add(config, "friction", 0, 1);
frictionChanger.onChange(function(value) {
force.friction(value)
force.start()
});
var linkDistanceChanger = fl.add(config, "linkDistance", 0, 400);
linkDistanceChanger.onChange(function(value) {
force.linkDistance(value)
force.start()
});
var linkStrengthChanger = fl.add(config, "linkStrength", 0, 1);
linkStrengthChanger.onChange(function(value) {
force.linkStrength(value)
force.start()
});
var chargeChanger = fl.add(config,"charge", 0, 500);
chargeChanger.onChange(function(value) {
force.charge(-value)
force.start()
});
var gravityChanger = fl.add(config,"gravity", 0, 1);
gravityChanger.onChange(function(value) {
force.gravity(value)
force.start()
});
var thetaChanger = fl.add(config,"theta", 0, 1);
thetaChanger.onChange(function(value) {
force.theta(value)
force.start()
});
var width = window.innerWidth,
height = window.innerHeight - 5,
fill = d3.scale.category20(),
nodes = [{x: width/2, y: height/2}],
links = [];
var projection = projections[config["projection"]]
.scale(height/2)
.translate([(width/2)-125, height/2])
.clipAngle(config["clip"] ? 90 : null)
var path = d3.geo.path()
.projection(projection)
var force = d3.layout.force()
.linkDistance(config["linkDistance"])
.linkStrength(config["linkStrength"])
.gravity(config["gravity"])
.size([width, height])
.charge(-config["charge"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { force.start(); var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
for(x=0;x<100;x++){
source = nodes[~~(Math.random() * nodes.length)]
target = {x: source.x + Math.random(), y: source.y + Math.random(), group: Math.random()}
links.push({source: source, target: target})
nodes.push(target)
}
var link = svg.selectAll(".link")
.data(dataset.edges)
.enter().append("path").attr("class", "link")
var node = svg.selectAll(".node")
.data(dataset.nodes)
.enter()
.append("g")
.attr("class", "node")
.style("cursor","pointer")
.call(force.drag);
// add the nodes
node.append("circle")
.attr("r", 35)
.style("fill", function(d) {
if (d.color) {return d.color;}
else { return "#fff"; }
});
// add the text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
force
.nodes(dataset.nodes)
.links(dataset.edges)
.on("tick", tick)
.start();
function tick() {
node.attr("transform", function(d) {
var p = path.centroid({"type":"Feature","geometry":{"type":"Point","coordinates":[d.x, d.y]}});
return "translate(" + p + ")";
});
link.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"LineString","coordinates":[[d.source.x, d.source.y],[d.target.x, d.target.y]]}}); return p ? p : 'M 0 0' });
}
// action to take on mouse click
function click() {
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 22)
.style("fill", "steelblue")
.style("stroke", "lightsteelblue")
.style("stroke-width", ".5px")
.style("font", "20px sans-serif");
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 16)
.style("fill", "lightsteelblue");
}
// action to take on mouse double click
function dblclick() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 6)
.style("fill", "#ccc");
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 12)
.style("stroke", "none")
.style("fill", "black")
.style("stroke", "none")
.style("font", "10px sans-serif");
}
body {
padding: 0;
margin: 0;
background:#222;
}
.node {
stroke-width: 2.5px;
width:100px;
fill:#fff;
}
text {
fill: #fff;
font: 10px sans-serif;
pointer-events: none;
}
circle {
fill: #ccc;
stroke: #fff;
stroke-width: 1.5px;
}
path.link {
stroke: #fff;
fill-opacity: 0
}
svg {
height:100%;
width:100%;
display:block;
}
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>