smcs
smcs

Reputation: 2004

D3: Detect mouse wheel event through overlaid SVG

In the following example, is there a way to for the zoomArea to detect a mouse wheel event that happens while pointing on one of the grey circles? The aim is to not interrupt the zoom behaviour when doing so. The circles should still be able to receive pointer events in order to e.g. display tooltips.

var dataset = [0, 2345786000, 10000000000];

var svg = d3.select("body").append("svg");
var w = 500, h = 200;
var padding = 50;
svg.attr("width", w)
   .attr("height", h);

// Background pattern 
var patternSize = 5;
svg.append("defs")
            .append("pattern")
            .attr("id", "dotPattern")
            .attr("patternUnits", "userSpaceOnUse")
            .attr("width", patternSize)
            .attr("height", patternSize)
            .append("circle")
            .attr("cx", patternSize / 2)
            .attr("cy", patternSize / 2)
            .attr("r", 2)
            .style("stroke", "none")
            .style("fill", "lightgrey")
            .style("opacity", 0.5);
   
var xScale = d3.time.scale()
    .domain([0, 10000000000])
    .range([padding, w-padding]);
var xAxis = d3.svg.axis()
    .scale(xScale)
    .ticks(5);
svg.append("g")
		.attr("class","axis")
    .attr("transform", "translate(0," + (h-padding) + ")")
    .call(xAxis);
    
var zoom = d3.behavior.zoom()
            .on("zoom", build)
            .scaleExtent([1, 20]);           
zoom.x(xScale);

var clipPath = svg.append("clipPath")
        .attr("id", "clip")
        .append("rect")
        .attr("x", padding)
        .attr("y", 0)
        .attr("width",w-2*padding)
        .attr("height", h-padding);
var zoomArea = svg.append("g")
         .attr("class", "zoomArea")
         .style("cursor","move")
         .attr("clip-path", "url(#clip)");
var zoomRect = zoomArea.append("rect")
     .attr("x", padding)
     .attr("y", 0)
     .attr("width", w-2*padding)
     .attr("height", h-padding)
     .style("fill", "url(#dotPattern)")
     .style("pointer-events", "all")
     .style("cursor","move")
     .call(zoom);

zoomArea.selectAll("circles")          
	.data(dataset)
  .enter()
  .append("circle")
  .attr("cx", function(d){
  	return xScale(d);
  })
  .attr("cy", h/2)
  .attr("r",10)
  .attr("fill","grey")

function build(){
					svg.select("g.axis").call(xAxis);
          d3.selectAll("circle")
          	  .attr("cx", function(d){
  							return xScale(d);
  						});
};    
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 3

Views: 1478

Answers (2)

Cyril Cherian
Cyril Cherian

Reputation: 32327

Another way of doing the same:

First make a rectangle with the fill background,don't attach the zoom listener to it.

var zoomRect = zoomArea.append("rect")
     .attr("x", padding)
     .attr("y", 0)
     .attr("width", w-2*padding)
     .attr("height", h-padding)
     .style("fill", "url(#dotPattern)")
     .style("cursor","move");//no zoom call

Not attach circles.

zoomArea.selectAll("circles")          
    .data(dataset)
  .enter()
  .append("circle")
  .attr("cx", function(d){
    return xScale(d);
  })
  .attr("cy", h/2)
  .attr("r",10)
  .attr("fill","grey");

Now make another rectangle same as the first except it has zoom behavior and fill transparent..so that its above all elements to handle the zoom behavior.

zoomArea.append("rect")
     .attr("x", padding)
     .attr("y", 0)
     .attr("width", w-2*padding)
     .attr("height", h-padding)
     .style("fill", "transparent")
     .style("pointer-events", "all")
     .style("cursor","move")
     .call(zoom);

Working example here

Hope this helps too!

Upvotes: 2

Cyril Cherian
Cyril Cherian

Reputation: 32327

Call zoom on circles as well.

zoomArea.selectAll("circles")          
    .data(dataset)
  .enter()
  .append("circle")
  .attr("cx", function(d){
    return xScale(d);
  })
  .attr("cy", h/2)
  .attr("r",10)
  .attr("fill","grey")
  .call(zoom);//call zoom on circle

Working code here

Hope this helps!

Upvotes: 2

Related Questions