T Nguyen
T Nguyen

Reputation: 3415

D3: remap mousewheel to be panning gesture instead of zoom gesture

By default, when you create a new zoom behavior in D3, it maps the mousewheel to control zoom level. You can also click and drag to pan the chart if it's larger than the chart area. I'd like to remap the mousewheel gesture to instead pan on the vertical axis (mousewheel up pans up, mousewheel down pans down). Anyone have any clue how to accomplish this?

Upvotes: 2

Views: 3688

Answers (2)

swinc
swinc

Reputation: 326

You can also intercept wheel events in the function you create with d3.behavior.zoom(). "Zoom" events are triggered on both wheel scroll and mouse drag by default, so one potential strategy is to just wait for zoom events and intercept the wheel events within the zoom function.

For example, here's an approach to doing Y translations with both wheel scroll and mouse drag:

var panYOnScrollAndDrag = d3.behavior.zoom()
   .scaleExtent([1, 1]) // prevents wheel events (or anything) from changing the scale
   .on("zoom", function() {
      if (d3.event.sourceEvent.type === "wheel") {
         // use the `d3.event.sourceEvent.deltaY` value to translate
         // e.g., yTranslation += yTranslationDelta;
      } else if (d3.event.sourceEvent.type === "mousemove") {
         // use the normal d3.event.translate array to translate
         // e.g., yTranslation = d3.event.translate[1];
      }
      container.attr("transform", "translate(0," + yTranslation + ")");
   });

Then you can throw this function at the relevant d3 container like so, selection.call(panYOnScrollAndDrag), and all the logic is contained in that one function.

Upvotes: 6

ahmohamed
ahmohamed

Reputation: 2970

Ok, here we go: Based on Lars' comment, we can specify event handler for mousewheel event. As shown in the answer, we start by mapping wheel.zoom event to a custom handler pan

selection.call(zoomer)
      .on("wheel.zoom",pan) // handler function for mousewheel zoom

Second, we need to define the pan gesture, which is basically a translate in x and/or y direction.

function pan() {
  svg.attr("transform", "translate(" + [dx,dy] + ")");
}

We also need to quantify the movement in both directions, and relate it to mousewheel movement. By inspecting the details of MouseWheel event, we find two useful attributes deltaX and deltaY, indicating how much the mousewheel moved in each direction.

The final pan function is as follows

function pan() {
    current_translate = d3.transform(svg.attr("transform")).translate;
    dx = d3.event.wheelDeltaX + current_translate[0];
    dy = d3.event.wheelDeltaY + current_translate[1];
    
  svg.attr("transform", "translate(" + [dx,dy] + ")");
  d3.event.stopPropagation();
}

Here is a working fiddle and bl.ock also modifying Mike's geometric zoom example.

Cross-browser support:

It seems that mousewheel events differs between browsers. For Safari and Firefox support, you need to add the following:

selection.call(zoomer)
  .on("wheel.zoom",pan) 
  .on("mousewheel.zoom", pan)
  .on("DOMMouseScroll.zoom", pan)

Also, the interpretation of wheelDelta is reversed in Firefox. wheelDelta can be fixed by this function

function crossWheelDelta()
  // cross-browser wheel delta
  var e = window.event || e; // old IE support
  return Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
}

Upvotes: 6

Related Questions