切换后丢失碰撞检测(d3v4)

时间:2017-01-05 07:28:41

标签: d3.js collision-detection

我有一个气泡图,其中气泡分开并在按下按钮时一起返回。

就我而言,我输入气泡图的原始数据有3列:Character,Total_Words和Sex。气泡图的工作方式是每个角色由其自己的气泡表示。每个气泡的面积根据每个字符的Total_Words进行缩放。根据性别,气泡被着色(并动态分裂)。

我能够让这个工作得很漂亮。气泡全部形成一个圆圈,并在按钮点击时分开,然后在第二个按钮点击时一起返回。我遇到的问题是,当气泡首次进入屏幕时,碰撞检测效果很好(到处均匀分布的气泡)。但是在我将气泡分成两组(通过第二次模拟)后,碰撞检测不再起作用(即使明确调用)。 Left: collision detection working, all bubbles spaced correctly. Right: Toggle switch "on", bubbles split, but collision detection not working. Bubbles overlap and never stop jittering. 在上图中:左:碰撞检测工作,所有气泡间隔正确。右:切换开“打开”,气泡分裂,但碰撞检测不起作用。气泡重叠,永不停止抖动。

这是我正在使用的bl.ocks演示。 https://bl.ocks.org/ProQuestionAsker/79d0228ae7161e349770e7d553cf4c94

这是我目前使用的整个.js脚本。我认为问题可能出在“添加切换开关”区域,我称之为模拟,但我似乎无法调整任何东西以使其工作。

(function() {
    var width = 400,
    height = 300;

    var svg = d3.select("#chart")
        .append("svg")
        .attr("height", height)
        .attr("width", width)
        .append("g")
        .attr("transform", "translate(0,0)")

    var radiusScale = d3.scaleSqrt().domain([1, 3114]).range([1, 50])

    var forceXSplit = d3.forceX(function(d){
        if(d.Sex === "male") {
            return (width * .30)
        } else {
            return (width * .70)
        }
        }).strength(0.15)

    var forceXCombine = d3.forceX((width)/2).strength(0.1)

    var forceCollide = d3.forceCollide(function(d){
         return radiusScale(d.Total_Words) + 1
         })

    var simulation = d3.forceSimulation()
        .force("x", forceXCombine)
        .force("y", d3.forceY(height / 2).strength(0.09))
        .force("collide", forceCollide) 

    var tooltip = d3.select("body")
        .append("div")
        .style("position", "absolute")
        .style("z-index", "20")
        .style("visibility", "hidden")
        .style("color", "white")
        .style("padding", "8px")
        .style("background-color", "rgba(0, 0, 0, 0.75)")
        .style("border-radius", "6px")
        .style("font", "12px sans-serif")
        .text("");  

// Importing data file

d3.queue()
    .defer(d3.csv, "data.csv")
    .await(ready)

function ready (error, datapoints) {

    var circles = svg.selectAll(".Character")
        .data(datapoints)
        .enter().append("circle")
        .attr("class", "Character")
        .attr("r", function(d){
            return radiusScale(d.Total_Words)
        })
        .style("fill", function(d) { 
            var returnColor;
                if (d.Sex === "male") { returnColor = "#355C7D";
                } else if (d.Sex === "female") {returnColor = "#F67280";}
                return returnColor;
            })
        .on("mouseover", function(d) {
            tooltip.html(d.Character + "<br><br> Words Spoken: " + d.Total_Words);
            tooltip.style("visibility", "visible");
            })
        .on("mousemove", function() {
            return tooltip.style("top", (d3.event.pageY-10)+"px").style("left", (d3.event.pageX+10)+"px");
            })
        .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

// Adding Toggle Switches   

    var atRight = true

    var rect = svg.append("rect")
        .attr("x", 10)
        .attr("y", 10)
        .attr("rx", 22)
        .attr("ry", 22)
        .style("fill", "lightgray")
        .attr("width", 64)
        .attr("height", 40);

    var circle = svg.append("circle")
        .attr("cx", 30)
        .attr("cy", 30)
        .attr("r", 16)
        .style("fill", "white")
        .on("click", function(){
            if(atRight === true){
            simulation 
                .force("x", forceXSplit)
                .alphaTarget(0.2)
                .force("collide", forceCollide)
            setAtRight(!atRight)
            } else {
            simulation
                .restart()
                .force("x", forceXCombine)
                .alphaTarget(0.2)   
            forceCollide.initialize(simulation.nodes());
            setAtRight(!atRight)
            }   
        });

    var setAtRight = function(newValue) {
        atRight = newValue;
        circle.transition().duration(250)
            .attr("cx", (atRight? (30) : (54)))
            .style("fill", "white");
        rect.transition().duration(250)
            .style("fill", atRight? "lightgray" : "#F67280");  
    };


    var res = {
        'getValue': function() { return atRight; },
        'setValue': setAtRight,
        'remove': function() { circle.remove(); }
    };


    simulation.nodes(datapoints)
        .on('tick', ticked)


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

我尝试使用simulation.restart()没有运气。我尝试使用forceCollide.initialize(simulation.nodes());按照此问题here的建议,但气泡仍然相互重叠。

我是d3.js的新手,很可能我错过了一些明显的东西,但我似乎无法让它发挥作用。

非常感谢任何见解。提前谢谢!

1 个答案:

答案 0 :(得分:3)

您的代码中有几个部分可以进行优化和/或简化。这样做有助于清理事情。按钮的单击处理程序可以写为:

.on("click", function(){
  simulation 
    .force("x", atRight ? forceXSplit : forceXCombine)  // 1. Set the force
    .alpha(1)                                           // 2. Reheat
    .restart();                                         // 3. Restart
   setAtRight(!atRight);
});

这将

  1. 将x-force设置为适当的函数
  2. 重新加热模拟和
  3. 重新开始计算。
  4. 由于已经设置了碰撞检测,因此无需重新设置此力或对其进行任何初始化。

    查看更新的Block以获取有效工作示例。请注意,我还在其他一些地方修改了代码,以使其更具可读性并改善气泡的布局。您可能需要进一步调整一些参数,以使布局符合您的需求。