创建人工缩放变换事件 [英] Create artificial zoom transform event

查看:34
本文介绍了创建人工缩放变换事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在D3中有一个时间轴,高度修改了拖动/滚动和平移/缩放.缩放回调使用由缩放行为生成的 d3.event.transform 对象.

我需要添加一个使用现有回调的程序化缩放.我曾经尝试并尝试这样做,但是我还没有使它起作用,因此重用现有结构会更加容易和快捷.

因此,输入是一个新域,即 [new Date(1800,0),new Date(2000,0)] ,输出应该是一个新的 d3.event..transform 的行为完全类似于鼠标滚轮事件的输出.

一些示例现有代码:

  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);...函数zoomHandler(transformEvent){this.xScale.domain(transformEvent.rescaleX(this.xScaleShadow).domain());//更新用户界面this.timeAxis.transformHandler(transformEvent);this.updateGraphics();} 

目标示例:

  function zoomTo(extents){var newTransform = ?????(范围);zoomHandler(newTransform);} 

(请不要将此问题标记为重复,我的问题更具体,涉及的是较新的d3 API)

解决方案

假设我了解此问题:

仅根据您的问题的标题,我们就可以使用 zoom.transform ,如下所示:

  selection.call(zoom.transform,newTransform) 

其中选择是调用缩放的选择, zoom 是缩放行为对象的名称, zoom.transform 是缩放对象的功能,设置应用于所选内容的缩放变换(并发出开始,缩放和结束事件),而 newTransform 是作为以下内容提供给 zoom.transform 的变换参数(请参见文档中的 selection.call()以获得有关此模式的更多信息,但与 zoom.transform(selection,newTransform))相同.

下面,您可以通过单击按钮在矩形上设置缩放比例:该缩放比例不是在空间上而是在颜色上应用,但是在语义或几何上缩放数据时,原理是相同的.

  var scale = d3.scaleSqrt().range([红色",蓝色",黄色"]).domain([1,40,1600]);var zoom = d3.zoom().on("zoom",放大).scaleExtent([1,1600])var rect = d3.select("svg").append("rect").attr("width",400).attr("height",200).attr(填充",红色").call(zoom);//首先调用zoom.transform来触发缩放(否则,当前的缩放未开始显示).rect.call(zoom.transform,d3.zoomIdentity);//在按下按钮时调用zoom.transform将k设置为100:d3.select("button").on("click",function(){var newTransform = d3.zoomIdentity.scale(100);rect.call(zoom.transform,newTransform);})//缩放功能:函数zoomed(){var k = d3.event.transform.k;rect.attr("fill",scale(k));d3.select(#currentZoom").text(k);}  

  rect {光标:指针;}  

 < script src ="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js></script>< button>触发缩放</button>< br/>< span>当前缩放:</span>< span id ="currentZoom"></span>< br/>< svg></svg>  

如果将缩放变换应用于比例,则需要根据新范围重新调整比例.这类似于现有的画笔和缩放示例,但我将仅使用比例尺和轴在裸露的骨骼示例中进行分解(您也可以使用鼠标在比例尺上进行缩放):

  var width = 400;var height = 200;var svg = d3.select("svg").attr("width",width).attr("height",height);//用于显示轴的比例尺.var scale = d3.scaleLinear().range([0,width]).domain([0,100]);//参考比例var shadowScale = scale.copy();var axis = d3.axisBottom().scale(scale);var g = svg.append("g").attr("transform","translate(0,50)").call(axis);//标准缩放行为:var zoom = d3.zoom().scaleExtent([1,10]).translateExtent([[0,0],[width,height]]).on("zoom",zoomed);//对齐鼠标缩放事件.var rect = svg.append("rect").attr("width",width).attr("height",height).attr("fill","none").call(zoom);d3.select(#extent").on("click",function(){//根据范围重新定义比例var范围= [10,20];//建立新的缩放转换:var transform = d3.zoomIdentity.scale(width/(scale(extent [1])-scale(extent [0])))//相对于所示域,整个域的宽度是多少?.translate(-scale(extent [0]),0);//转换以考虑初始值//应用新的缩放变换:rect.call(zoom.transform,transform);})d3.select(#reset").on("click",function(){//创建一个身份转换var transform = d3.zoomIdentity;//应用转换:rect.call(zoom.transform,transform);})//处理常规和人工缩放:函数zoomed(){var t = d3.event.transform;scale.domain(t.rescaleX(shadowScale).domain());g.call(axis);}  

  rect {指针事件:全部;}  

 < script src ="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js></script>< button id ="extent">缩放至10-20范围/lt;/button>< button id ="reset"> Reset</button>< br/>< svg></svg>  

看一下关键部分,当我们想要缩放到一定程度时,我们可以使用以下几行内容:

  d3.select(某物").on("click",function(){//根据我们要显示的缩放范围重新定义缩放比例var范围= [10,20];//建立新的缩放变换(以d3.zoomIdentity为基础)var transform = d3.zoomIdentity//完整域相对于显示域的宽度是多少?.scale(width/(scale(extent [1])-scale(extent [0])))//转换以考虑初始值.translate(-scale(extent [0]),0);//应用新的缩放变换:rect.call(zoom.transform,transform);}) 

请注意,通过使用 d3.zoomIdentity ,我们可以利用身份转换(具有用于重新缩放的内置方法)并修改其比例和转换以满足我们的需求.

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)

解决方案

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.com/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.com/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.

这篇关于创建人工缩放变换事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆