简体   繁体   中英

How to selectively zoom x and/or y in d3.js — dynamically?

I have a small JavaScript graphing/histogramming library which allows zooming/panning in either X or Y directions, or both, as declared when the plot is created. A simplified example is in this jsfiddle .

What I want to do instead is to allow the zoom behavior to change when, eg, the shift key is pressed.

The core of the zoom logic in my example currently looks like this:

 function draw() {
     // handle rendering of plot
 }

 // zoom behavior:
 var zoom = d3.behavior.zoom();
 zoom.x(xscale);
 //zoom.y(yscale);   <-- add this in if y-scrolling is to be allowed

 zoom.on("zoom", draw);

 // Restrict zoom action to display box (non-axis part) only:
 svg.append("svg:rect")
     .attr("class", "pane")
     .attr("width", w - (padding_left + padding_right))
     .attr("height", h - (padding_top + padding_bottom))
     .attr("cursor", "move")
     .attr("fill", "none")
     .attr("pointer-events", "all")
     .attr("x", padding_left)
     .attr("y", padding_top)
     .call(zoom);

 function keydown() {
     if(d3.event.shiftKey) {
         // turn on y-zoom behavior, turn off x...
     }
 }

 function keyup() {
     // revert to old behavior
 }

 d3.select("body").on("keydown", keydown);
 d3.select("body").on("keyup", keyup);

I can turn on X or Y zooming selectively when the plot is set up; but I want to do this intermittently, when the shift key is pressed, ie in keydown and keyup . Since the zoom behavior is wired in to my "pane" div once, it's not clear to me how to make this dynamic.

Thanks!

The easiest way to do this would be to handle this in your zoom function. That is, instead of modifying what events are generated, you just modify how to handle them. So if x zoom is enabled, you only specify the x scale. The code would look something like this.

var zoom = d3.behaviour.zoom().on("zoom", onZoom);
var svg = d3.select("body").append("svg").call(zoom);

function onZoom() {
  if(x_zoom) {
    svg.attr("transform", "scale(" + d3.event.scale + ", 1)");
  }
  else if(y_zoom) {
    svg.attr("transform", "scale(1, " + d3.event.scale + ")");
  }
}

I've written a plugin that modifies d3's default zoom behavior to allow for this:

在此处输入图片说明

https://github.com/mathisonian/d3-multiaxis-zoom

It turns out that if you call zoom.x or .y again with a new d3.scale.linear() argument, it will disable that axis's pan/zoom behavior, and if you call it again with the original xscale or yscale , it will reenable it.

However, one problem I ran into is that, unlike mouseover events, I could not tie keydown and keyup events to an individual div or svg ; they are fired for the page as a whole (see this related question ). This meant that, in order to support multiple graphs in i3d3 , I had to save separate zoom and scale behaviors for each plot; otherwise, only the last plot on the page would have the correct behavior.

I'll keep this question unanswered for a while to see if other people have additional suggestions.

I will just add something in Lars Kotthoff answer.

It will be much better if we add translation as well ie

//For X-Zoom
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + " ,1)");
//For Y-Zoom
svg.attr("transform", "translate(" + d3.event.translate + ")scale(1, " + d3.event.scale + ")");

This solution will work even if your drawing svg is in different container and scale svg is in different container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM