D3似乎更新了错误的SVG元素

时间:2022-11-20 14:35:21

I have a d3 layout that is intended to produce 3 charts:

我有一个d3布局,旨在生成3个图表:

  1. Custom item layout w/ transitions
  2. 自定义项目布局w /过渡

  3. Pie chart for sales by category (with transitions at some point)
  4. 按类别销售的饼图(在某些时候有过渡)

  5. Bar chart for top 5 performing items w/ transitions.
  6. 前5个表演项目的条形图与过渡。

1 & 2 work OK but when I add the third chart, I see some strange behavior. The intention is to create a bar chart where the width of each bar is tied to the sales metric for the top N items determined like so:

1和2工作正常但是当我添加第三个图表时,我看到一些奇怪的行为。目的是创建一个条形图,其中每个条形的宽度与前N个项目的销售指标相关联,如下所示:

data = data.filter(function(d){ return d.date === someDate});

var cf = crossfilter(data);
var salesDimension = cf.dimension(function(d){ return d.sales; });

topData = salesDimension.top(5);

The problem is that instead of drawing the bar chart, my code somehow overwrites the position of 5 items in chart 1 and moves them back to the origin. If I change 5 to 10 above then 10 items are overwritten and so on.

问题是,我的代码不是绘制条形图,而是以某种方式覆盖图表1中5个项目的位置并将它们移回原点。如果我将5更改为10,则会覆盖10个项目,依此类推。

I double checked the scope of my variables and even tried changing the names of everything in my drawTopItems() which made no difference. I suspect that I am doing something incorrectly when it comes to selecting the svg element or applying classes to the svg group elements that I want to modify but I can't for the life of me see what. Can anyone tell me what I might be doing wrong?

我仔细检查了我的变量的范围,甚至尝试更改我的drawTopItems()中的所有内容的名称,这没有任何区别。我怀疑我在选择svg元素或将类应用于我想要修改的svg组元素时做错了什么但我不能为我的生活看到什么。谁能告诉我我可能做错了什么?

Here is my issue in a fiddle: https://jsfiddle.net/Sledge/4eggpd5e/12/.

这是我的小问题:https://jsfiddle.net/Sledge/4eggpd5e/12/。

Here is my javascript code:

这是我的javascript代码:

var item_width = 40, item_height = 60;
var margin = {top: 50, right: 50, bottom: 75, left: 40},
    width = 700 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([0, height]);
var colorScale = d3.scaleLinear().domain([500,3000]).range(["white","#4169e1"]);

// Pie Chart parameters
var pieWidth = 300, pieHeight = 300;
var outerRadius = Math.min(pieWidth, pieHeight) / 2,
        innerRadius = outerRadius * .50;
var pieColor = d3.scaleOrdinal(['#42b9f4','#3791f2','#374ff1','#25b22e','#107222']);            // custom color scale
var legendRectSize = 18;                                  // NEW
var legendSpacing = 4;                                    // NEW

// Top Item Parameters
var topItemMargin = {top:25, right:25, bottom: 25, left: 25};
var topItemWidth = 300 - topItemMargin.left - topItemMargin.right, 
      topItemHeight = 300 - topItemMargin.top - topItemMargin.bottom;
var topItemXScale = d3.scaleLinear().range([0, topItemWidth]);
var barHeight = 20, barSpacing = 5;


/* SVG */
var svgItemLayout = d3.select("#item_layout")
  .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var svgPieChart = d3.select("#pie_chart")
    .append("svg") 
        .attr("width", pieWidth)
        .attr("height", pieHeight)
    .append("g")
        .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")") ;

var svgTopItems = d3.select("#top_items")
    .append("svg")
        .attr("width", topItemWidth)
        .attr("height", topItemHeight)
    .append("g")
        .attr("transform", "translate(" + topItemMargin.left + "," + topItemMargin.top + ")");


/* DRAW FUNCTIONS */
// a single function to draw 
function drawItemLayout(data, someDate){

  data.forEach(function(d) {
    d.x_pos = +d.x_pos;
    d.y_pos = +d.y_pos;
    d.sales = +d.sales;
  });

  // pre-filter data
  data = data.filter(function(d){ return d.date === someDate});

  var x_offset = 5, y_offset = 5;

  x.domain(d3.extent(data, function(d) { return d.x_pos; }));            // set the x domain
  y.domain(d3.extent(data, function(d) { return d.y_pos; }));            // set the y domain

  // create an update selection with a key function
  var g_sel = svgItemLayout.selectAll("g")
      .data(data, function(d){
        return d.item_name;
      });

  // get rid of those leaving the update
  g_sel.exit().remove();

  // our entering g
  var g_ent = g_sel.enter()
    .append("g");

  // add our rects to our g
  g_ent.append("rect")
    .attr("class", "dot")                   // wonder if I really need this class?
    .attr("width", item_width)
    .attr("height", item_height)
    .attr("rx", 3)
    .attr("ry", 3)
    .style("fill", function(d){ return colorScale(d.sales); })     // color factor variable
    .style("fill-opacity", 0.5);

  // add our text to our g
  g_ent.append("text")
    .attr("font-size", 10)
     .attr("text-anchor", "middle")
     .attr("fill", "black")
     .attr("dx", item_width/2)
     .attr("dy", item_height/2)
     .text(function(d){ return d.item_name; });

  // UPDATE + ENTER selection
  g_sel = g_ent.merge(g_sel);

  // move them into position with transition
  g_sel
    .transition()
    .duration(1200)
    .delay(function(d, i) { return i *40; })
    .attr("transform", function(d){
      return "translate(" + (x(d.x_pos) + x_offset)  + "," + (y(d.y_pos) + y_offset) + ")";
    });

}

function drawPieChart(data, someDate) {

    data.forEach(function(d) {
    d.x_pos = +d.x_pos;
    d.y_pos = +d.y_pos;
    d.sales = +d.sales;
  });

   // pre-filter data
  data = data.filter(function(d){ return d.date === someDate});

    var cf = crossfilter(data);
    var salesDimension = cf.dimension(function(d){ return d.sales; });
    var categoryDimension = cf.dimension(function(d){ return d.category; });
    var categoryGroup = categoryDimension.group();

    function reduceInitial(p, v) {
        return {
            sales : 0,
            count : 0
        };
    }

    function reduceAdd(p, v) {
        p.sales = p.sales + v.sales;
        p.count = p.count + 1;
        return p;
    }

    function reduceRemove(p, v) {
        p.sales = p.sales - v.sales;
        p.count = p.count - 1;
        return p;
    }

    categoryAggregated = categoryGroup.reduce(reduceAdd, reduceRemove, reduceInitial).all();

    var arc = d3.arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius);

    var pie = d3.pie()
    .value(function(d) { return d.value.sales; })
    .sort(null);

  var path = svgPieChart.selectAll('path')
      .data(pie(categoryAggregated))
      .enter()
      .append('path')
      .attr('d', arc)
      .attr('fill', function(d, i) { return pieColor(i);});

    // Add a legend:
    var legend = svgPieChart.selectAll('.legend')
      .data(pieColor.domain())
      .enter()
      .append('g')
      .attr('class', 'legend')
      .attr('transform', function(d, i) {
        var height = legendRectSize + legendSpacing;
        var offset =  height * pieColor.domain().length / 2;
        var horz = -3 * legendRectSize;
        var vert = i * height - offset;
        return 'translate(' + horz + ',' + vert + ')';
      });

    legend.append('rect')
      .attr('width', legendRectSize)
      .attr('height', legendRectSize)
      .style('fill', pieColor)
      .style('stroke', pieColor);

    legend.append('text')
      .attr('x', legendRectSize + legendSpacing)
      .attr('y', legendRectSize - legendSpacing)
        .text(function(d) { return categoryAggregated[d].key; });                       // returns text based on data index

}

function drawTopItems (data, someDate) {

    data.forEach(function(d) {
    d.x_pos = +d.x_pos;
    d.y_pos = +d.y_pos;
    d.sales = +d.sales;
  });

   // pre-filter data
  data = data.filter(function(d){ return d.date === someDate});

    var cf = crossfilter(data);
    var salesDimension = cf.dimension(function(d){ return d.sales; });

    topData = salesDimension.top(5);

    topItemXScale.domain(d3.extent(topData, function(d) { return d.sales; }));        // set the x domain

    var f_sel = svgTopItems.selectAll("g")
        .data(topData,function(d){ return d.item_name; }).enter();

    f_sel.exit().remove();

    var f_ent = f_sel.enter().append("g");

    f_ent.append("rect")
    .attr("class", "dot")                   // wonder if I really need this class?
    .attr("width", function(d){ return d.sales })
    .attr("height", barHeight)
    .style("fill","#351eff")     // color factor variable
    .style("fill-opacity", 0.75);

  // add our text to our g
  f_ent.append("text")
    .attr("font-size", 10)
     .attr("text-anchor", "left")
     .attr("fill", "black")
     .attr("dx", item_width/2)
     .attr("dy", item_height/2)
     .text(function(d){ return d.item_name});

  // UPDATE + ENTER selection
  f_sel = f_ent.merge(f_sel);

  f_sel.transition()
    .duration(1200)
    .delay(function(d, i) { return i *40; })
    .attr("transform", function(d, i){
      return "translate( 0, "+ i*25 +")" + ")";
    });

}

/* MAIN */
var data = d3.csvParse( d3.select("pre#data").text()); 
drawItemLayout(data, '1-20-2017');
drawPieChart(data, '1-20-2017');
drawTopItems(data, '1-20-2017');

/* UPDATE DATA */
function updateData(date) {

  //d3.csv("http://localhost:8080/udacity_test_vis_1/output_fixture_data.csv", function(data) {
  var data = d3.csvParse( d3.select("pre#data").text()); 
  drawItemLayout (data, date);
  drawPieChart(data, date);
  drawTopItems(data, date);

}

/* GET SELECTION */
$("#select_params").change(function(){
    var date = $("#select_params :selected").val();
    console.log(date);
    updateData(date);
})

1 个解决方案

#1


2  

Just three problems:

只有三个问题:

  1. You are repeating the enter() function:

    你正在重复enter()函数:

    var f_sel = svgTopItems.selectAll("g")
        .data(topData,function(d){ return d.item_name; }).enter();
    //this is an enter selection...
    
    var f_ent = f_sel.enter().append("g");
    //and you have enter() again here
    

    So, remove the first enter: f_sel should be just the data-binding selection.

    所以,删除第一个输入:f_sel应该只是数据绑定选择。

  2. Move your merged selection to before appending the rectangles

    在附加矩形之前将合并的选择移动到

  3. Your translate has an extra parenthesis:

    您的翻译有一个额外的括号:

    return "translate( 0, "+ i*25 +")" + ")";
    

With that problems corrected, this is your updated fiddle: https://jsfiddle.net/utf5hva2/

纠正这些问题后,这是你的更新小提琴:https://jsfiddle.net/utf5hva2/

#1


2  

Just three problems:

只有三个问题:

  1. You are repeating the enter() function:

    你正在重复enter()函数:

    var f_sel = svgTopItems.selectAll("g")
        .data(topData,function(d){ return d.item_name; }).enter();
    //this is an enter selection...
    
    var f_ent = f_sel.enter().append("g");
    //and you have enter() again here
    

    So, remove the first enter: f_sel should be just the data-binding selection.

    所以,删除第一个输入:f_sel应该只是数据绑定选择。

  2. Move your merged selection to before appending the rectangles

    在附加矩形之前将合并的选择移动到

  3. Your translate has an extra parenthesis:

    您的翻译有一个额外的括号:

    return "translate( 0, "+ i*25 +")" + ")";
    

With that problems corrected, this is your updated fiddle: https://jsfiddle.net/utf5hva2/

纠正这些问题后,这是你的更新小提琴:https://jsfiddle.net/utf5hva2/