【问题标题】:D3.js v4+ - How to determine specific location in force laypoutD3.js v4+ - 如何确定强制布局中的特定位置
【发布时间】:2019-03-24 17:33:38
【问题描述】:

其他在线教程/答案是关于 D3.js v3.x 或可拖动元素上的特定位置。

我查看了文档,但我不完全了解如何操作:

我试图在不改变矩形位置的情况下防止红色矩形与圆圈重叠。

我指定了fxfy,但仍然没有成功。

const nodes = d3.range(100).map(d => ({radius: 5, type: "circle"}));

const walls = [{}, {}, {}, {}].map((_, index) => ({
    fx: 200 * index,
    fy: 100,
    type: "wall"
}));

const circleCenters = [100, 300, 500];

d3.forceSimulation(nodes.concat(walls))
    .force('charge', d3.forceManyBody().strength(10))
    .force('x', d3.forceX().x(function (d, i) {
        if (d.type === "circle")
            return circleCenters[i % 3];
        else
            return d.fx;
    }))
    .force('y', d3.forceY().y(100))
    .force('collision', d3.forceCollide().radius(d => d.radius))
    .on('tick', ticked);

function ticked() {
    d3.select('svg')
        .selectAll('rect')
        .data(walls)
        .enter()
        .append('rect')
        .attr('width', 100)
        .attr('height', 10)
        .attr('fill', 'red')
        .attr('x', d => d.x)
        .attr('y', d => d.y);

    const u = d3.select('svg')
        .selectAll('circle')
        .data(nodes);

    u.enter()
        .append('circle')
        .merge(u)
        .attr('fill', 'blue')
        .attr('r', d => d.radius)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
<svg width="90vw" height="90vh">
</svg>

【问题讨论】:

    标签: javascript d3.js dom svg


    【解决方案1】:

    您需要用正确大小的保护节点填充整个矩形。

    如果您想查看保护圈,请取消注释部分代码。

    有 2 个 g 元素,一个包含保护节点,另一个包含 blue 节点。

    编辑

    如果你让 X 力更强一点,节点会更接近实际中心。

    .force('x', d3.forceX()
        .x( (d, i) => (d.type === "circle") ? circleCenters[i % 3] : d.fx )
        .strength(0.3))
    

    const nodes = d3.range(100).map(d => ({radius: 5, type: "circle"}));
    
    const walls = [{}, {}, {}, {}].map((_, index) => ({
        fx: 200 * index,
        fy: 100,
        width: 100,
        height: 10,
        radius: 5,
        type: "wall"
    }));
    
    const circleCenters = [100, 300, 500];
    
    // construct "invisible" circles covering the rects
    var invCircles = [];
    walls.forEach(e => {
        d3.range(e.fx+3, e.fx+e.width-3, 3).forEach(cx => {
            invCircles.push({
                fx: cx,
                fy: e.fy + e.radius,
                radius: e.radius,
                type: e.type
            });
        });
    });
    
    d3.forceSimulation(nodes.concat(invCircles))
        .force('charge', d3.forceManyBody().strength(10))
        .force('x', d3.forceX().x( (d, i) => (d.type === "circle") ? circleCenters[i % 3] : d.fx ).strength(0.3))
        .force('y', d3.forceY().y(100))
        .force('collision', d3.forceCollide().radius(d => d.radius))
        .on('tick', ticked);
    
    var wallGeom = d3.select('svg').append('g').attr('class', 'wall');
    var circlesGeom = d3.select('svg').append('g').attr('class', 'circles');
    
    wallGeom.selectAll('rect')
        .data(walls)
        .enter()
        .append('rect')
        .attr('width', d => d.width )
        .attr('height', d => d.height )
        .attr('fill', 'red')
        .attr('x', d => d.fx)
        .attr('y', d => d.fy);
    
    // wallGeom.selectAll('circle')
    //     .data(invCircles)
    //     .enter()
    //     .append('circle')
    //       .attr('fill', 'yellow')
    //       .attr('r', d => d.radius)
    //       .attr('cx', d => d.fx)
    //       .attr('cy', d => d.fy);
    
    function ticked() {
    
        const u = d3.select('svg')
            .select('.circles')
            .selectAll('circle')
            .data(nodes);
    
        u.enter()
            .append('circle')
            .attr('fill', 'blue')
            .attr('r', d => d.radius)
          .merge(u)
            .attr('cx', d => d.x)
            .attr('cy', d => d.y);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>
    <svg width="90vw" height="90vh"></svg>

    【讨论】:

    • 首先,谢谢。这是一个很好的解决方案!其次,你确定没有不涉及创造这么多隐形圈子的解决方案吗?我可能需要创建 3000 多个隐形球。对于浏览器来说是不是太多了? (我的矩形更宽更高)
    • @StavAlfi 目前力引擎没有约束节点的方法。你必须在tick() 方法中自己做,或者创建大大小小的(角)不可见的圆圈来填充矩形,不可见的节点只涉及力引擎,可能会减慢它的速度,但使用ticks() 中的碰撞检测是也不是免费的,而且使用圈子很快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 2013-12-02
    • 2018-02-05
    • 2015-06-25
    • 2018-12-24
    • 2013-06-25
    相关资源
    最近更新 更多