D3.js:“即时”向数组添加的元素不会刷新 svg 图形

Posted

技术标签:

【中文标题】D3.js:“即时”向数组添加的元素不会刷新 svg 图形【英文标题】:D3.js: "On the fly" added elements to array are not refreshing the svg graphic 【发布时间】:2017-03-20 05:46:54 【问题描述】:

我喜欢一个泡泡排,在任何给定时间我都有 6 个泡泡。该数组有 6 个 json 对象。该代码仅显示加载时首次添加的圆圈。但是当我修改数组时,我想删除第一个气泡并在行的右端添加一个气泡。我正在使用 setInterval 将一个元素插入到数组中以对其进行测试。由于我正在记录数组的状态,因此数组正在正确更改,但 svg 图形未刷新。我只是不知道问题是否在于重新利用 createElementGroup() 或在这种情况下如何删除节点(我看到常见的情况是使用 exit() d3 方法,但我不确定在哪里实现它这种特殊情况)。

此外,当我删除和添加元素时,我应该将过渡放在哪里以使其平滑?现场演示在这里:

http://codepen.io/juanf03/pen/BQyYBq(您可以点击气泡查看它展开并显示数据,这样我会检查这是正确的节点)

代码:

//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;
    

编辑:在 Gerardo Furtado 的伟大建议之后修复了代码。我发布它以防有人遇到类似问题:

//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

【问题讨论】:

【参考方案1】:

在我的情况下,我有 svg 圈子,必须 .remove() 他们并添加具有相同类的全新圈子。但是在d3.selectAll(".dynamic_child).on("click"... 上没有注册。我发现了一个不深入研究 d3.js 而是使用 jQuery 的解决方法。

我的解决方案如下:

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

static_parent 只是您保留的父 div,而 dynamic_child 是您动态删除和添加的元素(html 或 svg)。

Original source post

【讨论】:

【参考方案2】:

您需要“进入”、“退出”和“更新”选项。

首先,我们绑定数据(用一个key函数):

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

然后,我们设置回车选择:

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

现在有一个重要说明:由于这是 D3 v4.x,您需要 merge 选择才能获得有效的更新选择:

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

最后是退出选择:

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

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

【讨论】:

两个问题:1)你知道我的点击事件为什么死了吗?它曾经将气泡扩大两倍大小,现在修改后它不起作用 2)你为什么在数据集上使用“关键功能”?为什么要将元素绑定到那个数字? key函数“以一个数据点为输入,返回一个对应的key:一个字符串,比如名字,唯一标识数据点。”关于您的点击事件:我没有为您更改代码的其他部分,这是您现在的工作,我只是向您展示了如何处理选择。您必须相应地重写整个代码。 谢谢,我得看看哪里出了问题,但我还有一个问题,是不是每次都在这里重新渲染元素codepen.io/juanf03/pen/ZBYRNK?.....I 只是不想知道正确的方法....原因我将创建退出和更新放在要重用的函数上...单击事件是否与动态生成的元素有问题,就像以前在 jquery 中发生的那样?这就是我问的原因 是的,您可能会遇到一些问题。最佳做法是将点击事件置于“输入+更新”选择之后。 谢谢....我已经修复了它.....codepen.io/juanf03/pen/ZBYRNK?editors=0010我确实在 enter+update 和选择器之后更改了它,而不是使用 elementGroup.on('click',我使用d3.selectAll('.element-group').on('click')

以上是关于D3.js:“即时”向数组添加的元素不会刷新 svg 图形的主要内容,如果未能解决你的问题,请参考以下文章

理解d3.js中的UpdateEnterExit

如何使用 D3.js 将图像添加到 svg 容器

用新的替换 d3.js 路径转换?

如何在 D3.js 中为网络图的矩形节点添加文本?

向 MongoDB 添加元素后视图未更新

d3.js学习