d3过滤带有图例切换的条形图

时间:2017-10-17 15:19:06

标签: javascript d3.js

我正在尝试使用图例切换过滤/更新条形图。我不确定如何在初始化期间在条上设置活动状态 - 然后尝试停用 - 在切换时排除所需的数据集,但在活动状态恢复时恢复它们。

http://jsfiddle.net/5ruhac83/5/

//传奇翻转

legend.append("rect")
  .attr("x", width - 18)
  .attr("width", 18)
  .attr("height", 18)
  .style("fill", function(d, i) {
    return colores_google(i);
  })
  .on("click", function(name) {

    var active = false;

    newState = active ? "active" : "inactive";

    // Hide or show the elements
    d3.select(this).attr("class", newState);

    //set active state

        console.log("name", name)
    toggleBar(name)
  });

//使用已修剪的数据集动画条形图

function toggleBar(name) {
  var hiddenClassName = 'hidden',
    bar = chartHolder.selectAll('.bars'),
    currentBars = bar.selectAll('[value="' + name + '"]')

  currentBars.classed(hiddenClassName, !currentBars.classed(hiddenClassName))

  var barData = data.map(item => {
    item.valores = item.valores.map(valor => {
      return Object.assign({}, valor, {
        value: bar.selectAll('[value="' + valor.name + '"]').classed(hiddenClassName) ?
          0 : item[valor.name]
      })
    })
    return item;
  })




  var barData = [{
    label: "a",
    "Current Period": 20
  }, {
    label: "b",
    "Current Period": 15
  }, {
    label: "c",
    "Current Period": 25
  }, {
    label: "d",
    "Current Period": 5
  }];

  var options = getOptions(barData);
  barData = refactorData(barData, options);

  console.log("barData", barData)

  bar
    .data(barData)

  var rect = bar.selectAll("rect")
    .data(function(d) {
      return d.valores;
    })

  rect
    .transition()
    .duration(1000)
    .delay(100)
    .attr("width", x0.rangeBand() / 2)
    .attr("y", function(d) {
      return y(d.value);
    })
    .attr("height", function(d) {
      return height - y(d.value);
    });

  rect.exit().remove();

  /*
    var bar = bar.selectAll("rect")

     bar.transition()
        //.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
        .attr("x", function(d) { return x1(d.name); })
        .attr("width", x0.rangeBand())
        .attr("y", function(d) {
            return 0;
          //return y(d.value); 
         })
        .attr("height", function(d) { 
            return 0;
          //return height - y(d.value); 
         });

      //bar.exit().remove();
    */

}

2 个答案:

答案 0 :(得分:0)

这是一个根据切换的图例使用新选项重置域的图表:

JS Fiddle DEMO

function toggleBar(name, state) {
    data.forEach(function(d) { 
       _.findWhere(d.valores, {name: name}).hidden = state;
    });
    var filteredOptions;
    if(state) {
        filteredOptions = options.filter(function(d) { return d !== name; });
    } else {
      filteredOptions = options;
    }

    x1.domain(filteredOptions).rangeRoundBands([0, x0.rangeBand()]);

    y.domain([0, d3.max(data, function(d) {
      return d3.max(d.valores.filter(function(k) { return !k.hidden;}), function(d) {
        return d.value;
      });
    })]);

的变化:

  1. 您不需要在每个切换中重置数据。我刚刚为" valores"添加了一个隐藏的属性。在重置toggleBar函数中的域时,根据非隐藏选项过滤数据并相应地设置域。

  2. 我建议习惯d3"进入,更新和退出"方法。我希望代码也能帮助你理解这一点。 drawBars()是一个能够做到这一点的函数。

  3. 更改了工具提示的呈现方式。您可以使用datum()函数只使用父节点的数据,而不是将querySelector用于悬停元素(这绝对是单向)。

  4. 传说:我为每个图例添加了一个笔划,并指出相应的选项是否隐藏,每次点击都会切换填充不透明度

  5. 使用单独的颜色标度和选项的序数域,范围与以前的颜色相同,以便颜色基于名称而不是索引(如前所述)

  6. 添加了简单的过渡。

  7. 在toggleBars()函数中使用了underscore.js。您也可以切换回纯JS。

  8. 要回答关于活跃状态的问题,请检查"点击"类名。

    如果您的任何部分不清楚,请仔细阅读代码并告诉我。我也会添加一些评论。

    :)

答案 1 :(得分:0)

这是分组条形图图例切换动画的解决方案。

// jsfiddle - http://jsfiddle.net/0ht35rpb/259/

var $this =  this.$('.barChart');

var w = $this.data("width");
var h = $this.data("height");

//var configurations = $this.data("configurations");

var data = [{
  "State": "a",
  "AA": 100,
  "BB": 200
}, {
  "State": "b",
  "AA": 454,
  "BB": 344
},{
  "State": "c",
  "AA": 140,
  "BB": 500
}, {
  "State": "d",
  "AA": 154,
  "BB": 654
}];

var yLabel = "Count";


var svg = d3.select($this[0]).append("svg"),
  margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  },
  width = w - margin.left - margin.right,
  height = h - margin.top - margin.bottom,
  g = svg
  .attr("width", w)
  .attr("height", h)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// The scale spacing the groups:
var x0 = d3.scaleBand()
  .rangeRound([0, width])
  .paddingInner(0.1);

// The scale for spacing each group's bar:
var x1 = d3.scaleBand()
  .padding(0.05);

var y = d3.scaleLinear()
  .rangeRound([height, 0]);

var z = d3.scaleOrdinal()
  .range(["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"]);

var keys = d3.keys(data[0]).slice(1);

x0.domain(data.map(function(d) {
  return d.State;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
  return d3.max(keys, function(key) {
    return d[key];
  });
})]).nice();

g.append("g")
  .selectAll("g")
  .data(data)
  .enter().append("g")
  .attr("class", "bar")
  .attr("transform", function(d) {
    return "translate(" + x0(d.State) + ",0)";
  })
  .selectAll("rect")
  .data(function(d) {
    return keys.map(function(key) {
      return {
        key: key,
        value: d[key]
      };
    });
  })
  .enter().append("rect")
  .attr("x", function(d) {
    return x1(d.key);
  })
  .attr("y", function(d) {
    return y(d.value);
  })
  .attr("width", x1.bandwidth())
  .attr("height", function(d) {
    return height - y(d.value);
  })
  .attr("fill", function(d, i) {            
    return z(d.key);
  });

g.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x0));

g.append("g")
  .attr("class", "yaxis")
  .call(d3.axisLeft(y).ticks(null, "s"))
  .append("text")
  .attr("x", 2)
  .attr("y", y(y.ticks().pop()) + 0.5)
  .attr("dy", "0.32em")
  .attr("fill", "#000")
  .attr("font-weight", "bold")
  .attr("text-anchor", "start")
  .text(yLabel);

var legend = g.append("g")
  .attr("font-family", "sans-serif")
  .attr("font-size", 10)
  .attr("text-anchor", "end")
  .selectAll("g")
  .data(keys.slice().reverse())
  .enter().append("g")
  .attr("transform", function(d, i) {
    return "translate(0," + i * 20 + ")";
  });

legend.append("rect")
  .attr("x", width - 17)
  .attr("width", 15)
  .attr("height", 15)
  .attr("fill", z)
  .attr("stroke", z)
  .attr("stroke-width", 2)
  .on("click", function(d) {
    update(d)
  });

legend.append("text")
  .attr("x", width - 24)
  .attr("y", 9.5)
  .attr("dy", "0.32em")
  .text(function(d) {
    return d;
  });

var filtered = [];

////
//// Update and transition on click:
////

function update(d) {

  //
  // Update the array to filter the chart by:
  //

  // add the clicked key if not included:
  if (filtered.indexOf(d) == -1) {
    filtered.push(d);
    // if all bars are un-checked, reset:
    if (filtered.length == keys.length) filtered = [];
  }
  // otherwise remove it:
  else {
    filtered.splice(filtered.indexOf(d), 1);
  }

  //
  // Update the scales for each group(/states)'s items:
  //
  var newKeys = [];
  keys.forEach(function(d) {
    if (filtered.indexOf(d) == -1) {
      newKeys.push(d);
    }
  })
  x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
  y.domain([0, d3.max(data, function(d) {
    return d3.max(keys, function(key) {
      if (filtered.indexOf(key) == -1) return d[key];
    });
  })]).nice();

//g.select(".yaxis")
//.call(d3.axisLeft(y).ticks(null, "s"));

var t0 = svg.transition().duration(250);
var t1 = t0.transition();
t1.selectAll(".yaxis").call(d3.axisLeft(y).ticks(null, "s"));


  //
  // Filter out the bands that need to be hidden:
  //
  var bars = svg.selectAll(".bar").selectAll("rect")
    .data(function(d) {
      return keys.map(function(key) {
        return {
          key: key,
          value: d[key]
        };
      });
    })

  bars.filter(function(d) {
      return filtered.indexOf(d.key) > -1;
    })
    .transition()
    .attr("x", function(d) {
      return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width")) / 2;
    })
    .attr("height", 0)
    .attr("width", 0)
    .attr("y", function(d) {
      return height;
    })
    .duration(500);

  //
  // Adjust the remaining bars:
  //
  bars.filter(function(d) {
      return filtered.indexOf(d.key) == -1;
    })
    .transition()
    .attr("x", function(d) {
      return x1(d.key);
    })
    .attr("y", function(d) {
      return y(d.value);
    })
    .attr("height", function(d) {
      return height - y(d.value);
    })
    .attr("width", x1.bandwidth())
    .attr("fill", function(d, i) {
      return z(d.key);
    })
    .duration(500);

  // update legend:
  legend.selectAll("rect")
    .transition()
    .attr("fill", function(d, i) {
      if (filtered.length) {
        if (filtered.indexOf(d) == -1) {
          return z(d);
        } else {
          return "white";
        }
      } else {
        return z(d);
      }
    })
    .duration(100);
}