D3js 2个不同图形之间的交互

时间:2019-06-24 16:38:04

标签: javascript html d3.js svg interaction

我有2个图形:箱线图和折线图,并且我希望在画线图或放大线图时更新箱线图。我该怎么做?这是我的代码: 参见working example here

样式

.line {
    fill: none;                 
  stroke: url(#grad);    
  stroke-width: 2px; 
}
.zoom {
  cursor: move;
  fill: none;
  pointer-events: all;
}
.tooltip {
  background-color: black;
  border: 1px black solid;
  display: inline-block;
  border-radius: 5px;
  padding: 15px;
  min-width: 460px;
  text-align: left;
  color: white;  
}

html代码

<svg width="400" height="700">  
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
<!-- BOXPLOT -->
<!-- Create a div where the graph will take place -->
<span id="svg2"></span>

脚本

var svg = d3.select("svg"),
    margin = {top: 90, right: 50, bottom: 100, left: 40},
    margin2 = {top: 630, right: 50, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    height2 = +svg.attr("height") - margin2.top - margin2.bottom;

var parseDate = d3.timeParse("%Y-%m-%d");

var x = d3.scaleTime().range([0, width]),
    x2 = d3.scaleTime().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    y2 = d3.scaleLinear().range([height2, 0]);

var xAxis = d3.axisBottom(x),
    xAxis2 = d3.axisBottom(x2),
    yAxis = d3.axisLeft(y);

var brush = d3.brushX()
    .extent([[0, 0], [width, height2]])
    .on("brush end", brushed);

var zoom = d3.zoom()
    .scaleExtent([1, Infinity])
    .translateExtent([[0, 0], [width, height]])
    .extent([[0, 0], [width, height]])
    .on("zoom", zoomed);

    var line = d3.line()
        .x(function (d) { return x(d.date); })
        .y(function (d) { return y(d.value); });

    var line2 = d3.line()
        .x(function (d) { return x2(d.date); })
                .y(function (d) { return y2(d.value); });

    var clip = svg.append("defs").append("svg:clipPath")
        .attr("id", "clip")
        .append("svg:rect")
        .attr("width", width)
        .attr("height", height)
        .attr("x", 0)
        .attr("y", 0); 

        var grad = svg.append('defs')
      .append('linearGradient')
      .attr('id', 'grad');

    grad.append('stop')
      .attr('stop-color', '#2a85c9');

    grad.append('stop')
      .attr('stop-color', '#2a85c9')
      .attr('offset', '43.5%');

    grad.append('stop')
      .attr('stop-color', '#c96e2a')
      .attr('offset', '44%');

    grad.append('stop')
      .attr('stop-color', '#c96e2a')
      .attr('offset', '55%');

    grad.append('stop')
      .attr('stop-color', '#c92a35')
      .attr('offset', '56%');

    grad.append('stop')
      .attr('stop-color', '#c92a35')
      .attr('offset', '100%');

    var Line_chart = svg.append("g")
        .attr("class", "focus")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .attr("clip-path", "url(#clip)");


    var focus = svg.append("g")
        .attr("class", "focus")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var context = svg.append("g")
    .attr("class", "context")
        .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");


d3.csv("Gasolinegroup.csv", type, function (error, data) {
  if (error) throw error;

  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain([0, d3.max(data, function (d) { return d.value; })]);
  x2.domain(x.domain());
    y2.domain(y.domain());

    focus.append("g")
        .attr("class", "axis axis--x")
        .attr("transform", "translate(0," + height/0.65 + ")")
        .call(xAxis);

    focus.append("g")
        .attr("class", "axis axis--y")
        .call(yAxis);

    Line_chart.append("path")
        .datum(data)
        .attr("class", "line")
                .attr("d", line);

    context.append("path")
        .datum(data)
        .attr("class", "line")
        .attr("d", line2);


  context.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height2 + ")")
      .call(xAxis2);

  context.append("g")
      .attr("class", "brush")
      .call(brush)
      .call(brush.move, x.range());

  svg.append("rect")
      .attr("class", "zoom")
      .attr("width", width)
      .attr("height", height)
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
      .call(zoom);

    console.log(data);
});

function brushed() {
  if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
  var s = d3.event.selection || x2.range();
  x.domain(s.map(x2.invert, x2));
  Line_chart.select(".line").attr("d", line);
  focus.select(".axis--x").call(xAxis);
  svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
      .scale(width / (s[1] - s[0]))
      .translate(-s[0], 0));
}

function zoomed() {
  if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
  var t = d3.event.transform;
  x.domain(t.rescaleX(x2).domain());
  Line_chart.select(".line").attr("d", line);
  focus.select(".axis--x").call(xAxis);
  context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}

function type(d) {
  d.date = parseDate(d.date);
  d.value = +d.value;
  return d;
}

//the title 
svg.append("text")
      .attr("transform",  "translate(" + (width/2) + "," + 
                           (height + margin.top + -550) + ")")
      .style("text-anchor", "middle")
      .style("font-size", "18px")
      .text("New York Gas Prices Over Time- From 2000 - 2017")
      .attr("font-family", "sans-serif");


//x-axis label
svg.append("text")             
      .attr("transform",
            "translate(" + (width/2) + " ," + 
                           (height + margin.top + 100) + ")")
      .style("text-anchor", "middle"+ 75)
      .text("Date")
      .attr("font-family", "sans-serif");;

//y-axis label 
svg.append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 0 - margin.left)
      .attr("x", 0 - (height / 2))
      .attr("dy", "1em")
      .style("text-anchor", "middle" + 0)
      .text("Gas Price (in US Dollars)")
            .attr("font-family", "sans-serif");;

// set the dimensions and margins of the graph
var margin = {top: 20, right: 30, bottom: 50, left: 100},
    width = 460 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg2 = d3.select("#svg2")
  .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

// Read the data and compute summary statistics for each specie
d3.csv("Gasolinegroup.csv", function(data) {

  // Compute quartiles, median, inter quantile range min and max --> these info are then used to draw the box.
  var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
    .key(function(d) { return d.year_group;})
    .rollup(function(d) {
      q1 = d3.quantile(d.map(function(g) { return g.valuerounded;}).sort(d3.ascending),.25)
      median = d3.quantile(d.map(function(g) { return g.valuerounded;}).sort(d3.ascending),.5)
      q3 = d3.quantile(d.map(function(g) { return g.valuerounded;}).sort(d3.ascending),.75)
      interQuantileRange = q3 - q1
      min = q1 - 1.5 * interQuantileRange
      max = q3 + 1.5 * interQuantileRange
      return({q1: q1, median: median, q3: q3, interQuantileRange: interQuantileRange, min: min, max: max})
    })
    .entries(data)

  // Show the Y scale
  var y = d3.scaleBand()
    .range([ height, 0])
    .domain(["Post-Recession", "In-Recession", "Pre-Recession"])
    .padding(.4);
  svg2.append("g")
    .call(d3.axisLeft(y).tickSize(0))
    .select(".domain").remove()

  // Show the X scale
  var x = d3.scaleLinear()
    .domain([0,5])
    .range([0, width])
  svg2.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x).ticks(4))
    .select(".domain").remove()

  // Color scale
  var myColor = {
        "Pre-Recession": '#2a85c9',
        "In-Recession": '#c96e2a',
        "Post-Recession": '#c92a35',
      };


  // Add X axis label:
  svg2.append("text")
      .attr("text-anchor", "end")
      .attr("x", width - 120)
      .attr("y", height + margin.top + 30)
      .text("NY Gas Prices")
      .attr("font-family", "sans-serif");

  // Show the main vertical line
  svg2
    .selectAll("vertLines")
    .data(sumstat)
    .enter()
    .append("line")
      .attr("x1", function(d){return(x(d.value.min))})
      .attr("x2", function(d){return(x(d.value.max))})
      .attr("y1", function(d){return(y(d.key) + y.bandwidth()/2)})
      .attr("y2", function(d){return(y(d.key) + y.bandwidth()/2)})
      .attr("stroke", "black")
      .style("width", 40)

  // rectangle for the main box
  svg2
    .selectAll("boxes")
    .data(sumstat)
    .enter()
    .append("rect")
        .attr("x", function(d){return(x(d.value.q1))}) 
        .attr("width", function(d){ ; return(x(d.value.q3)-x(d.value.q1))}) 
        .attr("y", function(d) { return y(d.key); })
        .attr("height", y.bandwidth() )
        .attr("stroke", "black")
        .style("fill", "#B6B6B4")
        .style("opacity", 0.3)

  // Show the median
  svg2
    .selectAll("medianLines")
    .data(sumstat)
    .enter()
    .append("line")
      .attr("y1", function(d){return(y(d.key) + y.bandwidth()/2)})
      .attr("y2", function(d){return(y(d.key) + y.bandwidth()/2)})
      .attr("x1", function(d){return(x(d.value.median))})
      .attr("x2", function(d){return(x(d.value.median))})
      .attr("stroke", "black")
      .style("width", 80)

  // create a tooltip
  var tooltip = d3.select("#svg2")
    .append("div")
      .style("opacity", 0)
      .attr("class", "tooltip")
      .style("font-size", "16px")

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function(d) {
    tooltip
      .transition()
      .duration(200)
      .style("opacity", 1)
    tooltip
        .html("<span style='color:grey'>NY Gas Prices: </span>" + d.valuerounded) 
        .style("left", (d3.mouse(this)[0]+30) + "px")
        .style("top", (d3.mouse(this)[1]+30) + "px")
  }
  var mousemove = function(d) {
    tooltip
      .style("left", (d3.mouse(this)[0]+30) + "px")
      .style("top", (d3.mouse(this)[1]+30) + "px")
  }
  var mouseleave = function(d) {
    tooltip
      .transition()
      .duration(200)
      .style("opacity", 0)
  }

  // Add individual points with jitter
  var jitterWidth = 50
  svg2
    .selectAll("indPoints")
    .data(data)
    .enter()
    .append("circle")
      .attr("cx", function(d){ return(x(d.valuerounded))})
      .attr("cy", function(d){ return( y(d.year_group) + (y.bandwidth()/2) - jitterWidth/2 + Math.random()*jitterWidth )})
      .attr("r", 4)
      .attr("fill", function(d) {
          return myColor[d.year_group];
        })
      .style("opacity", 0.5)
      .on("mouseover", mouseover)
      .on("mousemove", mousemove)
      .on("mouseleave", mouseleave)
})

//the title 
svg2.append("text")
      .attr("transform",  "translate(" + (width/2) + "," + 
                           (height + margin.top + -350) + ")")
      .style("text-anchor", "middle")
      .style("font-size", "18px")
      .text("Gas Price in Time Period")
      .attr("font-family", "sans-serif");

我尝试使用双倍缩放功能,但无法正常工作。

1 个答案:

答案 0 :(得分:-1)

您应该使用dispatch启用这些图之间的动作交流。例如画线图时,可以使用d3.dispatch传递画图范围。箱线图可以监听此调度并相应地更新自身。反之亦然。