使圆圈与d3.js上的多行匹配相同的颜色过滤?

时间:2018-01-09 21:35:44

标签: javascript d3.js svg

我有一个多线图,每个水果过滤后都会更新。每种线条颜色对应不同的销售年份。在Shashank的帮助下,每个数据点的行上的圆圈已添加到组中,而不是直接附加到SVG。这是一段显示:

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;

//Legend sizing
var legendRectSize = 18;                                 
var legendSpacing = 4; 


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

var colors = d3.scaleOrdinal()
  .domain(["2016", "2015"])
  .range(["#00BFFF", "#87CEFA"]);
  
// Define the line
var valueLine = d3.line()
    .x(function(d) { return x(d.Month); })
    .y(function(d) { return y(+d.Sales); })
	
// Define the div for the tooltip
var div = d3.select("body").append("div")	
    .attr("class", "tooltip")				
    .style("opacity", 0);

// 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")
	   .style("font-family", "Courier New")
       .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")
	   .style("font-family", "Courier New")
       .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")
		.style("font-family", "Courier New")
		.style("font-size", "11px");
		
	//Add Title
  svg.append("text")
        .attr("y", 0)
        .attr("x", width/2)
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Fruit Sales")
        .attr("class", "y axis label")
		.style("font-family", "Courier New")
		.style("font-size", "20px");
  
  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")
			.attr("stroke", function(d){ return colors(d.key)});


		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)	
			.attr("fill", function(d){ return colors(d.key)})
			.attr("cx", function(d) { return x(d.Month); })
			.attr("cy", function(d) { return y(+d.Sales); })
				.on("mouseover", function(d) {
					div.transition()		
						.duration(200)		
						.style("opacity", .9);		
					div	.html("Sales:" + " "  + d.Sales)	
						.style("font-family", "Courier New")
						.style("left", (d3.event.pageX) + "px")		
						.style("top", (d3.event.pageY - 28) + "px");	
					})					
				.on("mouseout", function(d) {		
					div.transition()		
						.duration(500)		
						.style("opacity", 0);	
				});	
 	}

 	// 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)
    .attr("fill", function(d){ return colors(d.key)})
    .transition().duration(1000)
    .attr("cx", function(d) { return x(d.Month); })
    .attr("cy", function(d) { return y(+d.Sales); });
    
  }
  
  var legend = svg.selectAll('.legend')                     
          .data(colors.domain())                                   // NEW
          .enter()                                                // NEW
          .append('g')                                            // NEW
          .attr('class', 'legend')                                // NEW
          .attr('transform', function(d, i) {                     // NEW
            var height1 = legendRectSize + legendSpacing;          // NEW
            var offset =  height1 * colors.domain().length /2;     // NEW
            var horz = 37 * legendRectSize;                       // NEW
            var vert = i * height1 - offset;                       // NEW
            return 'translate(' + horz + ',' + vert + ')';        // NEW
          });                                                     // NEW

        legend.append('rect')                                     // NEW
          .attr('width', legendRectSize)                          // NEW
          .attr('height', legendRectSize)                         // NEW
          .style('fill', colors)                                   // NEW
          .style('stroke', colors);                                // NEW
          
        legend.append('text')                                     // NEW
          .attr('x', legendRectSize + legendSpacing)              // NEW
          .attr('y', legendRectSize - legendSpacing) 
		  .style("font-family", "Courier New")
          .text(function(d) { return d; });    


 	// 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-width: 2px;
}

div.tooltip {	
    position: absolute;			
    text-align: center;			
    width: 60px;					
    height: 28px;					
    padding: 2px;				
    font: 12px sans-serif;		
    background: lightsteelblue;	
    border: 0px;		
    border-radius: 8px;			
    pointer-events: none;			
}

.dot:hover {
      fill: black;
    }
	
.legend {                                                   /* NEW */
        font-size: 10px;                                          /* NEW */
      }                                                           /* NEW */
      rect {                                                      /* NEW */
        stroke-width: 2;                                          /* NEW */
      }                    
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3 Page Template</title>
        <script src="https://d3js.org/d3.v4.js"></script>
        <link rel="stylesheet" href="style.css">
    </head>
  
  <body>
	<g class="data-points"></g>
    <div id = "fruitDropdown"></div>
    <div id="graph"></div>
	
    <script src="Example11.js"></script>
  </body>
  
</html>

如何确保圆圈与线条颜色相同,同时确保在选择不同的水果时更新?我希望这个解决方案还能为图例中的额外矩形提供修复。

非常感谢, 詹姆斯

1 个答案:

答案 0 :(得分:1)

<xml> <logger name="my.test.module"> <level value="all"/> <appender-ref ref="file-module"/> </logger> <logger name="my.test.module2"> <level value="trace"/> <appender-ref ref="file-module2"/> </logger> </xml> 的圈子从fill更改为colors(d.key)就可以了。

代码更改和代码段:

colors(d.Year)

&#13;
&#13;
.attr("fill", function(d){ return colors(d.Year)})
&#13;
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;

//Legend sizing
var legendRectSize = 18;                                 
var legendSpacing = 4; 


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

var colors = d3.scaleOrdinal()
  .domain(["2016", "2015"])
  .range(["#00BFFF", "#87CEFA"]);
  
// Define the line
var valueLine = d3.line()
    .x(function(d) { return x(d.Month); })
    .y(function(d) { return y(+d.Sales); })
	
// Define the div for the tooltip
var div = d3.select("body").append("div")	
    .attr("class", "tooltip")				
    .style("opacity", 0);

// 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")
	   .style("font-family", "Courier New")
       .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")
	   .style("font-family", "Courier New")
       .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")
		.style("font-family", "Courier New")
		.style("font-size", "11px");
		
	//Add Title
  svg.append("text")
        .attr("y", 0)
        .attr("x", width/2)
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Fruit Sales")
        .attr("class", "y axis label")
		.style("font-family", "Courier New")
		.style("font-size", "20px");
  
  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")
			.attr("stroke", function(d){ return colors(d.key)});


		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)	
			.attr("fill", function(d){ return colors(d.Year)})
			.attr("cx", function(d) { return x(d.Month); })
			.attr("cy", function(d) { return y(+d.Sales); })
				.on("mouseover", function(d) {
					div.transition()		
						.duration(200)		
						.style("opacity", .9);		
					div	.html("Sales:" + " "  + d.Sales)	
						.style("font-family", "Courier New")
						.style("left", (d3.event.pageX) + "px")		
						.style("top", (d3.event.pageY - 28) + "px");	
					})					
				.on("mouseout", function(d) {		
					div.transition()		
						.duration(500)		
						.style("opacity", 0);	
				});	
 	}

 	// 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)
    .attr("fill", function(d){ return colors(d.Year)})
    .transition().duration(1000)
    .attr("cx", function(d) { return x(d.Month); })
    .attr("cy", function(d) { return y(+d.Sales); });
    
  }
  
  var legend = svg.selectAll('.legend')                     
          .data(colors.domain())                                   // NEW
          .enter()                                                // NEW
          .append('g')                                            // NEW
          .attr('class', 'legend')                                // NEW
          .attr('transform', function(d, i) {                     // NEW
            var height1 = legendRectSize + legendSpacing;          // NEW
            var offset =  height1 * colors.domain().length /2;     // NEW
            var horz = 37 * legendRectSize;                       // NEW
            var vert = i * height1 - offset;                       // NEW
            return 'translate(' + horz + ',' + vert + ')';        // NEW
          });                                                     // NEW

        legend.append('rect')                                     // NEW
          .attr('width', legendRectSize)                          // NEW
          .attr('height', legendRectSize)                         // NEW
          .style('fill', colors)                                   // NEW
          .style('stroke', colors);                                // NEW
          
        legend.append('text')                                     // NEW
          .attr('x', legendRectSize + legendSpacing)              // NEW
          .attr('y', legendRectSize - legendSpacing) 
		  .style("font-family", "Courier New")
          .text(function(d) { return d; });    


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


    });
&#13;
.line {
  fill: none;
  stroke-width: 2px;
}

div.tooltip {	
    position: absolute;			
    text-align: center;			
    width: 60px;					
    height: 28px;					
    padding: 2px;				
    font: 12px sans-serif;		
    background: lightsteelblue;	
    border: 0px;		
    border-radius: 8px;			
    pointer-events: none;			
}

.dot:hover {
      fill: black;
    }
	
.legend {                                                   /* NEW */
        font-size: 10px;                                          /* NEW */
      }                                                           /* NEW */
      rect {                                                      /* NEW */
        stroke-width: 2;                                          /* NEW */
      }
&#13;
&#13;
&#13;

此外,如果您需要提出建议,我们希望您在同一组内安排<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3 Page Template</title> <script src="https://d3js.org/d3.v4.js"></script> <link rel="stylesheet" href="style.css"> </head> <body> <g class="data-points"></g> <div id = "fruitDropdown"></div> <div id="graph"></div> <script src="Example11.js"></script> </body> </html>和相应的fruitGroups。我建议你有如下结构:

circles

基于评论的编辑

要改变水果的颜色,正如您在评论中提到的那样,我使用了单独的颜色域,以及如何使用它来确定<g class="fruitGroups"> <g class="2015"> <path></path> <circle class="dot"></circle> ... </g> <g class="2016"> <path></path> <circle></circle> ... </g> </g> 的颜色{ {1}}功能。

path

updateGraph

对于圈子:

path

传说:

.style('stroke', function(d) { if (fruit == "strawberry") {return colors(d.key)} else if (fruit == "grape") {return colors1(d.key)} else {return colors2(d.key)}});

这是一个完成上述所有操作的片段:

&#13;
&#13;
.style("fill", function(d){ if (d.Fruit == "strawberry") {return colors(d.Year)} else if (d.Fruit == "grape") {return colors1(d.Year)} else {return colors2(d.Year)}})
&#13;
// rerender legend rects
svg.selectAll('.legend rect')
  .style('fill', function(d) { if (fruit == "strawberry") {return colors(d)} else if (fruit == "grape") {return colors1(d)} else {return colors2(d)}})
  .style('stroke', function(d) { if (fruit == "strawberry") {return colors(d)} else if (fruit == "grape") {return colors1(d)} else {return colors2(d)}});
&#13;
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;

//Legend sizing
var legendRectSize = 18;                                 
var legendSpacing = 4; 


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

var colors = d3.scaleOrdinal()
  .domain(["2016", "2015"])
  .range(["#00BFFF", "#87CEFA"]);
  
var colors1 = d3.scaleOrdinal()
  .domain(["2016", "2015"])
  .range(["red", "orange"]);

var colors2 = d3.scaleOrdinal()
  .domain(["2016", "2015"])
  .range(["darkgreen", "lightgreen"]);

// Define the line
var valueLine = d3.line()
    .x(function(d) { return x(d.Month); })
    .y(function(d) { return y(+d.Sales); })
	
// Define the div for the tooltip
var div = d3.select("body").append("div")	
    .attr("class", "tooltip")				
    .style("opacity", 0);

// 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")
	   .style("font-family", "Courier New")
       .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")
	   .style("font-family", "Courier New")
       .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")
		.style("font-family", "Courier New")
		.style("font-size", "11px");
		
	//Add Title
  svg.append("text")
        .attr("y", 0)
        .attr("x", width/2)
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Fruit Sales")
        .attr("class", "y axis label")
		.style("font-family", "Courier New")
		.style("font-size", "20px");
  
  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")
			.attr("stroke", function(d){ if (fruit == "strawberry") {return colors(d.key)} else if (fruit == "grape") {return colors1(d.key)} else {return colors2(d.key)}});


		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)	
			.attr("fill", function(d){ if (d.Fruit == "strawberry") {return colors(d.Year)} else if (d.Fruit == "grape") {return colors1(d.Year)} else {return colors2(d.Year)}})
			.attr("cx", function(d) { return x(d.Month); })
			.attr("cy", function(d) { return y(+d.Sales); })
				.on("mouseover", function(d) {
					div.transition()		
						.duration(200)		
						.style("opacity", .9);		
					div	.html("Sales:" + " "  + d.Sales)	
						.style("font-family", "Courier New")
						.style("left", (d3.event.pageX) + "px")		
						.style("top", (d3.event.pageY - 28) + "px");	
					})					
				.on("mouseout", function(d) {		
					div.transition()		
						.duration(500)		
						.style("opacity", 0);	
				});	
 	}

 	// 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)
                  }).style('stroke', function(d) { if (fruit == "strawberry") {return colors(d.key)} else if (fruit == "grape") {return colors1(d.key)} else {return colors2(d.key)}});
                  
   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)
    .transition().duration(1000)
    .style("fill", function(d){ if (d.Fruit == "strawberry") {return colors(d.Year)} else if (d.Fruit == "grape") {return colors1(d.Year)} else {return colors2(d.Year)}})
    .attr("cx", function(d) { return x(d.Month); })
    .attr("cy", function(d) { return y(+d.Sales); });
    
    // rerender legend rects
    svg.selectAll('.legend rect')
      .style('fill', function(d) { if (fruit == "strawberry") {return colors(d)} else if (fruit == "grape") {return colors1(d)} else {return colors2(d)}})
      .style('stroke', function(d) { if (fruit == "strawberry") {return colors(d)} else if (fruit == "grape") {return colors1(d)} else {return colors2(d)}});
  }
  
  var legend = svg.selectAll('.legend')                     
          .data(colors.domain())                                   // NEW
          .enter()                                                // NEW
          .append('g')                                            // NEW
          .attr('class', 'legend')                                // NEW
          .attr('transform', function(d, i) {                     // NEW
            var height1 = legendRectSize + legendSpacing;          // NEW
            var offset =  height1 * colors.domain().length /2;     // NEW
            var horz = 37 * legendRectSize;                       // NEW
            var vert = i * height1 - offset;                       // NEW
            return 'translate(' + horz + ',' + vert + ')';        // NEW
          });                                                     // NEW

        legend.append('rect')                                     // NEW
          .attr('width', legendRectSize)                          // NEW
          .attr('height', legendRectSize)                         // NEW
          .style('fill', colors)                                   // NEW
          .style('stroke', colors);                                // NEW
          
        legend.append('text')                                     // NEW
          .attr('x', legendRectSize + legendSpacing)              // NEW
          .attr('y', legendRectSize - legendSpacing) 
		  .style("font-family", "Courier New")
          .text(function(d) { return d; });    


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


    });
&#13;
&#13;
&#13;

您可以使用的改进:

  1. 颜色域:)
  2. .line { fill: none; stroke-width: 2px; } div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; } .dot:hover { fill: black; } .legend { /* NEW */ font-size: 10px; /* NEW */ } /* NEW */ rect { /* NEW */ stroke-width: 2; /* NEW */ }转换后渲染图例(使用<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3 Page Template</title> <script src="https://d3js.org/d3.v4.js"></script> <link rel="stylesheet" href="style.css"> </head> <body> <g class="data-points"></g> <div id = "fruitDropdown"></div> <div id="graph"></div> <script src="Example11.js"></script> </body> </html>回调。如果您不确定如何使用,请参阅我的回答:Transition on end callback < / LI>
  3. 修复错误(如果有的话)
  4. 希望这有帮助,如果您有任何问题,请与我联系。