D3 Brush coordinates under CSS transform scale

2019-06-07 18:45发布

D3 brush does not function properly under css transform scale. When svg is under div element, and div element is transformed using CSS scale, the brush operation shows wrong coordinates.

To demonstrate this case, here is the jsFiddle.

It is simple modification from Bostock's Brushable Network example.

What I did was simply putting SVG in the div element and made div element zoom 50% using CSS transform scale(0.5). And the brushing coordinates are not updating because of zooming.

#test {
transform: scale(0.5);
}

Thanks.

Deok

2条回答
做个烂人
2楼-- · 2019-06-07 19:22

If you use an svg based transform:

transform="scale(0.5)"

Then it'll play nice with the brush:

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

svg = svg.append("g")
    .attr("transform","scale(0.5)");

Updated fiddle.

To fix the calculations you need two things.

1.) Scale the extent rect opposite the div:

.extent {
   transform: scale(2);
}

2.) Fix the extent calculations:

   .on("brush", function() {
      var extent = d3.event.target.extent();
      node.classed("selected", function(d) {
        return extent[0][0] <= d.x && d.x < extent[1][0] && 
               extent[0][1] <= (d.y/2) && (d.y/2) < extent[1][1]; // scale y opposite div transform
      });
   });

New example here.

查看更多
不美不萌又怎样
3楼-- · 2019-06-07 19:22

If you use CSS transform you should apply it to all the classes that related by nodes:

 .node {
  stroke: #fff;
  stroke-width: 1.5px;
  transform: scale(0.5);
 }

 .node .selected {
        stroke: red;
  }

.link {
   stroke: #999;
     transform: scale(0.5);
  }

.brush .extent {
  fill-opacity: .1;
  stroke: #fff;
  shape-rendering: crispEdges;
  transform: scale(0.5);
}

When you add transform: scale(0.5); you can brush all the node. Try from left to right over node position.

 var width = 960,height = 500;
  var svg = d3.select("#body").append("svg")
        .attr("width", width)
        .attr("height", height);


    graph.links.forEach(function(d) {
        d.source = graph.nodes[d.source];
        d.target = graph.nodes[d.target];
    });

    var link = svg.append("g")
        .attr("class", "link")
        .selectAll("line")
        .data(graph.links)
        .enter().append("line")
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    var node = svg.append("g")
        .attr("class", "node")
        .selectAll("circle")
        .data(graph.nodes)
        .enter().append("circle")
        .attr("r", 4)
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });

    var brush = svg.append("g")
        .attr("class", "brush")
        .call(d3.svg.brush()
            .x(d3.scale.identity().domain([0, width]))
            .y(d3.scale.identity().domain([0, height]))
            .on("brush", function() {
                var extent = d3.event.target.extent();
                node.classed("selected", function(d) {
                    return extent[0][0] <= d.x && d.x < extent[1][0]
                        && extent[0][1] <= d.y && d.y < extent[1][1];
                });
            }));

Complete jsfiddle here.

查看更多
登录 后发表回答