javascript - D3 中有 "axis equal"吗?

标签 javascript d3.js math svg data-visualization

我正在使用 D3 和 Python 创建一些绘图。使用 Python,我可以做到轴相等,如图 1 所示:

With Axis Equal in Python

我想在使用 D3 的 SVG 中实现同样的效果。请引用图 2,没有轴相等:

Without Axis Equal in Javascript d3

我使用的坐标轴比例如下:

var xScale = d3.scaleLinear()
    .range([0, width])
    .domain([minX, maxX]);

var yScale = d3.scaleLinear()
    .range([height, 0])
    .domain([minY, maxY]);

var xAxis = d3.axisBottom(xScale).ticks(5),
    yAxis = d3.axisLeft(yScale).ticks(5);

谁能告诉我如何实现与 D3 相等的轴?

最佳答案

简答 :不,D3 中没有“轴相等”。 D3 级别很低,只是方法的集合,所以几乎所有事情都必须手动完成……

好消息是,解决问题所需的只是一些数学来计算新的范围。

作为介绍,因为这是 标记的问题,“轴相等”是一些程序中使用的术语,如 Matlab , 哪个...

Use the same length for the data units along each axis.



它可以更好地从视觉上解释。看看这张图片,不同的域和范围(source):

enter image description here

在这个简短的介绍之后,让我们回到你的问题。假设这个简单的代码,生成两个轴:

const minX = 0,
  minY = 0,
  maxX = 120,
  maxY = 50,
  width = 500,
  height = 300,
  paddings = [10, 10, 20, 30];

const xScale = d3.scaleLinear()
  .range([paddings[3], width - paddings[1]])
  .domain([minX, maxX]);

const yScale = d3.scaleLinear()
  .range([height - paddings[2], paddings[0]])
  .domain([minY, maxY]);

const svg = d3.select("svg")
  .attr("width", width)
  .attr("height", height);

svg.append("g")
  .attr("transform", `translate(0,${height - paddings[2]})`)
  .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + paddings[0]));

svg.append("g")
  .attr("transform", `translate(${paddings[3]},0)`)
  .call(d3.axisLeft(yScale).tickValues(xScale.ticks()).tickSizeInner(-width + paddings[3] + paddings[1]));
svg {
  border: 2px solid tan;
}

line {
  stroke-dasharray: 2, 2;
  stroke: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>


我们可以清楚地看到,这些单元的间距不相等。我们可以做两件事来创建具有等间距单位的轴:
1.更改域名;
2. 改变范围。

根据your comment ,更改域不是一种选择。所以,让我们改变范围。

我们所需要的只是计算每个用户空间的单位数,并使用其中最大的(x 或 y)来改变相反比例的范围。

例如,给定上面片段的比例:
const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0]));

const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0]));
xStep大于 yStep ,向我们展示了 x 轴每个像素有更多的单位。因此,我们将更改 y 轴范围:
yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);

这是演示:

const minX = 0,
  minY = 0,
  maxX = 120,
  maxY = 50,
  width = 500,
  height = 300,
  paddings = [10, 10, 20, 30];

const xScale = d3.scaleLinear()
  .range([paddings[3], width - paddings[1]])
  .domain([minX, maxX]);

const yScale = d3.scaleLinear()
  .range([height - paddings[2], paddings[0]])
  .domain([minY, maxY]);

const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0]));

const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0]));

yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);

const svg = d3.select("svg")
  .attr("width", width)
  .attr("height", height);

svg.append("g")
  .attr("transform", `translate(0,${height - paddings[2]})`)
  .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + yScale.range()[1]));

svg.append("g")
  .attr("transform", `translate(${paddings[3]},0)`)
  .call(d3.axisLeft(yScale).tickValues(yScale.ticks().filter(function(d){return !(+d%10)})).tickSizeInner(-width + paddings[3] + paddings[1]));
svg {
  border: 2px solid tan;
}

line {
  stroke-dasharray: 2, 2;
  stroke: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>


正如您使用网格线所看到的,这些单元现在是等距的。

最后,请记住,以编程方式确定要更改的比例、重新调整正确的范围(例如,您的 y 比例,从底部到顶部)、集中轴等是完全不同的问题。

关于javascript - D3 中有 "axis equal"吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59262026/

相关文章:

javascript - 使用 Javascript 添加两个输入框

javascript - 如何通过 D3 重复和延迟对 API 的调用

javascript - 如何在此 slider 上访问时添加随机图像?

javascript - D3 - 获取垂直条形图中矩形顶部的文本

javascript - D3 图表 - 圆环图 SVG 中心的图像

python - 检测立方体和圆锥体是否相交?

php - 使用任意精度数学的 log() 算法示例

java - 将数字四舍五入到特定的倍数

javascript滚动到新页面上的位置

javascript - 在 vanilla javascript 中实现 siblings()