保持内容居中并允许平移/放大d3

时间:2017-10-02 20:50:28

标签: javascript d3.js svg

在我基于d3.js的项目中,我希望保持当前视图中心的内容,即使视图的宽度或高度发生变化,也要保持居中。同时我想让用户通过平移和缩放进行交互。

我的想法是使用嵌套组,外部组用于居中,内部组用作d3.zoom目标:

<g transform="translate(WIDTH/2, HEIGHT/2)">
  <g transform="translate(ZOOM_X, ZOOM_Y) scale(ZOOM_K)">

    ...

  </g>
</g>

请参阅以下尝试:

&#13;
&#13;
var svgWrapper = d3.select("#svgWrapper").node(),
  svg = d3.select("svg"),
  width = svgWrapper.offsetWidth,
  height = svgWrapper.offsetHeight;

var outerG = svg.append("g");
var g = outerG.append("g");

outerG.append("line")
  .attr("stroke", "red")
  .attr("stroke-width", 2)
  .attr("x1", -10)
  .attr("x2", 10)
  .attr("y1", -10)
  .attr("y2", 10);

outerG.append("line")
  .attr("stroke", "red")
  .attr("stroke-width", 2)
  .attr("x1", -10)
  .attr("x2", 10)
  .attr("y1", 10)
  .attr("y2", -10);

for (var i = 1; i <= 3; ++i) {
  g.append("circle")
    .attr("fill", "none")
    .attr("stroke", "black")
    .attr("stroke-width", 1.5)
    .attr("r", i * 5);
}

var zoom = d3.zoom();

svg.call(zoom
  .scaleExtent([1 / 2, 4])
  .on("zoom", zoomed));

g.call(zoom.transform, d3.zoomIdentity.translate(-width / 2, -height / 2));

function zoomed() {
  g.attr("transform", d3.event.transform);
}

// https://developer.mozilla.org/en-US/docs/Web/Events/resize#requestAnimationFrame_customEvent
function throttle(type, name, obj) {
  obj = obj || window;
  var running = false;
  var func = function() {
    if (running) {
      return;
    }
    running = true;
    requestAnimationFrame(function() {
      obj.dispatchEvent(new CustomEvent(name));
      running = false;
    });
  };
  obj.addEventListener(type, func);
}

function resize() {
  width = svgWrapper.offsetWidth;
  height = svgWrapper.offsetHeight;
  zoom.extent([
    [0, 0],
    [width, height]
  ]);
  outerG.attr("transform", "translate(" + (width / 2) + ", " + (height / 2) + ")");
}

resize();

throttle("resize", "optimizedResize");
window.addEventListener("optimizedResize", resize);
&#13;
html,
body,
#svgWrapper,
svg {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
&#13;
<div id="svgWrapper">
  <svg></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

红叉表示视口中心,圆圈表示可以拖动的内容。点击&#34;整页&#34;并调整窗口大小以查看调整大小。

可悲的是,第一个平底锅以及朝某一点放大似乎都被打破了。将translates()同时放入单个transform也无效。

为什么?有什么可以做的吗?

目前为止的想法和失败的尝试

单次转换

将其转换为单个转换,即在每个调整大小时调用zoom.transform,由于各种原因,在我的情况下不会是一个很好的解决方案:

  • 调整窗口大小时会导致发出start / zoom / end个事件,甚至检查event.sourceEvent不会发生帮助很多

  • 平移限制(.translateExtent())将相对于显示。

转换回内部变换

与单一转换方法相比,这几乎没有任何优势,而且我的有限测试似乎也存在问题。

zoom.extent()

我不明白这对我有什么帮助。

1 个答案:

答案 0 :(得分:1)

添加<rect>可以捕获https://bl.ocks.org/mbostock/4e3925cdc804db257a86fdef3a032a45中的所有输入似乎有效但我必须将矩形添加到外部组并将其转换回来(否则缩放中心将关闭):

&#13;
&#13;
var svgWrapper = d3.select("#svgWrapper").node(),
  svg = d3.select("svg"),
  width = svgWrapper.offsetWidth,
  height = svgWrapper.offsetHeight;

var outerG = svg.append("g");
var g = outerG.append("g");

outerG.append("line")
  .attr("stroke", "red")
  .attr("stroke-width", 2)
  .attr("x1", -10)
  .attr("x2", 10)
  .attr("y1", -10)
  .attr("y2", 10);

outerG.append("line")
  .attr("stroke", "red")
  .attr("stroke-width", 2)
  .attr("x1", -10)
  .attr("x2", 10)
  .attr("y1", 10)
  .attr("y2", -10);

for (var i = 1; i <= 3; ++i) {
  g.append("circle")
    .attr("fill", "none")
    .attr("stroke", "black")
    .attr("stroke-width", 1.5)
    .attr("r", i * 5);
}

var zoom = d3.zoom();

var rect = outerG.append("rect")
  .attr("x", "-50%")
  .attr("y", "-50%")
  .attr("width", "100%")
  .attr("height", "100%")
  .style("fill", "yellow")
  .style("opacity", ".1")
  .style("pointer-events", "all")
  .call(zoom
    .scaleExtent([1 / 2, 4])
    .on("zoom", zoomed));

function zoomed() {
  g.attr("transform", d3.event.transform);
}

// https://developer.mozilla.org/en-US/docs/Web/Events/resize#requestAnimationFrame_customEvent
function throttle(type, name, obj) {
  obj = obj || window;
  var running = false;
  var func = function() {
    if (running) {
      return;
    }
    running = true;
    requestAnimationFrame(function() {
      obj.dispatchEvent(new CustomEvent(name));
      running = false;
    });
  };
  obj.addEventListener(type, func);
}

function resize() {
  width = svgWrapper.offsetWidth;
  height = svgWrapper.offsetHeight;
  zoom.extent([
    [0, 0],
    [width, height]
  ]);
  outerG.attr("transform", "translate(" + (width / 2) + ", " + (height / 2) + ")");
}

resize();

throttle("resize", "optimizedResize");
window.addEventListener("optimizedResize", resize);
&#13;
html,
body,
#svgWrapper,
svg {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
&#13;
<div id="svgWrapper">
  <svg></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

这适用于该示例,但遗憾的是叠加层捕获了所有事件,因此显然您不能拥有可点击/可收回的元素。随意提出替代解决方案。

由于这些原因,我毕竟采用单一转换方法进行我的项目。