メインコンテンツに移動
ホーム

古松

メインナビゲーション

  • ホーム
  • ビデオ
  • ご連絡

パンくず

  • ホーム
  • d3.jsのフォース(force)を理解するための簡単な例5:オブジェクトが跳ねる効果再現できるd3.forceLink

d3.jsのフォース(force)を理解するための簡単な例5:オブジェクトが跳ねる効果再現できるd3.forceLink

やりたいこと:オブジェクトが弾む効果の作成

  • d3.forceLinkを使用します(d3.js v4)
  • オブジェクト(例:玉)をドラッグして移動、ドロップしたら、決まった場所に弾むような効果
    d3.forceLinkでオブジェクトが弾む効果

実現構想:d3.forceLinkのバネ効果を利用します

  • 前回紹介した「d3.jsのフォース(force)を理解するための簡単な例4:バネのような伸縮効果再現できるd3.forceLink」 コードの元にします
  • 決まった場所(例:四角内)にオブジェクト(例:玉)が弾む
    • オブジェクトをドラッグして移動します
    • オブジェクトをドロップしたら、オブジェクトが四角範囲内に弾みます
  • 説明するため、簡単の二つのオブジェクトのデータセット、リンクセットをします
    // オブジェクト:aaa、bbb 
    data_set = [ {name:"aaa",fx:400,fy:200},  // fx/fyで配置場所に固定
                 {name:"bbb"} ]  ;
    
    // オブジェクト:aaa、bbb の繋ぎリンクセット
    link_set = [ {source:"aaa", target:"bbb"} ]; 

     

ポイント1:d3.forceLinkにつなぐ一つノード(node)を固定します

  • d3.forceLinkでつなぐオブジェクトがノード(node)と言います
  • 一つノードにfx、fy、index、vx、vy、x、y要素があります
  • fx/fyはノードを固定する座標を定義します
    • nullの場合は固定しない
    • 初期値:null
  • 今回の例では最初にオブジェクト(aaa)をx:400,y:200に固定します
  • もう一つオブジェクト(bbb)をaaaに重ねって表示させます
    var line_force = d3.forceSimulation()
      .nodes(data_set)
      .on("tick", ticked)
      .force("link", d3.forceLink(link_set)
      		.id(function(d){ return d.name ; })
                     .distance(0)  // 重ねる表示のため、リンクの距離はゼロにする
                     .strength(0.5))
      .force('charge', d3.forceManyBody().strength(0.8))
      .force("collision", d3.forceCollide(0))  // 重ねる表示のため、両オブジェクトの衝突距離はゼロにする
      ;	

ポイント2:関数(datum())で固定しているオブジェクト(bbb)を決まった場所(四角)に再固定します

  • オブジェクト(aaa)をドロップ時、オブジェクト(bbb)を決まった範囲(四角)に再固定すれば、オブジェクト(aaa)がd3.forceLinkの力で決まった範囲(四角)に弾むことになります
  • オブジェクト(bbb)ノードにバインディングされたデータをアクセスするため、d3.select.datum()の出番です
    d3.select.datum()で個別のノードにバインディングされたデータのアクセス
    // オブジェクト(aaa)をドロップ時の処理
    function dragended(d) {
      if (!d3.event.active) line_force.alphaTarget(0);
      d.fx = null;
      d.fy = null;
      var a = d3.select(".circle-aaa") ; // オブジェクト(bbb)を取得
      a.datum().fy=50;                          // 決まった範囲にxを再固定
      a.datum().fx=400;                        // 決まった範囲にy再固定
     }
  • プログラム全体は以下ようです
    data_set = [ {name:"aaa",fx:400,fy:200},
                       {name:"bbb"} ]  ;
    link_set = [
    	{source:"aaa", target:"bbb"},
    ];    
    
    var width=600, height=300;
    
    d3.select("#content")
      .append("rect")
      .attr("x",380)
      .attr("y",30)
      .attr("width", 40)
      .attr("height",40)
      .attr('fill', 'rgba(0,0,0,0)')
      .attr('stroke', 'red') 
      ;
    
    var force_g = d3.select("#content").append("g") ;
    
    var links =  force_g.selectAll(".link")
      .data(link_set)
      .enter()
      .append("line")
      .attr("class", "link")
      .attr("stroke", "none")
      .attr("stroke-weight", 1)
      ;
    
    var circles =  force_g.selectAll("circle")
      .data(data_set)
      .enter()
      .append("circle")
      .attr("class",function(d){ return "circle-"+d.name })
      .attr("r", 15)
      .attr("fill","lightblue")
      .call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended)
              )
      ;
    
    var line_force = d3.forceSimulation()
      .nodes(data_set)
      .on("tick", ticked)
      .force("link", d3.forceLink(link_set)
      		  .id(function(d){ return d.name ; })
                      .distance(0)
                      .strength(0.5))
      .force('charge', d3.forceManyBody().strength(0.8))
      .force("collision", d3.forceCollide(0))
      ;	
    
    function dragstarted(d) {
      if (!d3.event.active) line_force.alphaTarget(0.9).restart();
      d.fx = d.x;
      d.fy = d.y;
    }
    
    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    
    function dragended(d) {
      if (!d3.event.active) line_force.alphaTarget(0);
      d.fx = null;
      d.fy = null;
      var a = d3.select(".circle-aaa") ;
      a.datum().fy=50;
      a.datum().fx=400;
     }
    
    function ticked(){
    	circles
      	.attr("cx", function(d){ return d.x; })
        .attr("cy", function(d){ return d.y; }) ;
        
      links
      .attr("x1", function(d) {   return d.source.x; })
      .attr("y1", function(d) {	return d.source.y; })
      .attr("x2", function(d) {	return d.target.x; })
      .attr("y2", function(d) {	return d.target.y; })
      ;
    }
    

     

d3.jsのフォース(force)を理解するための簡単な例4:バネのような伸縮効果再現できるd3.forceLink

d3.forceLinkがオブジェクト要素間のバネの伸縮効果の再現

  • d3.forceにある一つ重要な要素d3.forceLinkよりバネの伸縮効果を生み出すことができます
  • 特にドラッグ/ドロップ併合に使用すると、よりバネの伸縮効果の体験はできます
  • d3.forceLinkはsvgのlineエレメントでLinkを描画します
    • d3.forceSimulationのtickイベントより、動的にオブジェクト間の距離を計算しながら、lineを描画します
  • d3.forceLinkのバネ効果の強さはcollision/charge/centerなどの要素の設定より調整されます

作成要点1:オブジェクト要素のリンク元(source)ターゲット先(target)設定

  • 描画のオブジェクトデータセットを定義します
  • オブジェクト間にリンクをつけるターゲット先にのデータセットを定義します
    // 描画するオブジェクトのデータセット
    data_set = [{name:"aaa"},
                      {name:"bbb"},
                      {name:"ccc"},
                      {name:"ddd"},
                      {name:"eee"},
                      {name:"fff"}  ]  ;
    
    // オブジェクト間のリンク元とリンク先定義
    link_set = [
    	{source:"aaa", target:"bbb"}, 
    	{source:"bbb", target:"ccc"},
    	{source:"ccc", target:"ddd"},
    	{source:"ddd", target:"eee"},
    	{source:"eee", target:"fff"},
    	{source:"fff", target:"aaa"},
    ];                  
    • リンク元: sourceで定義
    • リンク先: target で定義
    • 「source」、「target」は決まりキーワード、変えることはできません

作成要点2:d3.forceSimulationでd3.forceLinkを定義

  • d3.forceSimulationでforceのオブジェクト描画定義時にd3.forceLinkを定義します
    var line_force = d3.forceSimulation()
      .nodes( data_set )            // オブジェクトのデータセット
      .on("tick", ticked)
      .force("link", d3.forceLink(link_set)        // リンク元とリンク先のデータセット
      		  .id(function(d){ return d.name ; })  // オブジェクトデータセットにあるリンク要素フィールド設定
              .distance(20) )
      .force("center", d3.forceCenter(width/2, height/2))
      .force('charge', d3.forceManyBody().strength(15))
      .force("collision", d3.forceCollide(15))        // オブジェクトのぶつけ合い距離
      ;	
    • d3.forceLink().id()でのidがリンク元/先にのデータフィールド名を定義

作成要点3:d3.tickイベントのリスナーでリンクの描画要素(line)の始点/終了設定

  • d3.forceの描画設定はd3.tickイベントリスナーの処理で実現します
  • d3.forceLinkはsvgの要素(line)の始点/終点を定義して描画します
    function ticked(){
    	circles
      	.attr("cx", function(d){ return d.x; })
        .attr("cy", function(d){ return d.y; }) ;
        
      links    // svgのLineの始点/終点設定
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) {	return d.source.y; })
      .attr("x2", function(d) {	return d.target.x; })
      .attr("y2", function(d) {	return d.target.y; });
    }

    d3.forceLinkのデータセットおよび描画の始点/終点設定

  • 上記例の全コードは以下のようです
    data_set = [			{name:"aaa"},
                      {name:"bbb"},
                      {name:"ccc"},
                      {name:"ddd"},
                      {name:"eee"},
                      {name:"fff"}  ]  ;
    link_set = [
    	{source:"aaa", target:"bbb"},
    	{source:"bbb", target:"ccc"},
    	{source:"ccc", target:"ddd"},
    	{source:"ddd", target:"eee"},
    	{source:"eee", target:"fff"},
    	{source:"fff", target:"aaa"},
    ];                  
    var width=600, height=300;
    
    var force_g = d3.select("#content").append("g") ;
    
    var links =  force_g.selectAll(".link")
    	.data(link_set)
      .enter()
      .append("line")
      .attr("class", "link")
      .attr("stroke", "#000")
      .attr("stroke-weight", 1)
      ;
    
    var circles =  force_g.selectAll("circle")
      .data(data_set)
      .enter()
      .append("circle")
      .attr("class","test-circle")
      .attr("r", 5)
      .attr("fill","lightblue")
      .call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended)
              )
      ;
    
    var line_force = d3.forceSimulation()
    	.nodes(data_set)
      .on("tick", ticked)
      .force("link", d3.forceLink(link_set)
      								.id(function(d){ return d.name ; })
                      .distance(20) )
      .force("center", d3.forceCenter(width/2, height/2))
      .force('charge', d3.forceManyBody().strength(15))
      .force("collision", d3.forceCollide(15))
      ;	
    
    function dragstarted(d) {
      if (!d3.event.active) line_force.alphaTarget(0.9).restart();
      d.fx = d.x;
      d.fy = d.y;
    }
    
    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    
    function dragended(d) {
      if (!d3.event.active) line_force.alphaTarget(0);
      d.fx = null;
      d.fy = null;
     }
    
    function ticked(){
    	circles
      	.attr("cx", function(d){ return d.x; })
        .attr("cy", function(d){ return d.y; }) ;
        
      links
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) {	return d.source.y; })
      .attr("x2", function(d) {	return d.target.x; })
      .attr("y2", function(d) {	return d.target.y; });
    }

     


検索フォーム

カテゴリ別

  • laravel
  • drupal
  • javascript
  • windows
  • html
  • mysql
  • php
  • apache
  • css
  • SEO
  • video
  • wordpress
  • linux
  • python
  • Electron
  • Visual Studio Code

google ads