3

im trying to update http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a and add brushing into it(brushing displayed under the line graph) to make it look like https://www.google.com.hk/#q=s%26p+500

added coded to the first link:

var brush = d3.svg.brush()
  .x(x)
  .on("brush", brushmove)
  .on("brushend", brushend);

svg.append("g")
  .attr("class", "brush")
  .call(brush)
  .selectAll('rect')
    .attr('height', height);

function brushmove() {
  var extent = brush.extent();
}

function brushend() {
 x.domain(brush.extent())
 console.log(brush.extent());
}

The problem is that once i add brushing into it, there's a background formed behind the graph and i can't perform mouse events(mousemove) anymore.

Is there a way to fix it to make it look like google? 1) brushing and mouse event coexist 2) brushing area under the curve

var csv = date,close1, close2 26-Mar-12,606.98,58.13 27-Mar-12,614.48,53.98 28-Mar-12,617.62,67.00 29-Mar-12,609.86,89.70 30-Mar-12,599.55,99.00 2-Apr-12,618.63,130.28 3-Apr-12,629.32,166.70 4-Apr-12,624.31,234.98 5-Apr-12,633.68,345.44 9-Apr-12,636.23,443.34 10-Apr-12,628.44,543.70 11-Apr-12,626.20,580.13 12-Apr-12,622.77,605.23 13-Apr-12,605.23,626.20 16-Apr-12,580.13,628.44 17-Apr-12,543.70,636.23 18-Apr-12,443.34,633.68 19-Apr-12,345.44,624.31 20-Apr-12,234.98,629.32 23-Apr-12,166.70,618.63 24-Apr-12,130.28,599.55 25-Apr-12,99.00,609.86 26-Apr-12,89.70,617.62 27-Apr-12,67.00,614.48 30-Apr-12,53.98,606.98 1-May-12,58.13,503.15

MIN KYU YUN
  • 33
  • 1
  • 6

1 Answers1

4

Both the example you link to and the brush add a rect on top of the plot to capture mouse events. The key to making them coexist is to add the brush (and allow it to create its rect) and then use that rect to add the tooltip events. This way you only end up with one point-events rect:

// add a g for the brush
var context  = svg.append("g");

// add the brush
context.call(brush);

// grab the brush's rect and add the tooltip events
context.select(".background")
  .on("mouseover", function() {
    focus.style("display", null);
  })
  .on("mouseout", function() {
    focus.style("display", "none");
  })
  .on("mousemove", mousemove);

Full code:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  /* set the CSS */
  
  body {
    font: 12px Arial;
  }
  
  path {
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
  }
  
  .axis path,
  .axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
  }
  
  .extent {
    stroke: #fff;
    fill-opacity: .125;
    shape-rendering: crispEdges;
  }
</style>

<body>

  <!-- load the d3.js library -->
  <script src="http://d3js.org/d3.v3.min.js"></script>

  <script>
    // Set the dimensions of the canvas / graph
    var margin = {
        top: 30,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = 600 - margin.left - margin.right,
      height = 270 - margin.top - margin.bottom;

    // Parse the date / time
    var parseDate = d3.time.format("%d-%b-%y").parse,
      formatDate = d3.time.format("%d-%b"),
      bisectDate = d3.bisector(function(d) {
        return d.date;
      }).left;

    // Set the ranges
    var x = d3.time.scale().range([0, width]);
    var y = d3.scale.linear().range([height, 0]);

    // Define the axes
    var xAxis = d3.svg.axis().scale(x)
      .orient("bottom").ticks(5);

    var yAxis = d3.svg.axis().scale(y)
      .orient("left").ticks(5);

    // Define the line
    var valueline = d3.svg.line()
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.close);
      });
      
    var area = d3.svg.area()
      .x(function(d) {
          return x(d.date);
        })
      .y0(height)
      .y1(function(d) {
        return y(d.close);
      });

    // Adds the svg canvas
    var svg = d3.select("body")
      .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 + ")");
    
    var defs = svg.append("defs");

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

    var lineSvg = svg.append("g");

    var focus = svg.append("g")
      .style("display", "none");
      
    var brush = d3.svg.brush()
      .x(x)
      .on("brush", function() {
        var s = brush.extent(),
          x1 = x(s[0]),
          x2 = x(s[1]);
          
        areaClip.attr('x', x1);
        areaClip.attr('width', x2 - x1);
      })

    var csv = `date,close
26-Mar-12,606.98
27-Mar-12,614.48
28-Mar-12,617.62
29-Mar-12,609.86
30-Mar-12,599.55
2-Apr-12,618.63
3-Apr-12,629.32
4-Apr-12,624.31
5-Apr-12,633.68
9-Apr-12,636.23
10-Apr-12,628.44
11-Apr-12,626.20
12-Apr-12,622.77
13-Apr-12,605.23
16-Apr-12,580.13
17-Apr-12,543.70
18-Apr-12,443.34
19-Apr-12,345.44
20-Apr-12,234.98
23-Apr-12,166.70
24-Apr-12,130.28
25-Apr-12,99.00
26-Apr-12,89.70
27-Apr-12,67.00
30-Apr-12,53.98
1-May-12,58.13`;

    var data = d3.csv.parse(csv);

    data.forEach(function(d) {
      d.date = parseDate(d.date);
      d.close = +d.close;
    });

    // Scale the range of the data
    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));
    y.domain([0, d3.max(data, function(d) {
      return d.close+20;
    })]);

    // Add the valueline path.
    lineSvg.append("path")
      .attr("class", "line")
      .attr("d", valueline(data));
      
    lineSvg.append("path")
      .attr("d", area(data))
      .style("fill", "steelblue")
      .style("stroke", "none")
      .style("opacity", "0.5")
      .attr("clip-path", "url(#areaClip)")

    // Add the X Axis
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    // Add the Y Axis
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    // append the x line
    focus.append("line")
      .attr("class", "x")
      .style("stroke", "blue")
      .style("stroke-dasharray", "3,3")
      .style("opacity", 0.5)
      .attr("y1", 0)
      .attr("y2", height);

    // append the y line
    focus.append("line")
      .attr("class", "y")
      .style("stroke", "blue")
      .style("stroke-dasharray", "3,3")
      .style("opacity", 0.5)
      .attr("x1", width)
      .attr("x2", width);

    // append the circle at the intersection
    focus.append("circle")
      .attr("class", "y")
      .style("fill", "none")
      .style("stroke", "blue")
      .attr("r", 4);

    // place the value at the intersection
    focus.append("text")
      .attr("class", "y1")
      .style("stroke", "white")
      .style("stroke-width", "3.5px")
      .style("opacity", 0.8)
      .attr("dx", 8)
      .attr("dy", "-.3em");
    focus.append("text")
      .attr("class", "y2")
      .attr("dx", 8)
      .attr("dy", "-.3em");

    // place the date at the intersection
    focus.append("text")
      .attr("class", "y3")
      .style("stroke", "white")
      .style("stroke-width", "3.5px")
      .style("opacity", 0.8)
      .attr("dx", 8)
      .attr("dy", "1em");
    focus.append("text")
      .attr("class", "y4")
      .attr("dx", 8)
      .attr("dy", "1em");

    // append the rectangle to capture mouse
    var context  = svg.append("g");
    
    context.call(brush);
    
    context.selectAll(".resize").append("path")
      .attr("d", "M0,2V" + (height - 2))
      .style("stroke", "black")
    
    context.select(".extent")
      .attr("height", height - 2)
      .attr("fill", "none");
    
    context.select(".background")
      .attr("height", height)
      .on("mouseover.tooltip", function() {
        focus.style("display", null);
      })
      .on("mouseout.tooltip", function() {
        focus.style("display", "none");
      })
      .on("mousemove.tooltip", mousemove);

    function mousemove() {
      var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0;

      focus.select("circle.y")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")");

      focus.select("text.y1")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")")
        .text(d.close);

      focus.select("text.y2")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")")
        .text(d.close);

      focus.select("text.y3")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")")
        .text(formatDate(d.date));

      focus.select("text.y4")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")")
        .text(formatDate(d.date));

      focus.select(".x")
        .attr("transform",
          "translate(" + x(d.date) + "," +
          y(d.close) + ")")
        .attr("y2", height - y(d.close));

      focus.select(".y")
        .attr("transform",
          "translate(" + width * -1 + "," +
          y(d.close) + ")")
        .attr("x2", width + width);
    }
    
  </script>
</body>
Mark
  • 106,305
  • 20
  • 172
  • 230
  • Thank you! can you help me with one more problem? I want to make 1) the brush displayed area only under the curve(just like the google example and 2) have the pole like thingy at the end of the brush. Not sure how to accomplish that... – MIN KYU YUN Jul 31 '16 at 07:36
  • @MINKYUYUN, that's actually pretty tricky. See edits to code snippet above. – Mark Jul 31 '16 at 14:14
  • I tried to add another line to it, just like http://bl.ocks.org/davidshinn/7917466 I tried it by adding another svg.line and 'path' to the line. I guess i have to map it somehow...? 1) how could i do it using the data on my question? 2) currently, brushing can start from any coordinate to any coordinate. when i console.log(brush.extent()), i get time depending on how much i extend the brush. Is it possible to make the brush extend daily base(only from circle to circle?), just like it is in google? I appreciate it so much – MIN KYU YUN Aug 01 '16 at 12:54
  • @MINKYUYUN, here's a quick update for [multiple lines](http://plnkr.co/edit/ApzcjP9OAtkD4TV2o7AT?p=preview). You'll need to add to the tooltip for that scenerio. For your second question, you need to implement brush snapping, see here for [example](https://bl.ocks.org/mbostock/6232620/d71a0beca70c9101231bcaec4b3dcf0e4f71ef75) – Mark Aug 01 '16 at 14:33
  • 2
    @Mark just to update the brush rect class is now ".overlay" rather than ".background" - other than that this technique works perfectly in the current d3v7 - nice :) – Fraser Mar 06 '22 at 17:34