创建人工缩放变换事件 [英] Create artificial zoom transform event
问题描述
我在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屋!