简体   繁体   English

D3.js:“动态”添加到数组的元素不会刷新 svg 图形

[英]D3.js: "On the fly" added elements to array are not refreshing the svg graphic

I have like a bubble row in which I have 6 bubbles at any given time.我有一个气泡行,在任何给定时间我都有 6 个气泡。 The array has 6 json objects.该数组有 6 个 json 对象。 The code is displaying only the circles that were first added on loading.该代码仅显示在加载时首次添加的圆圈。 But when I modify the array I want to remove the first bubble and add one bubble to the right end of the row.但是当我修改数组时,我想删除第一个气泡并在行的右端添加一个气泡。 I'm using a setInterval to insert an element into the array to test it.我正在使用 setInterval 将一个元素插入到数组中以对其进行测试。 The array is changing properly since i'm logging the state of the array, but the svg graphic is not refreshed.数组正在正确更改,因为我正在记录数组的状态,但 svg 图形没有刷新。 I just don't know if the problem is with reutilizing the createElementGroup() or how to remove nodes in this case(I saw that the common case is using the exit() d3 method but I'm not sure where to implement it in this particular case).我只是不知道问题是否与重新利用 createElementGroup() 或在这种情况下如何删除节点有关(我看到常见的情况是使用 exit() d3 方法,但我不确定在哪里实现它这种特殊情况)。

Additionally, Where should I put the transition to make it smooth when I remove and add an element?.此外,当我删除和添加元素时,我应该在哪里放置过渡以使其平滑?。 The live demo is here:现场演示在这里:

http://codepen.io/juanf03/pen/BQyYBq (you can click on the bubbles to see it expand and show the data, in that way I check that is the correct node) http://codepen.io/juanf03/pen/BQyYBq (你可以点击气泡看到它展开并显示数据,这样我就会检查这是正确的节点)

The code:代码:

//listener that will be executed on setIntervalCheck to update the graphic   
setInterval(function(){
  moveForwardOnBubbleList();
  createElementGroup();
  }, 100000);



var profile_pic_url="https://scontent.fsst1-2.fna.fbcdn.net/v/t1.0-9/13680856_103268503450198_1479797031996897052_n.jpg?oh=f43bced91822fb210c8be8a410825da9&oe=58D46460";

var dataset = [{unique_followers: 5, profile_pic:profile_pic_url}, {unique_followers: 10, profile_pic:profile_pic_url},{ unique_followers: 15, profile_pic:profile_pic_url}, { unique_followers: 20, profile_pic:profile_pic_url}, { unique_followers: 25, profile_pic:profile_pic_url}, {unique_followers: 40, profile_pic:profile_pic_url} ];

var w=600,h=600;

var svg=d3.select("body").append("svg")
                                  .attr("width",w)
                                  .attr("height",h);

//1st level:All circles group
var circlesGroup = svg.append("g").classed("general-group",true);
//2nd level: Group of circle and text
var elementGroup;

var circle;

var circleAttributes;
//create g's of existing data
createElementGroup();

elementGroup.on('click', function(d,i){
  var that=this;

  d3.selectAll('.element-group').each(function(d,i) {
    if(this.id!==that.id){
      d3.select(this).classed("selected",false);
    }
  });

  d3.select(this).classed("selected", !d3.select(this).classed("selected"));

  });

//adding circular background image to the circles
//var circlesSelection=svg.selectAll('circle');


function createElementGroup(){
  elementGroup = circlesGroup
  .selectAll('circle')
  .data(dataset)
  .enter()
  .append("g").classed("element-group",true);

  circle=elementGroup.append('circle');

  circleAttributes = circle
  .attr("r", 20)
  .attr("stroke","black")
  .attr("fill", "white")
  .classed("circle",true);

  //text to show
   elementGroup.append("text")
      .attr("text-anchor", "middle")
   .text(function(d) {
     return parseInt(d.unique_followers);
   })
     .style("pointer-events","none")
     .classed('tweet-number', true);

     //image to show as background

  //element group positioning for the text to be inside circle
  elementGroup.attr("transform", function(d,i){
    return "translate(" + (i*80+45) + "," + h/2 + ")"; 
});

  elementGroup.attr( "fill-opacity", 0 ).transition().duration(500).attr( "fill-opacity", 1 );
  elementGroup.attr("id", function(d, i) { return "c"+i; });


}

function addBubbleLast(){
    dataset.push({unique_followers: 40, profile_pic:profile_pic_url});
}

function removeFirstBubble(){
  dataset.shift();

}

function moveForwardOnBubbleList(){
  addBubbleLast();
  removeFirstBubble();
}



/*CSS*/
 body
        {
          /*padding-top: 50px;*/
          padding-left: 100px;
        }

        .tweet-number{
         opacity:0.25;
        }

        .circle{

        }


        .selected *{
          transform: scale(2);
          transition: all 0.5s ease, opacity 0.5s ease;
          opacity:1.0;
    }

EDIT: Fixed code after the great suggestions of Gerardo Furtado .编辑:在Gerardo Furtado的伟大建议之后修复了代码。 I post it in case someone comes across a similar problem:我发布它以防有人遇到类似的问题:

//listener that will be executed on setIntervalCheck to update the graphic   
setInterval(function(){
  moveForwardOnBubbleList();
  createElementGroup();
  }, 6000);



var profile_pic_url="https://scontent.fsst1-2.fna.fbcdn.net/v/t1.0-9/13680856_103268503450198_1479797031996897052_n.jpg?oh=f43bced91822fb210c8be8a410825da9&oe=58D46460";

var dataset = [{unique_followers: 5, profile_pic:profile_pic_url}, {unique_followers: 10, profile_pic:profile_pic_url},{ unique_followers: 15, profile_pic:profile_pic_url}, { unique_followers: 20, profile_pic:profile_pic_url}, { unique_followers: 25, profile_pic:profile_pic_url}, {unique_followers: 40, profile_pic:profile_pic_url} ];

var w=900,h=600;

var svg=d3.select("body").append("svg")
                                  .attr("width",w)
                                  .attr("height",h);

//1st level:All circles group
var circlesGroup = svg.append("g").classed("general-group",true);
//2nd level: Group of circle and text
var elementGroup;

var circle;

var circleAttributes;
//create g's of existing data
createElementGroup();



//adding circular background image to the circles
//var circlesSelection=svg.selectAll('circle');


function createElementGroup(){
  elementGroup = circlesGroup
  .selectAll('.element-group')
  .data(dataset, function(d){ return d.unique_followers});
 //doesn't work the exit transition 
   var elementExit = elementGroup.exit().transition().duration(1000).style("opacity", 0).remove();

  var elementEnter = elementGroup.enter()
  .append("g").classed("element-group",true).style("opacity",0);

    elementEnter.merge(elementGroup).attr("transform", function(d,i){

   //option 1 generation by mod   
   if(i%2===0){   
    return "translate(" + (i*80+45) + "," + h/1.55 + ")"; 
   }else{
    return "translate(" + (i*80+45) + "," + h/1.45 + ")"; 

   }

  /*   
  //option 2 random
  var random= (Math.random() * (1.6 - 1.45) + 1.45).toFixed(4);

         return "translate(" + (i*80+45) + "," + h/random + ")";*/ 


}).transition().duration(2000).style("opacity", 1.0);

    circle=elementEnter.append('circle');



  circleAttributes = circle
  .attr("r", 20)
  .attr("stroke","black")
  .attr("fill", "white")
  .classed("circle",true);

  d3.selectAll('.element-group').on('click', function(d,i){
  var that=this;
  d3.selectAll('.element-group').each(function(d,i) {
    if(this.id!==that.id){
      d3.select(this).classed("selected",false);
    }
  });

  d3.select(this).classed("selected", !d3.select(this).classed("selected"));

  });

  //text to show
   var texts = elementEnter.append("text")
      .attr("text-anchor", "middle")
   .text(function(d) {
     return parseInt(d.unique_followers);
   })
     .style("pointer-events","none")
     .classed('tweet-number', true);

     //image to show as background

  //element group positioning for the text to be inside circle



}

function addBubbleLast(){

  var random=Math.floor(Math.random() * (40)) + 1;


    dataset.push({unique_followers: random, profile_pic:profile_pic_url});
}

function removeFirstBubble(){
  dataset.shift();

}

function moveForwardOnBubbleList(){
  addBubbleLast();
  removeFirstBubble();
}

//CSS

    body
    {
      /*padding-top: 50px;*/
      padding-left: 100px;
    }

    .tweet-number{
     opacity:0.25;
    }

    .circle{

    }

 .selected *{
      transform: scale(2);
      transition: all 0.5s ease;
      opacity:1.0;
}

.element-group *{
  transition: all 0.5s ease;
}

circle + text{
}

You need an "enter", "exit" and "update" selections.您需要“进入”、“退出”和“更新”选项。

First, we bind the data (with a key function):首先,我们绑定数据(使用key函数):

elementGroup = circlesGroup
    .selectAll('.element-group')
    .data(dataset, function(d){ return d.unique_followers});

Then, we set the enter selection:然后,我们设置输入选择:

var elementEnter = elementGroup.enter()
    .append("g").classed("element-group",true);

Now an important note: as this is D3 v4.x, you need to merge the selections to have a working update selection:现在有一个重要的注意事项:由于这是 D3 v4.x,您需要merge选择以获得有效的更新选择:

elementEnter.merge(elementGroup).attr("transform", function(d,i){
    return "translate(" + (i*80+45) + "," + h/2 + ")"; 
});

Finally, the exit selection:最后,退出选择:

var elementExit = elementGroup.exit().remove();

Here is your CodePen: http://codepen.io/anon/pen/Wobyem这是您的 CodePen: http ://codepen.io/anon/pen/Wobyem

In my case I had svg circles and had to .remove() them and add completely new ones with the same class.就我而言,我有 svg 圈子,不得不.remove()它们并添加具有相同类的全新圈子。 But on d3.selectAll(".dynamic_child).on("click"... didn't register it. I found a workaround that doesn't dive deep into d3.js and instead uses jQuery .但是在d3.selectAll(".dynamic_child).on("click"...没有注册它。我找到了一个没有深入研究 d3.js 而是使用jQuery的解决方法。

My solution is the following:我的解决方案如下:

$( ".static_parent" ).on("click", '.dynamic_child', function(){
    console.log("I'm working!")
    }

where the static_parent is just parent div that you keep and the dynamic_child is element(s) (html or svg) that you remove and add on the fly.其中static_parent只是您保留的父 div,而dynamic_child是您随时删除和添加的元素(html 或 svg)。

Original source post原始来源帖子

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM