but*_*fly 4 javascript math svg data-visualization d3.js
我正在使用 D3 和 Python 创建一些图。使用 Python 我可以使轴相等,如图 1 所示:

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

我使用的轴比例如下:
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);
Run Code Online (Sandbox Code Playgroud)
谁能告诉我如何实现与 D3 相等的轴?
简短回答:不,D3 中没有“轴相等”。D3是相当低级的,它只是方法的集合,所以你几乎所有的事情都必须手工完成......
好消息是,解决问题所需的只是一些数学运算来计算新的范围。
作为介绍,由于这是一个d3.js标记的问题,因此“轴相等”是一些程序中使用的术语,例如Matlab,它...
对沿每个轴的数据单位使用相同的长度。
在视觉上可以更好地解释它。看看这张图片,有不同的域和范围(来源):
在这个简短的介绍之后,让我们回到你的问题。假设这个简单的代码,生成两个轴:
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]));Run Code Online (Sandbox Code Playgroud)
svg {
border: 2px solid tan;
}
line {
stroke-dasharray: 2, 2;
stroke: gray;
}Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>Run Code Online (Sandbox Code Playgroud)
正如我们可以清楚地看到的,这些单位不是等距的。我们可以做两件事来创建具有等距单位的轴:1. 更改域;2. 更改范围。
根据您的评论,更改域不是一种选择。因此,让我们改为更改范围。
我们所需要的只是计算每个用户空间的单位数,并使用其中最大的(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]));
Run Code Online (Sandbox Code Playgroud)
xStep大于yStep,向我们表明 x 轴每个像素有更多的单位。因此,我们将更改 y 轴范围:
yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);
Run Code Online (Sandbox Code Playgroud)
这是演示:
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]));
Run Code Online (Sandbox Code Playgroud)
yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);
Run Code Online (Sandbox Code Playgroud)
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]));Run Code Online (Sandbox Code Playgroud)
正如您使用网格线所看到的,单位现在等距。
最后,请记住,以编程方式确定将更改的比例、重新调整正确的范围(例如,您的 y 比例从底部到顶部)、将轴居中等是完全不同的问题。