javascript - Create artificial zoom transform event - Stack Overflow

I have a timeline in D3 with a highly modified dragscroll panzoom. The zoom callbacks use the d3.even

I have a timeline in D3 with a highly modified drag/scroll pan/zoom. The zoom callbacks use the d3.event.transform objects generated by the zoom behavior.

I need to add a programmatic zoom that uses my existing callbacks. I have tried and tried to do this without doing so, but I haven't gotten it to work and it would be radically easier and faster to reuse the existing structure.

So the input is a new domain, i.e. [new Date(1800,0), new Date(2000,0)], and the output should be a new d3.event.transform that acts exactly like the output of a, say, mousewheel event.

Some example existing code:

this.xScale = d3.scaleTime()
  .domain(this.initialDateRange)
  .range([0, this.w]);

this.xScaleShadow = d3.scaleTime()
  .domain(this.xScale.domain())
  .range([0, this.w]);

this.zoomBehavior = d3.zoom()
  .extent([[0, 0], [this.w, this.h]])
  .on('zoom', this.zoomHandler.bind(this));

this.timelineSVG
  .call(zoomBehavior);

... 

function zoomHandler(transformEvent) {
  this.xScale.domain(transformEvent.rescaleX(this.xScaleShadow).domain());

  // update UI
  this.timeAxis.transformHandler(transformEvent);
  this.updateGraphics();
}

Example goal:

function zoomTo(extents){
  var newTransform = ?????(extents);

  zoomHandler(newTransform);
}

(Please don't mark as duplicate of this question, my question is more specific and refers to a much newer d3 API)

I have a timeline in D3 with a highly modified drag/scroll pan/zoom. The zoom callbacks use the d3.event.transform objects generated by the zoom behavior.

I need to add a programmatic zoom that uses my existing callbacks. I have tried and tried to do this without doing so, but I haven't gotten it to work and it would be radically easier and faster to reuse the existing structure.

So the input is a new domain, i.e. [new Date(1800,0), new Date(2000,0)], and the output should be a new d3.event.transform that acts exactly like the output of a, say, mousewheel event.

Some example existing code:

this.xScale = d3.scaleTime()
  .domain(this.initialDateRange)
  .range([0, this.w]);

this.xScaleShadow = d3.scaleTime()
  .domain(this.xScale.domain())
  .range([0, this.w]);

this.zoomBehavior = d3.zoom()
  .extent([[0, 0], [this.w, this.h]])
  .on('zoom', this.zoomHandler.bind(this));

this.timelineSVG
  .call(zoomBehavior);

... 

function zoomHandler(transformEvent) {
  this.xScale.domain(transformEvent.rescaleX(this.xScaleShadow).domain());

  // update UI
  this.timeAxis.transformHandler(transformEvent);
  this.updateGraphics();
}

Example goal:

function zoomTo(extents){
  var newTransform = ?????(extents);

  zoomHandler(newTransform);
}

(Please don't mark as duplicate of this question, my question is more specific and refers to a much newer d3 API)

Share Improve this question edited May 1, 2019 at 3:58 Gerardo Furtado 102k9 gold badges128 silver badges177 bronze badges asked Apr 30, 2019 at 19:26 T3db0tT3db0t 3,5514 gold badges33 silver badges45 bronze badges 2
  • Have you looked at brush and zoom examples? In these the brush is used to rescale the scale and then apply a new zoom by setting a new transform (with selection.call(zoom.transform, zoomTransform). Assuming I understand the question correctly, I'll fill an answer out later when I'm free later tonight. – Andrew Reid Commented Apr 30, 2019 at 19:49
  • @AndrewReid I have, but to be honest I simply don't understand the details of zoom.transform and other moving parts... – T3db0t Commented Apr 30, 2019 at 20:00
Add a ment  | 

1 Answer 1

Reset to default 5

Assuming I understand the problem:

Simply based on the title of your question, we can assign a zoom transform and trigger a zoom event programatically in d3v4 and d3v5 using zoom.transform, as below:

selection.call(zoom.transform, newTransform)

Where selection is the selection that the zoom was called on, zoom is the name of the zoom behavior object, zoom.transform is a function of the zoom object that sets a zoom transform that is applied on a selection (and emits start, zoom, and end events), while newTransform is a transformation that is provided to zoom.transform as a parameter (see selection.call() in the docs for more info on this pattern, but it is the same as zoom.transform(selection,newTransform)).

Below you can set a zoom on the rectangle by clicking the button: The zoom is applied not spatially but with color, but the principles are the same when zooming on data semantically or geometrically.

var scale = d3.scaleSqrt()
  .range(["red","blue","yellow"])
  .domain([1,40,1600]);
  
var zoom = d3.zoom()
  .on("zoom", zoomed)
  .scaleExtent([1,1600])
    

var rect = d3.select("svg")
  .append("rect")
  .attr("width", 400)
  .attr("height", 200)
  .attr("fill","red")
  .call(zoom);
  
// Call zoom.transform initially to trigger zoom (otherwise current zoom isn't shown to start). 
rect.call(zoom.transform, d3.zoomIdentity);

// Call zoom.transform to set k to 100 on button push:
d3.select("button").on("click", function() {
  var newTransform = d3.zoomIdentity.scale(100);
  rect.call(zoom.transform, newTransform);
})

// Zoom function:
function zoomed(){
  var k = d3.event.transform.k;
  rect.attr("fill", scale(k));
  d3.select("#currentZoom").text(k);
}
rect {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare./ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Trigger Zoom</button> <br />
<span> Current Zoom: </span><span id="currentZoom"></span><br />
<svg></svg>

If applying a zoom transform to a scale, we need to rescale based on the new extent. This is similar to the brush and zoom examples that exist, but I'll break it out in a bare bones example using only a scale and an axis (you can zoom on the scale itself with the mouse too):

var width = 400;
var height = 200;

var svg = d3.select("svg")
  .attr("width",width)
  .attr("height",height);
  
// The scale used to display the axis.
var scale = d3.scaleLinear()
  .range([0,width])
  .domain([0,100]);
  
// The reference scale
var shadowScale = scale.copy();

var axis = d3.axisBottom()
  .scale(scale);
  
var g = svg.append("g")
  .attr("transform","translate(0,50)")
  .call(axis);
  
// Standard zoom behavior:
var zoom = d3.zoom()
  .scaleExtent([1,10])
  .translateExtent([[0, 0], [width, height]])
  .on("zoom", zoomed);
 
// Rect to interface with mouse for zoom events.
var rect = svg.append("rect")
  .attr("width",width)
  .attr("height",height)
  .attr("fill","none")
  .call(zoom);
  
d3.select("#extent")
  .on("click", function() {
    // Redfine the scale based on extent
    var extent = [10,20];

    // Build a new zoom transform:
    var transform = d3.zoomIdentity
      .scale( width/ ( scale(extent[1]) - scale(extent[0]) ) ) // how wide is the full domain relative to the shown domain?
      .translate(-scale(extent[0]), 0);  // Shift the transform to account for starting value
      
    // Apply the new zoom transform:
    rect.call(zoom.transform, transform);

  })
  
d3.select("#reset")
  .on("click", function() {
    // Create an identity transform
    var transform = d3.zoomIdentity;
    
    // Apply the transform:
    rect.call(zoom.transform, transform);
  })

// Handle both regular and artificial zooms:  
function zoomed() {
  var t = d3.event.transform;
  scale.domain(t.rescaleX(shadowScale).domain());
  g.call(axis);
}
rect {
  pointer-events: all;
}
<script src="https://cdnjs.cloudflare./ajax/libs/d3/5.7.0/d3.min.js"></script>
<button id="extent">Zoom to extent 10-20</button><button id="reset">Reset</button><br />
<svg></svg>

Taking a look at the key part, when we want to zoom to a certain extent we can use something along the following lines:

d3.select("something")
  .on("click", function() {
    // Redfine the scale based on scaled extent we want to show
    var extent = [10,20];

    // Build a new zoom transform (using d3.zoomIdentity as a base)
    var transform = d3.zoomIdentity
      // how wide is the full domain relative to the shown domain?
      .scale( width/(scale(extent[1]) - scale(extent[0])) ) 
      // Shift the transform to account for starting value
      .translate(-scale(extent[0]), 0);  

    // Apply the new zoom transform:
    rect.call(zoom.transform, transform);

  })

Note that by using d3.zoomIdentity, we can take advantage of the identity transform (with its built in methods for rescaling) and modify its scale and transform to meet our needs.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745441450a4627850.html

相关推荐

  • javascript - Create artificial zoom transform event - Stack Overflow

    I have a timeline in D3 with a highly modified dragscroll panzoom. The zoom callbacks use the d3.even

    3小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信