使用d3.js上的下拉列表将圆圈添加到多线图

时间:2017-12-30 18:56:31

标签: javascript d3.js

我正在尝试将圆圈添加到以下折线图示例中的数据点:https://ibb.co/cnHqgb

要生成圆圈,我使用了以下内容:

onHandleIntent(Intent)

然而,正如https://bl.ocks.org/ProQuestionAsker/8382f70af7f4a7355827c6dc4ee8817d所见,每个水果的所有圆圈。我想只根据线条显示所选水果的圆圈。

非常感谢 詹姆斯

1 个答案:

答案 0 :(得分:2)

您会看到每个水果的圆圈,因为您没有根据下拉选项过滤数据。

这是一个代码片段,用于数据过滤并附加点:

var dataAsCsv = `Month,Sales,Fruit,Year
Jan,87,strawberry,2016
Feb,3,strawberry,2016
Mar,89,strawberry,2016
Apr,56,strawberry,2016
May,1,strawberry,2016
Jun,17,strawberry,2016
Jul,59,strawberry,2016
Aug,43,strawberry,2016
Sep,16,strawberry,2016
Oct,94,strawberry,2016
Nov,99,strawberry,2016
Dec,53,strawberry,2016
Jan,93,grape,2016
Feb,8,grape,2016
Mar,95,grape,2016
Apr,62,grape,2016
May,5,grape,2016
Jun,24,grape,2016
Jul,62,grape,2016
Aug,49,grape,2016
Sep,18,grape,2016
Oct,101,grape,2016
Nov,103,grape,2016
Dec,53,grape,2016
Jan,94,blueberry,2016
Feb,15,blueberry,2016
Mar,95,blueberry,2016
Apr,64,blueberry,2016
May,11,blueberry,2016
Jun,33,blueberry,2016
Jul,64,blueberry,2016
Aug,53,blueberry,2016
Sep,27,blueberry,2016
Oct,103,blueberry,2016
Nov,108,blueberry,2016
Dec,62,blueberry,2016
Jan,80,strawberry,2015
Feb,0,strawberry,2015
Mar,71,strawberry,2015
Apr,51,strawberry,2015
May,3,strawberry,2015
Jun,11,strawberry,2015
Jul,56,strawberry,2015
Aug,34,strawberry,2015
Sep,12,strawberry,2015
Oct,75,strawberry,2015
Nov,94,strawberry,2015
Dec,46,strawberry,2015
Jan,76,grape,2015
Feb,0,grape,2015
Mar,78,grape,2015
Apr,58,grape,2015
May,10,grape,2015
Jun,22,grape,2015
Jul,47,grape,2015
Aug,36,grape,2015
Sep,18,grape,2015
Oct,86,grape,2015
Nov,98,grape,2015
Dec,40,grape,2015
Jan,79,blueberry,2015
Feb,0,blueberry,2015
Mar,78,blueberry,2015
Apr,49,blueberry,2015
May,5,blueberry,2015
Jun,31,blueberry,2015
Jul,62,blueberry,2015
Aug,49,blueberry,2015
Sep,7,blueberry,2015
Oct,86,blueberry,2015
Nov,100,blueberry,2015
Dec,46,blueberry,2015`;


// Set the margins
var margin = {top: 60, right: 100, bottom: 20, left: 80},
  width = 850 - margin.left - margin.right,
  height = 370 - margin.top - margin.bottom;

// Parse the month variable
var parseMonth = d3.timeParse("%b");
var formatMonth = d3.timeFormat("%b");

var formatYear = d3.timeFormat("%Y");
var parseYear = d3.timeParse("%Y");


// Set the ranges
var x = d3.scaleTime().domain([parseMonth("Jan"), parseMonth("Dec")]).range([0, width]);
var y = d3.scaleLinear().range([height, 0]);


// Define the line
var valueLine = d3.line()
    .x(function(d) { return x(d.Month); })
    .y(function(d) { return y(+d.Sales); })

// Create the svg canvas in the "graph" div
var svg = d3.select("#graph")
        .append("svg")
        .style("width", width + margin.left + margin.right + "px")
        .style("height", height + margin.top + margin.bottom + "px")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform","translate(" + margin.left + "," + margin.top + ")")
        .attr("class", "svg");


var data = d3.csvParse(dataAsCsv);
  
   // Format the data
  data.forEach(function(d) {
      d.Month = parseMonth(d.Month);
      d.Sales = +d.Sales;
      d.Fruit = d.Fruit;
      d.Year = formatYear(parseYear(+d.Year));
  });

  var nest = d3.nest()
	  .key(function(d){
	    return d.Fruit;
	  })
	  .key(function(d){
	  	return d.Year;
	  })
	  .entries(data)

  // Scale the range of the data
  x.domain(d3.extent(data, function(d) { return d.Month; }));
  y.domain([0, d3.max(data, function(d) { return d.Sales; })]);
  
  // Set up the x axis
  var xaxis = svg.append("g")
       .attr("transform", "translate(0," + height + ")")
       .attr("class", "x axis")
       .call(d3.axisBottom(x)
          .ticks(d3.timeMonth)
          .tickSize(0, 0)
          .tickFormat(d3.timeFormat("%B"))
          .tickSizeInner(0)
          .tickPadding(10));

  // Add the Y Axis
   var yaxis = svg.append("g")
       .attr("class", "y axis")
       .call(d3.axisLeft(y)
          .ticks(5)
          .tickSizeInner(0)
          .tickPadding(6)
          .tickSize(0, 0));
  
  // Add a label to the y axis
  svg.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 0 - 60)
        .attr("x", 0 - (height / 2))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Monthly Sales")
        .attr("class", "y axis label");
    
  svg.append('g').classed('data-points', true);
  
  // Create a dropdown
    var fruitMenu = d3.select("#fruitDropdown")

    fruitMenu
		.append("select")
		.selectAll("option")
        .data(nest)
        .enter()
        .append("option")
        .attr("value", function(d){
            return d.key;
        })
        .text(function(d){
            return d.key;
        })


 
 	// Function to create the initial graph
 	var initialGraph = function(fruit){

 		// Filter the data to include only fruit of interest
 		var selectFruit = nest.filter(function(d){
                return d.key == fruit;
              })

	    var selectFruitGroups = svg.selectAll(".fruitGroups")
		    .data(selectFruit, function(d){
		      return d ? d.key : this.key;
		    })
		    .enter()
		    .append("g")
		    .attr("class", "fruitGroups")

		var initialPath = selectFruitGroups.selectAll(".line")
			.data(function(d) { return d.values; })
			.enter()
			.append("path")

		initialPath
			.attr("d", function(d){
				return valueLine(d.values)
			})
			.attr("class", "line")
      
   svg.select('g.data-points').selectAll("dot")
    .data(data.filter(function(d) { 
    	return d.Fruit === fruit;
    }))
    .enter().append("circle").classed('dot', true)
    .attr("r", 3)
    .style("fill", "pink").style('stroke', '#000')
    .attr("cx", function(d) { return x(d.Month); })
    .attr("cy", function(d) { return y(+d.Sales); });

 	}

 	// Create initial graph
 	initialGraph("strawberry")


 	// Update the data
 	var updateGraph = function(fruit){

 		// Filter the data to include only fruit of interest
 		var selectFruit = nest.filter(function(d){
                return d.key == fruit;
              })

 		// Select all of the grouped elements and update the data
	    var selectFruitGroups = svg.selectAll(".fruitGroups")
		    .data(selectFruit)

		    // Select all the lines and transition to new positions
            selectFruitGroups.selectAll("path.line")
               .data(function(d){
                  return (d.values);
                })
                .transition()
                  .duration(1000)
                  .attr("d", function(d){
                    return valueLine(d.values)
                  });
                  
   var circles = svg.select('g.data-points').selectAll(".dot")
    .data(data.filter(function(d) { 
    	return d.Fruit === fruit;
    }));

	circles
    .enter().append("circle")
    .merge(circles).classed('data-point', true)
    .attr("r", 3)
    .style("fill", "pink").style('stroke', '#000')
    .transition().duration(1000)
    .attr("cx", function(d) { return x(d.Month); })
    .attr("cy", function(d) { return y(+d.Sales); });
    
  }


 	// Run update function when dropdown selection changes
 	fruitMenu.on('change', function(){

 		// Find which fruit was selected from the dropdown
 		var selectedFruit = d3.select(this)
            .select("select")
            .property("value")

        // Run update function with the selected fruit
        updateGraph(selectedFruit)


    });
.line {
  fill: none;
  stroke: #EF5285;
  stroke-width: 2px;
}
<script src="https://d3js.org/d3.v4.js"></script>

    
    <div id = "fruitDropdown"></div>
    <div id="graph"></div>

重要的代码更改:

  1. 我没有直接将circles添加到SVG,而是创建了一个包含所有点的组<g class="data-points"></g>

    svg.append('g').classed('data-points', true);
    
  2. 在两个函数中输入/更新/退出上述组中的所有点,即initialGraphupdateGraph

    <强> InitialGraph:

    svg.select('g.data-points').selectAll("dot")
      .data(data.filter(function(d) { 
        return d.Fruit === fruit;
      }))
     .enter().append("circle").classed('dot', true)
     .attr("r", 3)
     .style("fill", "pink").style('stroke', '#000')
     .attr("cx", function(d) { return x(d.Month); })
     .attr("cy", function(d) { return y(+d.Sales); });
    

    <强> UpdateGraph:

    var circles = svg.select('g.data-points').selectAll(".dot")
      .data(data.filter(function(d) { 
        return d.Fruit === fruit;
      }));
    
    circles
      .enter().append("circle")
      .merge(circles).classed('data-point', true)
      .attr("r", 3)
      .style("fill", "pink").style('stroke', '#000')
      .transition().duration(1000)
      .attr("cx", function(d) { return x(d.Month); })
      .attr("cy", function(d) { return y(+d.Sales); });
    
  3. 观察绑定到圆圈的数据过滤 和应用的过渡,以匹配线条的过渡。

    1. 始终使用style来申请fill而不是attr。这是一个很好的做法。添加color:pink不会更改圈子的颜色,但会fill。另外,我添加了stroke,以便即使使用粉红色颜色也可以看到它们。你总是可以改变它。
    2. 我建议您每次提出问题时都添加代码段而不提供链接。对于任何人来说,调试和帮助修复错误都会更容易。

      希望这会有所帮助。 :)