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

2024-05-16

我有一个多线图,当按每种水果过滤时会更新。每条线条颜色对应不同的销售年份。在...的帮助下Shashank https://stackoverflow.com/users/5569282/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>

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

非常感谢, 詹姆士


改变fill对于来自的圈子colors(d.key) to colors(d.Year)朋友成功了。

代码更改和片段:

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


    });
.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>

另外,如果您需要建议,我希望您能安排fruitGroups以及相应的circles在同一组内。我建议你有一个如下的结构:

<g class="fruitGroups">
   <g class="2015">
     <path></path>
     <circle class="dot"></circle>
     ...
   </g>
   <g class="2016">
     <path></path>
     <circle></circle>
     ...
   </g>
</g>

根据评论进行编辑

为了改变水果的颜色,正如您在评论中提到的,我使用了单独的颜色域,以下是如何使用它来确定水果的颜色path in the updateGraph功能。

For 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)}});

对于圆圈:

.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)}})

对于传说:

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


    });
.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. 色域:)
  2. 一旦渲染图例paths 被转换(使用.on('end, function(){})打回来。如果您不确定如何使用,请参考我的回答:结束回调时转换 https://stackoverflow.com/questions/46896661/d3-y-axis-label-position/46897908#46897908
  3. 修复错误(如果有)

希望这对您有所帮助,如果您有任何疑问,请告诉我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使圆圈与 d3.js 上的多线匹配相同的颜色过滤? 的相关文章

  • AJAX 安全问题

    我希望能够解决一些关于 AJAX 安全性的问题 这是我试图理解的一个场景 假设我正在使用 AJAX 向页面请求一些半敏感材料 例如 我将把用户的 ID 传递给一个 php 文件 并返回一些关于他们自己的信息 现在 是什么阻止人们模拟此 Ja
  • 当内部元素滚动位置到达顶部/底部时防止父元素滚动?

    我有一个小 浮动工具箱 一个带有position fixed overflow auto 效果很好 但是 当在该框内滚动 使用鼠标滚轮 并到达底部或顶部时 父元素 接管 滚动请求 工具框后面的文档滚动 这很烦人 而不是用户 要求的 我正在使
  • 三个JS,给纹理添加镜面反射(光泽)

    我有一个纹理应用于 Three js 中的对象 我想为其添加一些镜面反射或光泽 我看到这样的例子 new THREE MeshPhongMaterial color 0x996633 specular 0x050505 shininess
  • 为什么省略分号会破坏这段代码?

    或者换句话说 为什么分号插入失败 导致下面的代码被破坏 function Foo Foo prototype bar function console log bar lt missing semicolon function Foo pr
  • 在有限的上下文中运行 JS 代码

    我正在尝试奔跑trusted 隔离 上下文中的 JS 代码 基本上想出了这个方法 function limitedEval src context return function with this return eval src call
  • 如何将值发布到输入框中?

    Intro I would like to get the current time after clicking at click and POST the value into input text box Note 假设包含引导样式表
  • Javascript 将对象推送为克隆

    我将 d3 用于交互式网络应用程序 我需要绑定的数据在交互过程中发生变化 并且由 JSON 变量中的一些选定对象组成 为此 我在 JSON 变量上使用了映射 并进行了一些查询来选择适当的对象 对象被推送到列表中 并且该列表被绑定为新数据 我
  • 检测 Webkit/Chrome 中 HTML5 数字控件更改的事件?

    HTML5 为我们提供了一些新的输入元素 例如
  • 将时间戳转换为一个数组

    在应用程序脚本 谷歌表中运行 我从 API 获取时间戳并返回此结果 1 6370611672429312E18 1 63706107263277082E18 我执行此代码并且工作正常 但问题不在数组中 我每次都需要它在数组中 const t
  • 裁剪 SVG 的正确方法?

    我对 SVG 图像完全感到困惑 我想将图像裁剪为其核心内容 我想通过指定其视图框和 或视口和 或其他任何内容来裁剪它 除非我不想更改折线元素中的任何点 图像按原样呈现类似这样的内容 注意 边框仅用于说明目的 边框实际上并不是 SVG 的一部
  • 我可以在 GWT 中使用第三方 Javascript 库吗

    例如穆工具 用 js 编码对我来说很舒服 但显然不适合所有人 你当然可以 最好的事情就是给自己写一些好看的JavaScript 覆盖类型 http code google com webtoolkit doc latest DevGuide
  • websockets 如何处理同一浏览器的两个选项卡

    I have 1 个 PHP 服务器 提供 http 请求 和 1 node js 发布更新的数据消息 每个连接都带有 websocket php 服务器设置其 cookie 在一个浏览器中 此 cookie 可在所有选项卡中使用 当浏览器
  • .points 不透明度/大小在三个.js 内

    我回来回答有关 points 的第二个问题 这次想知道如何将不透明度从 0 更改为 1 然后又回到距发射器的特定像素距离内 var particleCount 14 particles new THREE Geometry pMateria
  • AngularJS + jQuery 移动

    是否还有其他可能性来设计AngularJS以移动友好的方式应用程序CSS 我正在计划一个移动应用程序 并希望使用 AngularJS 进行逻辑和数据绑定 但我不想自己设计所有内容CSS The AngularJSFAQ说它使用jQuery
  • 如何在React中动态分配属性?

    这是一个有两个参数的函数 我要创建的标签的名称 具有以下属性的对象 Using React 我创建一个组件并将该元素渲染到 DOM 问题是我想向元素添加属性 但它不允许循环在元素内设置属性 var Element function elem
  • javascript - 如何获取对象名称或关联数组索引名称?

    我有一个像这样的 JSON 对象 var list name1 element1 value1 name2 element1 value2 如何提取所有 nameX 字符串值 例如 假设我想将它们连接在一个字符串中输出 例如 name1 n
  • 如何解决“消息端口在收到响应之前已关闭”的问题。在 JavaScript 中的 window.location.reload() 之后

    我遇到了 javascript 问题 从 chrome v73 0 3683 86 开始 每当我在 window location reload 函数之后运行 javascript 代码时 它总是给我错误 Unchecked runtime
  • 如何在 React Native 中使用相同的 Firebase 数据库在两个应用程序之间进行通信?

    我有两个不同的应用程序使用相同的实时数据库 在第一个应用程序中 我发送的订单包含一些要保存在数据库中的数据字段 在另一个应用程序中 我只添加一个侦听器 firebase database ref userOrder currentUser
  • 如何跨多个文件跨越 javascript 命名空间?

    我永远忽略了javascript 几年前我开始使用 jQuery 这样我就可以过得去 但随着我开始更多地进行 TDD 我昨天决定真正深入研究 javascript 之后可能还有咖啡脚本 在我的 ASP NET Web 窗体应用程序中 我有很
  • JavaScript 阶乘防止无穷大

    我一直在 JavaScript 中使用这个函数来计算阶乘数 var f function factorial n if n 0 n 1 return 1 if f n gt 0 return f n return f n factorial

随机推荐