为什么我的轴不与 d3 中的视图框对齐?
Why aren't my axes aligned with the viewbox in d3?
我正在使用视图框将数据坐标映射到 SVG 显示。
所有数据似乎都与视图框对齐(我使用红色边框显示视图框边界,使用蓝色边框显示 SVG 边界)但轴线在两个维度上都恰好偏移了 0.5。
有趣的是,刻度正确地锚定在轴应该在的位置(尽管略有偏移),只是偏移了轴线。
我尝试过旧版本的 d3(我从 3 年前将几个版本回退到 5.3.0,但所有版本都有相同的行为)
我可以看到在结果 HTML 输出中轴的 path
规范不正确,但不知道为什么 - 例如对于 y 轴,
<path class="domain" stroke="currentColor" d="M-0.2,0.5H0.5V12.5H-0.2"></path>
真的应该
<path class="domain" stroke="currentColor" d="M-0.2,0.0H0.0V12.5H-0.2"></path>
并且刻度也被 translate
d 错误地增加了 0.5 的额外 y 偏移量(这个不应该被转换或使用 translate(0,0)
):
<g class="tick" opacity="1" transform="translate(0,0.5)"><line stroke="currentColor" x2="-0.2"></line><text fill="currentColor" x="-0.35000000000000003" dy="0.32em">0</text></g>
再现测试用例:
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
fill: none;
}
rect {
fill: #bbb;
stroke: #222;
stroke-width: 0.05;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 0.1;
shape-rendering: crispEdges;
}
.debug {
fill: none;
}
.debug.red {
stroke: #f44;
}
.debug.blue {
stroke: #44f;
}
</style>
</head><body>
<!-- load the d3.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.1/d3.min.js"></script>
<script>
var data = [[0,0], [3,3], [10,10]]
var width = 12
var height = 12
const margin = 1.5
const graph = d3.select("body")
.append("svg")
.attr("viewBox", d => `-${margin} -${margin} ${width+2*margin} ${height+2*margin}`)
.attr("width", 600)
graph.append("rect")
.attr("class", "debug blue")
.attr("x", -margin)
.attr("y", -margin)
.attr("width", width + 2 * margin)
.attr("height", height + 2 * margin)
graph.append("rect")
.attr("class", "debug red")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
const scaleY = d3.scaleLinear().domain([0, height]).range([0, height])
const axisY = d3.axisLeft(scaleY).tickSize(0.2).tickPadding(0.1*margin)
const scaleX = d3.scaleLinear().domain([0, width]).range([0, width])
const axisX = d3.axisBottom(scaleX).tickSize(0.2).tickPadding(0.1*margin)
graph.append("g")
.attr("class", "axis")
.call(axisY)
graph.append("g")
.attr("class", "axis")
.attr("transform", d => `translate(0,${height})`)
.call(axisX)
const plot = graph.selectAll("plot")
.data(data)
.enter()
.append("rect")
.attr("x", d => { console.log(d); return d[0]})
.attr("y", d => d[1])
.attr("width", 0.8)
.attr("height", 0.8)
</script>
</body></html>
在 d3v4 或 5 左右,D3 开始合并轴刻度的偏移量:
Safari often botches shape-rendering: crispEdges, drawing a
two-pixel-wide line even though the stroke-width is one pixel. Also,
in the common case where the coordinates are rounded, using a
half-pixel offset should produce crisp edges even without
shape-rendering.
I think we should use zero-based indexing for coordinates, such that y = 0 means the topmost pixel and x = 0 means the leftmost pixel, and therefore offsetting by +0.5 (not -0.5) in both x and y. (source)
这has caught让人猝不及防
我不确定初始实现,但从 d3v7 开始,您可以使用 axis.offset():
修改偏移量
If offset is specified, sets the offset to the specified value in
pixels and returns the axis. If offset is not specified, returns the
current offset which defaults to 0 on devices with a devicePixelRatio
greater than 1, and 0.5px otherwise. This default offset ensures crisp
edges on low-resolution devices.
所以解决方案最有可能转向d3v7并使用axis.offset(0)
。
我正在使用视图框将数据坐标映射到 SVG 显示。
所有数据似乎都与视图框对齐(我使用红色边框显示视图框边界,使用蓝色边框显示 SVG 边界)但轴线在两个维度上都恰好偏移了 0.5。
有趣的是,刻度正确地锚定在轴应该在的位置(尽管略有偏移),只是偏移了轴线。
我尝试过旧版本的 d3(我从 3 年前将几个版本回退到 5.3.0,但所有版本都有相同的行为)
我可以看到在结果 HTML 输出中轴的 path
规范不正确,但不知道为什么 - 例如对于 y 轴,
<path class="domain" stroke="currentColor" d="M-0.2,0.5H0.5V12.5H-0.2"></path>
真的应该
<path class="domain" stroke="currentColor" d="M-0.2,0.0H0.0V12.5H-0.2"></path>
并且刻度也被 translate
d 错误地增加了 0.5 的额外 y 偏移量(这个不应该被转换或使用 translate(0,0)
):
<g class="tick" opacity="1" transform="translate(0,0.5)"><line stroke="currentColor" x2="-0.2"></line><text fill="currentColor" x="-0.35000000000000003" dy="0.32em">0</text></g>
再现测试用例:
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
fill: none;
}
rect {
fill: #bbb;
stroke: #222;
stroke-width: 0.05;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 0.1;
shape-rendering: crispEdges;
}
.debug {
fill: none;
}
.debug.red {
stroke: #f44;
}
.debug.blue {
stroke: #44f;
}
</style>
</head><body>
<!-- load the d3.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.1/d3.min.js"></script>
<script>
var data = [[0,0], [3,3], [10,10]]
var width = 12
var height = 12
const margin = 1.5
const graph = d3.select("body")
.append("svg")
.attr("viewBox", d => `-${margin} -${margin} ${width+2*margin} ${height+2*margin}`)
.attr("width", 600)
graph.append("rect")
.attr("class", "debug blue")
.attr("x", -margin)
.attr("y", -margin)
.attr("width", width + 2 * margin)
.attr("height", height + 2 * margin)
graph.append("rect")
.attr("class", "debug red")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
const scaleY = d3.scaleLinear().domain([0, height]).range([0, height])
const axisY = d3.axisLeft(scaleY).tickSize(0.2).tickPadding(0.1*margin)
const scaleX = d3.scaleLinear().domain([0, width]).range([0, width])
const axisX = d3.axisBottom(scaleX).tickSize(0.2).tickPadding(0.1*margin)
graph.append("g")
.attr("class", "axis")
.call(axisY)
graph.append("g")
.attr("class", "axis")
.attr("transform", d => `translate(0,${height})`)
.call(axisX)
const plot = graph.selectAll("plot")
.data(data)
.enter()
.append("rect")
.attr("x", d => { console.log(d); return d[0]})
.attr("y", d => d[1])
.attr("width", 0.8)
.attr("height", 0.8)
</script>
</body></html>
在 d3v4 或 5 左右,D3 开始合并轴刻度的偏移量:
Safari often botches shape-rendering: crispEdges, drawing a two-pixel-wide line even though the stroke-width is one pixel. Also, in the common case where the coordinates are rounded, using a half-pixel offset should produce crisp edges even without shape-rendering.
I think we should use zero-based indexing for coordinates, such that y = 0 means the topmost pixel and x = 0 means the leftmost pixel, and therefore offsetting by +0.5 (not -0.5) in both x and y. (source)
这has caught让人猝不及防
我不确定初始实现,但从 d3v7 开始,您可以使用 axis.offset():
修改偏移量If offset is specified, sets the offset to the specified value in pixels and returns the axis. If offset is not specified, returns the current offset which defaults to 0 on devices with a devicePixelRatio greater than 1, and 0.5px otherwise. This default offset ensures crisp edges on low-resolution devices.
所以解决方案最有可能转向d3v7并使用axis.offset(0)
。