D3仪表板转换不更新某些元素

时间:2017-03-21 23:21:09

标签: javascript d3.js transitions

我在d3中整理了一个简单的自定义仪表板,其中包含了我从各种来源在线改编的示例。虽然我已经取得了稳步进展,但我仍然遇到了一些可能或可能没有相关的问题,可能与d3过渡的错误应用有关。

我想解决的问题如下: 1.饼图在加载时不会显示任何内容。只有在更改日期字段时才会加载图表。 2.饼图图例未正确更新,从示例1切换到示例2在图例中留下一个空框,即使该示例不包含该类的实例。 3.标签在条形图上没有正确更新,另外条形图的顺序也没有正确更新。这些可能都与过渡问题有关。

这是演示行为的小提琴:

https://jsfiddle.net/Sledge/xo1s9dz9/6/

这是我的代码:

var item_width = 40, item_height = 50;
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([0,10]).range(["white","#4169e1"]);

// Pie Chart parameters
var pieMargin = {top:25, right:25, bottom:25, left: 25},
    pieWidth = 350 - pieMargin.right - pieMargin.left, 
    pieHeight = 350 - pieMargin.top - pieMargin.bottom;
var outerRadius = Math.min(pieWidth, pieHeight) / 2,
        innerRadius = outerRadius * .50;
var pieColor = d3.scaleOrdinal(['#42b9f4','#3791f2','#374ff1','#25b22e','#107222']);
var legendRectSize = 18, legendSpacing = 4;
var enterAntiClockwise = {startAngle: Math.PI * 2, endAngle: Math.PI * 2};

// Top Item Parameters
var topItemMargin = {top:25, right:50, bottom: 25, left: 25};
var topItemWidth = 350 - topItemMargin.left - topItemMargin.right, 
      topItemHeight = 350 - topItemMargin.top - topItemMargin.bottom;
var topItemXScale = d3.scaleLinear().range([0, topItemWidth-5]);
var barHeight = 25, 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 + pieMargin.left + pieMargin.right)
        .attr("height", pieHeight + pieMargin.top + pieMargin.bottom)
    .append("g")
        .attr("transform", "translate(" + (outerRadius + pieMargin.left) + "," + (outerRadius + pieMargin.top) + ")") ;

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


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

  var x_offset = 5, y_offset = 5;

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

  var colorScale = d3.scaleLinear()
    .domain([0,d3.max(data, function(d) { return d.sales; })])
    .range(["white","#4169e1"]);


  // 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")
    .attr("width", item_width)
    .attr("height", item_height)
    .attr("rx", 3)
    .attr("ry", 3)
    //.style("fill", function(d){ return colorScale(d.sales); })
    .style("fill-opacity", 0.8);

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

/*  $('rect').tipsy({ 
      gravity: 'w', 
      html: true, 
      title: function() {
        var d = this.__data__;
        return 'Sales: '+d.sales; 
      }
    });*/

  // move them into position with transition
  g_sel
    .transition()
    .duration(1000)
    .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) + ")";
    })
    .style("fill", function(d){ return colorScale(d.sales); });

}

function drawPieChart(data) {

    var cf = crossfilter(data);
    var salesDimension = cf.dimension(function(d){ return d.sales; });
    var categoryDimension = cf.dimension(function(d){ return d.category_name; });
    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();

    console.log(categoryAggregated);

    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), function(d){ return d.data.key; });

    path
      .enter()
      .append('path')
      .attr('d', arc(enterAntiClockwise))
      .attr('fill', function(d, i) { return pieColor(i); })
      .each(function(d){ this._current = d; }); // store the initial values

    path.exit()
        .transition()
        .duration(750)
        .attrTween('d', arcTweenOut)
        .remove();

    path
        .transition()
        .duration(1000)
        .attrTween('d', arcTween);


    // remove old legend:
    svgPieChart.selectAll(".legend").remove();

    // 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) { console.log(categoryAggregated[d].key);return categoryAggregated[d].key; }); 

    // TRANSITION FUNCTIONS
    // Then, interpolate from _current to the new angles.
    // During the transition, _current is updated in-place by d3.interpolate.
    function arcTween(a) {
      var i = d3.interpolate(this._current, a);
      this._current = i(0);
      return function(t) {
      return arc(i(t));
      };
    }
    // Interpolate exiting arcs start and end angles to Math.PI * 2
    // so that they 'exit' at the end of the data
    function arcTweenOut(a) {
      var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
      this._current = i(0);
      return function (t) {
        return arc(i(t));
      };
    }

}


function drawTopItems (data) {

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

    topData = salesDimension.top(10);

    topItemXScale
        .domain([0,d3.max(topData, function(d) { return d.sales; })]);

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

    g_sel.exit().remove();

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

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

    g_ent
        .append("rect")
    .attr("class", "dot")   
    .attr("width", function(d){ return topItemXScale(d.sales); })
    .attr("height", barHeight)
    .style("fill","#3791f2")
    .style("fill-opacity", 0.8);

  // add our text to our g
  var labelTxt = g_ent
    .append("text")
    .attr("font-size", 12)
    .attr("text-anchor", "left")
    .attr("fill", "black")
    .attr("dy", barHeight/2)
    .text(function(d){ return d.item_name; })
    .attr("transform", function(d, i){
      return "translate("+(topItemWidth)+", 0)";
    });

  var valueTxt = g_ent
    .append("text")
    .attr("font-size", 14)
    .attr("text-anchor", "left")
    .attr("fill", "white")
    .attr("dy", 2*barHeight/3)
    .text(function(d){ return d.sales; })
    .attr("transform", function(d, i){
      return "translate("+(0.9*topItemXScale(d.sales))+", 0)";
    });

  g_sel
    .transition()
    .duration(1200)
    .delay(function(d, i) { return i *40; })
    .attr("transform", function(d, i){
      return "translate(0, "+(i*(barHeight + barSpacing))+")";
    })
    .attr("width", function(d){ return topItemXScale(d.sales); });

}

/* UTILITY FUNCTIONS */
function castData(data) {
    data.forEach(function(d) {
        d.item_name = d.item;
    d.x_pos = +d.x_pos;
    d.y_pos = +d.y_pos;
    d.fixture_id = +d.fixture_id;
    d.db_id = +d.db_id;
    d.campaign_id = +d.campaign_id;
    d.sales = +d.sum_units_sold;
    d.category_name = d.category;
  });

    return data;
}

function filterData(data, campaignId, fixtureId) {

    data = castData(data);

    data = data.filter( function(d){ 
        return (d.campaign_id === campaignId  && d.fixture_id === fixtureId);
    });

    return data;

}


/* MAIN */
    var data = d3.csvParse( d3.select("pre#data").text());
    // pre-filter data
  data = filterData(data, 1550, 10987018);

  drawItemLayout(data);
  drawPieChart(data);
  drawTopItems(data);


/* UPDATE DATA */
function updateData(campaignId, fixtureId) {

  var data = d3.csvParse( d3.select("pre#data").text());

  // pre-filter data
  data = filterData(data, campaignId, fixtureId);

  drawItemLayout(data);
  drawPieChart(data);
  drawTopItems(data);

}


/* GET SELECTION */
$("#select_date_div").change(function(){
    var date = $("#select_date_div :selected").val();
    var fixtureId = $("#select_fixture_div :selected").val();
    date = +date;
    fixtureId = +fixtureId;
    updateData(date, fixtureId);
});

$("#select_fixture_div").change(function(){
    var date = $("#select_date_div :selected").val();
    var fixtureId = $("#select_fixture_div :selected").val();
    date = +date;
    fixtureId = +fixtureId;
    updateData(date, fixtureId);
});

我觉得我理解如何为我在网上找到的简单示例实现转换。对于这样的示例,我试图更新给定<g>的多个元素,但有一些我不太了解。

我希望在如何解决这些具体问题方面得到一些反馈,以及在概念上我对这些转换的工作方式缺少什么,特别是当图表的多个方面发生变化时。

0 个答案:

没有答案