2017-01-05 8 views
3

ボタンを押したときに気泡がばらばらになって元に戻るバブルグラフがあります。トグル後衝突検出が失われました(d3v4)

私の場合、バブルグラフに入力する生データには、Character、Total_Words、Sexの3つの列があります。バブルグラフの働きは、それぞれのキャラクターが独自のバブルで表現されています。各バブルの領域は、各文字のTotal_Wordsに基づいてスケーリングされます。バブルはセックスに応じて色付けされ(そして動的に分割されます)

私はこれを美しく動作させることができました。バブルはすべてボタンのクリックで分離して円になり、2番目のボタンをクリックすると一緒に戻ります。私が持っている問題は、気泡が最初に画面に現れたときに、衝突検出が美しく機能することです(どこにでも均等に気泡があります)。しかし、気泡を2つのグループに分けた後(2番目のシミュレーションで)、衝突検出は(明示的に呼び出されても)機能しなくなりました。 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スクリプト全体です。この問題は、私がシミュレーションを呼び出す「Toggle Switchesの追加」領域にあるかもしれないと思うが、この作業をするために何かを調整することはできない。

(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()を使ってみました。私はこの質問hereから提案されたようにforceCollide.initialize(simulation.nodes());を使ってみましたが、まだ泡はお互いに重なっています。

私はd3.jsに新しく、明らかに何かが欠落している可能性がありますが、これを動作させることはできません。

洞察力は非常に高く評価されます。前もって感謝します!

+0

実際に役立つデモがあります。これの実行可能なバージョンを提供できますか? – altocumulus

+0

@altocumulus、すばらしい返答をいただきありがとうございます!私は[リンク](https://bl.ocks.org/ProQuestionAsker/79d0228ae7161e349770e7d553cf4c94)を追加するための説明を編集しました。 –

答えて

3

コードには、最適化または簡略化できるセクションがいくつかあります。そうすることは物事をクリアするのに役立ちます。あなたのボタンのクリックハンドラを以下のように書くことができる

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

これます

  1. が再起動し、適切な関数に
  2. 再熱シミュレーションと
  3. をX-力を設定しました計算。

すでに衝突検出が設定されているため、この強制を再設定するか、初期化を行う必要はありません。

動作例については、Blockを更新してください。私は、他の場所でもコードを変更して読みやすくし、バブルのレイアウトを改善しています。レイアウトをニーズに合わせるために、いくつかのパラメータをさらに微調整する必要があるかもしれません。

+0

あなたは私の好きな人かもしれません!本当にありがとう!どのように動作するのか把握するために時間を費やしていますが、クリックハンドラを切り替えるだけで衝突検出の問題が解決されます。この修正はとても単純でエレガントであることが判明しました。 –

+0

同様に、 'simulation.force(" x "、null)'と同様の方法で強制的に_remove_できることを指摘して、再加熱、再起動などを指摘すると良いでしょう。質問と答えの両方に感謝します! – ibgib