我正在尝试在v4中实现following script:
此脚本是在v3中编写的,在v4中无效。 (我也是d3的新手,并开始学习v4)
var width = 400,
height = 400;
var nodes = d3.range(200).map(function() { return {radius: Math.random() * 12 + 4}; }),
root = nodes[0],
color = d3.scale.category10();
root.radius = 0;
root.fixed = true;
var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) { return i ? 0 : -2000; })
.nodes(nodes)
.size([width, height]);
force.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", function(d, i) { return color(i % 3); });
force.on("tick", function(e) {
var q = d3.geom.quadtree(nodes),
i = 0,
n = nodes.length;
while (++i < n) q.visit(collide(nodes[i]));
svg.selectAll("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
svg.on("mousemove", function() {
var p1 = d3.mouse(this);
root.px = p1[0];
root.py = p1[1];
force.resume();
});
function collide(node) {
var r = node.radius + 16,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + quad.point.radius;
if (l < r) {
l = (l - r) / l * .5;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
我能想到的最好的解决方案是绕着一个大的特殊圆圈移动,并使用碰撞来避免其余的圆圈碰到我的大圆圈。
但是我的大圈子正在冻结。 另外,认为我的整体解决方案不太好:
我在做什么错了?
var width = 1000,
height = 600;
d3.select("body")
.append("svg")
.attr("width", 1000)
.attr("height", 600);
const collideCircle = {
radius: 30,
fill: 'black',
center: {
x: 200,
y: 200
}
};
const radius = 10;
const moreCircles = [...Array(40).keys()].map(i => ({
radius,
fill: 'pink',
center: {
x: 200,
y: 200
}
}));
const simulation = d3.forceSimulation([collideCircle, ...moreCircles])
.force('x', d3.forceX().x(d => d.center.x))
.force('y', d3.forceY().y(d => d.center.y))
.force('collision', d3.forceCollide().radius(d => d.radius))
.on('tick', ticked);
const collideCircles = d3.select('svg')
.append('g')
.selectAll('circle')
.data([collideCircle])
.enter().append('circle')
.attr('fill', d => d.fill)
.attr('r', d => d.radius)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
const moreCirclesGeom = d3.select('svg')
.append('g')
.selectAll('circle')
.data(moreCircles)
.enter().append('circle')
.attr('fill', d => d.fill)
.attr('r', d => d.radius)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
function ticked() {
// stay in the SVG (box) only:
moreCirclesGeom
.attr("cx", function (d) {
return d.x = Math.max(radius, Math.min(width - radius, d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(radius, Math.min(height - radius, d.y));
});
collideCircles
.attr("cx", function (d) {
return d.x = Math.max(radius, Math.min(width - radius, d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(radius, Math.min(height - radius, d.y));
});
}
d3.select('svg').on("mouseover", handleMouseOver);
function handleMouseOver() {
collideCircle.fx = d3.event.x;
collideCircle.fy = d3.event.y;
simulation.alphaTarget(0.1).restart();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>