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

phk*_*phk 5 javascript svg d3.js

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

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

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

    ...

  </g>
</g>
Run Code Online (Sandbox Code Playgroud)

请参阅以下尝试:

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);
Run Code Online (Sandbox Code Playgroud)
html,
body,
#svgWrapper,
svg {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
Run Code Online (Sandbox Code Playgroud)
<div id="svgWrapper">
  <svg></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

红十字代表视口中心,圆圈代表可以拖动的内容。单击“整页”并调整窗口大小以查看调整大小的实际效果。

遗憾的是,第一个平移以及向某个点的缩放似乎已损坏。将两者合并translates()为一个transform也不起作用。

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

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

单次变换

由于各种原因,将其变成单个转换(即调用zoom.transform每次调整大小)在我的情况下并不是一个好的解决方案:

  • start//当窗口大小调整时,这会导致zoom事件被发出,甚至检查那里也没有多大帮助endevent.sourceEvent

  • 平移限制 (.translateExtent()​​ ) 与显示器相关。

变换回内部变换内部

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

zoom.extent()

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

phk*_*phk 3

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

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);
Run Code Online (Sandbox Code Playgroud)
html,
body,
#svgWrapper,
svg {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
Run Code Online (Sandbox Code Playgroud)
<div id="svgWrapper">
  <svg></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

这对于示例来说效果很好,但遗憾的是覆盖层捕获了所有事件,因此显然您不能拥有可点击/可悬停的元素。请随意提出替代解决方案。

由于这些原因,我最终在我的项目中采用了单一转换方法。