preserveAspectRatio、viewBox 和 layout.size() 的问题

Issues with preserveAspectRatio, viewBox and layout.size()

我编写了以下代码来显示 d3js 树布局,但是在尝试让生成的 svg 根据其宽高比调整大小时遇到​​了一些困难。我能够(在附带的演示中)让 svg 以我想要的方式缩放,但我编写的代码受到 const ASPECT_RATIO 的限制,如下所示:

canvas.attr("viewBox", " 0 0 " + window.innerWidth + " " + (window.innerWidth * ASPECT_RATIO));

再往下,这里:

layout.size([(window.innerWidth * ASPECT_RATIO), window.innerWidth - 128]);

有什么办法可以避免这种情况吗?我不希望每次 svg 的纵横比发生变化时(即添加新内容时)都不必手动更改此值。

此致,

布莱恩


tl;dr: 帮我消除 const ASPECT_RATIO.


代码:

/// <reference path="d3.d.ts" />

"use strict";

/* (c) brianjenkins94 | brianjenkins94.me | MIT licensed */

// Get JSON, impose synchronicity
d3.json("js/data.json", function(error, treeData) {
  if (!error) {
// Instantiate canvas
var canvas = d3.select("#canvas");

// Aspect ratio nonsense
const ASPECT_RATIO = 1.89260808926;

canvas.attr("viewBox", " 0 0 " + window.innerWidth + " " + (window.innerWidth * ASPECT_RATIO));
canvas.attr("preserveAspectRatio", "xMinYMin slice");

// Update
update();

function update() {

  // Add an SVG group element
  canvas.append("g");

  // Instantiate group
  var group = canvas.select("g");

  // Translate group right
  group.attr("transform", "translate(64, 0)");

  // Instantiate layout tree
  var layout = d3.layout.tree();

  // Initialize layout dimensions
  layout.size([(window.innerWidth * ASPECT_RATIO), window.innerWidth - 128]);

  // Instantiate rotation diagonal
  var diagonal = d3.svg.diagonal();

  // Rotate projection 90 degrees about the diagonal
  diagonal.projection(function(d) { return [d.y, d.x]; });

  // Initialize node array
  var nodes = layout.nodes(treeData);

  // Initialize link array
  var links = layout.links(nodes);

  // Select all paths in group
  group.selectAll("path")
    // For each link, create a path
    .data(links).enter().append("path")
    // Provide the specific diagonal
    .attr("d", diagonal);

  // Select all groups in group
  var node = group.selectAll("g")
    // For each node, create a group
    .data(nodes).enter().append("g")
    // Translate accordingly
    .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

  // Add a circle at every node
  node.append("circle")
    .attr("r", 3);

  // Add label
  node.append("text")
    // To the left if the node has children, otherwise right
    .attr("dx", function(d) { return d.children ? -8 : 8; })
    .attr("dy", 0)
    // Branch if the node has children
    .attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
    .text(function(d) { return d.name; });
    }
  } else {
    console.log("There was a connection error of some sort.");
  }
});

演示:

这是我学到的:

  • 虽然 svg 可以 "dimensionless" 但不能 "aspect-ratio-less." 必须有一些核心宽高比来控制调整大小。
  • tree.size() and tree.nodeSize() 函数创建一个 "arbitrary coordinate system" "is not limited screen coordinates."
  • 宽高比是多少,由开发者决定,可以表示为"arbitrary coordinates",或者根据响应式设计,可以表示为相对论测量

我的解决方法如下:

// Viewbox & preserveAspectRatio
canvas.attr("viewBox", " 0 0 " + window.innerWidth + " " + (2 * window.innerWidth));
canvas.attr("preserveAspectRatio", "xMinYMin slice");

...

// Initialize layout dimensions
layout.size([(2 * window.innerWidth), (window.innerWidth - 128)]);

因此消除了对 const ASPECT_RATIO 的依赖,支持基于浏览器尺寸的相对测量。

这可能(并且几乎肯定会)导致跨多个视口的渲染不一致,但可以通过在渲染之前查询视口并进行修饰调整来相应地处理。