使用Javascript D3库,如何在选择rects时替换数据?

时间:2012-11-01 17:49:06

标签: javascript svg d3.js

我有一个带有count和date对象的对象数组。使用它们在时间尺度上创建一系列rects。效果很好。但是我想使用jQuery UI中的滑块来允许用户将结果缩小到整个范围的一部分,并更好地查看更精细的细节。但是我无法弄清楚如何从我的矩形选择中动态添加和删除数据点。这是代码。有问题的部分位于滑块幻灯片事件处理程序的最底部。我非常感谢有关如何正确管理数据的任何帮助。

  var data=env.client.data;
  var selected=env.client.selected;

  var max=_.max(data,function(element){
    return(element.count);
  }).count;

    var height=250;
    var width=960;

    var padding={
      bottom:25,
      left:30,
      right:10,
      top:10
    };

    var barwidth=(width-(padding.left+padding.right))/data.length;

    var chart=d3.select('#chart')
                .html('')
                .append('svg')
                .attr('height',height)
                .attr('width',width);

    var scale={};

    scale.x=d3.time
              .scale
              .utc()
              .domain([d3.first(data).date,d3.last(data).date])
              .range([padding.left,width-padding.right]);

    scale.yy=d3.scale
               .linear()
               .domain([0,max||1])
               .range([height-padding.bottom,padding.top]);

    scale.yh=d3.scale
               .linear()
               .domain([0,max||1])
               .range([0,height-(padding.bottom+padding.top)]);

    var axis={};

    axis.x=d3.svg
             .axis()
             .scale(scale.x)
             .orient('bottom');

    axis.y=d3.svg
             .axis()
             .scale(scale.yy)
             .orient('left')
             .ticks(12);

    if(max<12) axis.y.ticks(max);

    var xa=chart.append('g')
                .attr('class','xAxis')
                .attr('transform','translate(0,'+(height-padding.bottom)+')')
                .call(axis.x);

    var ya=chart.append('g')
                .attr('class','yAxis')
                .attr('transform','translate('+(padding.left)+',0)')
                .call(axis.y);

    var rects=chart.selectAll('rect')
                   .data(data)
                   .enter()
                   .append('rect')
                   .attr('x',function(d){
                     return(scale.x(d.date));
                   })
                   .attr('y',function(d){
                     return(scale.yy(d.count));
                   })
                   .attr('height',function(d){
                     return(scale.yh(d.count));
                   })
                   .attr('fill',function(d){
                     return(d3.hsl(120-(120*(d.count/50)),1,0.1));
                   })
                   .attr('width',barwidth)
                   .attr('title',function(d){
                     return(d.tooltip);
                   })
                   .on('mouseover',function(d){
                     d3.select(this)
                       .attr('fill',d3.hsl(120-(120*(d.count/50)),1,0.5));
                   })
                   .on('mouseout',function(d){
                     d3.select(this)
                       .attr('fill',d3.hsl(120-(120*(d.count/50)),1,0.1));
                   })
                   .on('click',function(d){
                     var selected=moment(d.date.getTime()).utc();
                     env.client.range.selected=selected;
                     $('#client-datetime').val(selected.format('YYYY MMM DD, HH:mm'));
                     _.publish('client date changed');
                   });

    var be=d3.first(data).date.getTime();
    var ee=d3.last(data).date.getTime();

    $('#slider').slider({
      range:true,
      min:be,
      max:ee,
      values:[be,ee],
      slide:function(event,ui){
        var bd=new Date(ui.values[0]);
        var ed=new Date(ui.values[1]);
        var subdata=_.filter(data,function(element){
          return((element.date>=bd)&&(element.date<=ed));
        });
        var barwidth=(width-(padding.left+padding.right))/subdata.length;
        scale.x.domain([bd,ed]);
        xa.call(axis.x);
        rects.data(subdata)
             .remove();
        rects.enter()
             .append('rect')
             .attr('x',function(d){
               return(scale.x(d.date))
             })
             .attr('width',barwidth);
      }
    });

1 个答案:

答案 0 :(得分:2)

据我所知,你走在正确的轨道上。

您获得subdata,然后将其应用于

    rects.data(subdata)
         .remove();// this is wrong!!

我不确定你为什么要调用remove(),但这实际上是删除了子数据中的所有DOM元素。这应该被拿出来。

接下来,(我很确定)您需要将rects重新分配给新绑定。即。

    rects = rects.data(subdata);

接下来,您会附加所有enter()个字词。但是你错过了一些设置('y','fill'等属性)。但是,您还需要更新未添加或从集合中删除的所有子数据成员。所以它变成了:

    rects.enter()
         .append('rect')
         .attr('y',function(d) {
           return(scale.yy(d.count));
         })
         .attr('height',function(d){
           return(scale.yh(d.count));
         })
         .attr('fill',function(d){
           return(d3.hsl(120-(120*(d.count/50)),1,0.1));
         });

    rects// This happens for all updated AND entering rects
         .attr('x',function(d){
           return(scale.x(d.date))
         })
         .attr('width',barwidth);

最后,您需要删除不再属于子数据的那些:

    rects.exit().remove();

这个逻辑很像你在init脚本中的更高级别(除了仅在enter()'集合上运行的init版本)。所以你应该把它全部转移到一个函数中:

updateChart(newData) {
    rects = rects.data(subdata);
    rects.enter()
    // etc....

    rects.remove()
}

并在初始时和正在应用新子数据时调用此函数。