d3缩放:滚动轴在滚动上,拖动轴在拖动,缩放轴在捏紧状态

时间:2019-04-20 22:46:53

标签: d3.js

我希望将绘图的缩放行为配置为具有三种交互方式:

  • 应该可以使用滚轮从左向右平移。
  • 应该可以通过鼠标向下拖动事件从左向右平移。
  • 应该可以在出现捏事件的情况下放大一维(即按住控制键的滚轮)。

现在,我可以让后两个在这个小提琴中工作:https://jsfiddle.net/s1t7mrpw/6/

缩放功能如下:

function zoomed() {

  if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.type === 'mousemove') {

    view.attr("transform", d3.event.transform);
    centerline.call(xAxis.scale(d3.event.transform.rescaleX(x)));
    d3.event.transform.y = 0;
    g.attr("transform", d3.event.transform);

  } else {

    current_transform = d3.zoomTransform(view);
    current_transform.x = current_transform.x - d3.event.sourceEvent.deltaY;

    // what do I do here to pan the axis and update `view`'s transform? 
    // centerline.call(xAxis.scale(d3.event.transform.rescaleX(x))); ? 
    // view.attr('transform', current_transform); ? 

    g.attr('transform', current_transform);
  }
}

它使用以下代码块中的重新缩放术语:

它使用d3-xyzoom在x和y方向上进行独立缩放。

但是当用户只是滚动(不按控制键)时,我不知道如何在else括号中平移轴。

我以前为wheel.zoom使用了一个单独的触发器,但是随后我还需要处理该其他函数中的控制键。

我基本上希望mousemove的默认缩放行为以及按下控制键时的默认缩放行为,但是在不按下控制键时平移而不是缩放滚动。

1 个答案:

答案 0 :(得分:0)

我在此答案上使用d3-zoom,因为它更常见且是d3捆绑包的一部分。 d3-xyzoom作为一个附加模块,以d3-zoom为基础添加了一些有用的功能,因此该解决方案应与带有较小修订的xyzoom一起使用-但我认为不必使用它

主要挑战在于使用滚轮来应用平移更改而不是比例更改。粗略地讲,当发生无控制的滚动事件时,您可以抓住所选内容的转换(应该是节点),并通过获取滚动并为其创建x偏移量来更新它以平移视图矩形。 但是,您不会更新zoomTransform来使缩放引起的缩放变化无效-因此,当您通过拖动进行平移时,矩形会更改大小,因为您尚未更新缩放所使用的zoomTransform行为。同样,如果不考虑缩放比例,则可以关闭转换。

应用于元素的缩放变换(作为“变换”属性)与缩放行为所跟踪的缩放变换之间的关联是SO上许多麻烦的原因-但由于不需要使用元素的变换,它允许实现更自由的实现方式属性以应用缩放行为(在画布中有用,对非正统的事物(例如颜色)进行缩放等)。

我将在这里提出一个基本的解决方案。

首先,可以通过修改d3.event.transform对象本身(或使用d3.zoomTransform(selection.node)(其中选择是最初调用缩放的选择))来更新由缩放行为跟踪的缩放变换。

第二,当d3.event.transform对象注册为按比例滚动时,我们需要覆盖此比例,并将此比例转换为翻译-需要时。为此,我们需要跟踪当前(事件前)的翻译和缩放比例-因此我们可以手动对其进行修改:

// Keep track of zoom state:
var k = 1;   
var tx = 0;

function zoomed() {
  var t = d3.event.transform;

  // If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
  if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
    t.k > k ? tx += 40/k : tx -= 40/k;
    t.k = k;
    t.x = tx;
  }
  // otherwise, proceed as normal, but track current k and tx:
  else {
    k = t.k;
    tx = t.x;
  }

  // Apply the transform:
  view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
  axisG.call(xAxis.scale(t.rescaleX(xScale)));

}

以上,我修改了t.k和t.k的转换,以适应需要滚动/滚动的事件。通过将t.k与存储的k值进行比较,我可以确定方向:通常是缩小还是放大事件,然后可以将其转换为当前转换值的变化。通过将t.k设置回原始的k值,我可以停止发生比例缩放的更改。

对于其他缩放事件,与往常一样, 除外,我存储t.x和t.k值以备以后使用,以便车轮事件应该转换而不是缩放。

注意:我已经设置了y缩放比例,并在转换中将其转换为硬编码零,因此我们完全不必担心y分量。

var width = 500;
var height = 120;

// Keep track of zoom state:
var k = 1; 
var tx = 0;

// Some text to show the transform parameters:
var p = d3.select("body")
  .append("p");  

// Nothing unordinary here:
var svg = d3.select("body")
   .append("svg")
   .attr("width",width)
   .attr("height",height);
   
var xScale = d3.scaleLinear()
  .domain([0,100])
  .range([20,width-20])
  
var xAxis = d3.axisTop(xScale);

var axisG = svg.append("g")
  .attr("transform","translate(0,50)")
  .call(xAxis);
  
var view = svg.append("rect")
  .attr("width", 20)
  .attr("height",20)
  .attr("x", width/2)
  .attr("y", 60)

var zoom = d3.zoom()
  .on("zoom",zoomed);
  
svg.call(zoom)


function zoomed() {
  var t = d3.event.transform;

  // If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
  if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
	t.k > k ? tx += 40/k : tx -= 40/k;
	t.k = k;
	t.x = tx;
  }
  // otherwise, proceed as normal, but track current k and tx:
  else {
	k = t.k;
	tx = t.x;
  }
  
  // Apply the transform:
  view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
  axisG.call(xAxis.scale(t.rescaleX(xScale)));
  
  // Show current zoom transform:
  p.text("transform.k:" + t.k + ", transform.x: " + t.x);

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>