D3刷选和鼠标移动共存

3
我正在尝试更新http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a,并添加brushing(在线图下方显示的brushing)以使其看起来像https://www.google.com.hk/#q=s%26p+500。已添加代码到第一个链接:
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());
}

当我将brushing添加到其中后,问题就出现了,图形的背景会有一个覆盖层,这样就无法执行鼠标事件(mousemove)。有没有一种方法可以修复它,使它看起来像Google? 1)刷选和鼠标事件共存 2)在曲线下面进行刷选
变量csv如下: 日期,收盘价1,收盘价2 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
1个回答

4

您提供的示例和brush都在图表上方添加了一个rect来捕获鼠标事件。让它们共存的关键是先添加brush(并允许其创建其自己的rect),然后使用该rect添加工具提示事件。这样,您最终只会得到一个点事件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);

完整代码:

<!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>


谢谢! 你能帮我解决另一个问题吗? 我想让1)画笔显示区域仅在曲线下方(就像谷歌的例子一样),并且2)在画笔末端有一个类似于极点的东西。 不确定如何实现... - MIN KYU YUN
@MINKYUYUN,这实际上相当棘手。请参见上面代码片段的编辑。 - Mark
我试图像http://bl.ocks.org/davidshinn/7917466那样给它添加另一行。 我尝试通过添加另一个svg.line和“path”到该行来实现它。 我猜我必须以某种方式进行映射...? 1)如何使用我的问题中的数据来完成它? 2)目前,刷子可以从任何坐标开始到任何坐标结束。当我console.log(brush.extent())时,我得到根据我扩展刷子的程度而变化的时间。是否可能使刷子按日基础延伸(仅从圆到圆?),就像在Google中一样? 非常感谢。 - MIN KYU YUN
@MINKYUYUN,这是一个快速更新multiple lines。你需要为该场景添加工具提示。对于你的第二个问题,你需要实现刷子捕捉,请参考示例 - Mark
2
@Mark,只是想更新一下,现在画刷矩形类别已经改为“.overlay”而不是“.background”了——除此之外,这种技术在当前的d3v7中完美运作——很棒 :) - Fraser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接