Search code examples
d3.jsbrush

D3 Brush events - which brush was moved? The left one or the right one?


Hopefully the title says it all.

I am handling a D3 brush moved event. And am trying to work out, whether the user moved the left brush or the right brush...?

I really want to avoid storing the mouse position somewhere.

Is there are relatively easy way to work this out..?


Solution

  • Easiest way I can see:

    // bind to at least start and end events
    var brush = d3.brushX()
        .extent([[0, 0], [width, height]])
        .on("start brush end", brushmoved);
    
    // handle it
    var bs = "";
    function brushmoved() {
      var s = d3.event.selection;
    
      if (d3.event.type === "start"){
        bs = d3.event.selection;
      } else if (d3.event.type === "end"){
        if (bs[0] !== s[0] && bs[1] !== s[1]) {
          console.log('moved both');
        } else if (bs[0] !== s[0]) {
          console.log('moved left');
        } else {
          console.log('moved right');
        }
      }
    

    }


    Full example:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>
    
    circle {
      fill-opacity: 0.2;
      transition: fill-opacity 250ms linear;
    }
    
    circle.active {
      stroke: #f00;
    }
    
    </style>
    <svg width="960" height="500"></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script>
    
    var data = d3.range(800).map(Math.random);
    
    var svg = d3.select("svg"),
        margin = {top: 194, right: 50, bottom: 214, left: 50},
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom,
        g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var x = d3.scaleLinear().range([0, width]),
        y = d3.randomNormal(height / 2, height / 8);
    
    var brush = d3.brushX()
        .extent([[0, 0], [width, height]])
        .on("start brush end", brushmoved);
    
    g.append("g")
        .attr("class", "axis axis--x")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x));
    
    var circle = g.append("g")
        .attr("class", "circle")
      .selectAll("circle")
      .data(data)
      .enter().append("circle")
        .attr("transform", function(d) { return "translate(" + x(d) + "," + y() + ")"; })
        .attr("r", 3.5);
    
    var gBrush = g.append("g")
        .attr("class", "brush")
        .call(brush);
    
    var handle = gBrush.selectAll(".handle--custom")
      .data([{type: "w"}, {type: "e"}])
      .enter().append("path")
        .attr("class", "handle--custom")
        .attr("fill", "#666")
        .attr("fill-opacity", 0.8)
        .attr("stroke", "#000")
        .attr("stroke-width", 1.5)
        .attr("cursor", "ew-resize")
        .attr("d", d3.arc()
            .innerRadius(0)
            .outerRadius(height / 2)
            .startAngle(0)
            .endAngle(function(d, i) { return i ? Math.PI : -Math.PI; }));
    
    gBrush.call(brush.move, [0.3, 0.5].map(x));
    
    var bs = "";
    function brushmoved() {
      var s = d3.event.selection;
      
      if (d3.event.type === "start"){
        bs = d3.event.selection;
      } else if (d3.event.type === "end"){
        if (bs[0] !== s[0] && bs[1] !== s[1]) {
          console.log('moved both');
        } else if (bs[0] !== s[0]) {
          console.log('moved left');
        } else {
          console.log('moved right');
        }
      }
      
      if (s == null) {
        handle.attr("display", "none");
        circle.classed("active", false);
      } else {
        var sx = s.map(x.invert);
        circle.classed("active", function(d) { return sx[0] <= d && d <= sx[1]; });
        handle.attr("display", null).attr("transform", function(d, i) { return "translate(" + s[i] + "," + height / 2 + ")"; });
      }
    }
    
    </script>