D3js:找到路径的边界框(没有 getBBox() )?
D3js: finding path's bounding box (without getBBox() )?
以下代码适用于 Chromium:
var node = window.d3.selectAll('#L1 > *:nth-child(2)');
var bbox = node.node().getBBox();
console.log(bbox) // {height: 44, width: 44, y: -13, x: 144}
但不是 nodejs + jsdom:
"TypeError: Object [ PATH ] has no method 'getBBox' "
米。 Bostock 指出 JSDOM doesn't support getBBox()
使用什么 D3js 替换来获取 #L1 > *:nth-child(2)
的边界框?
过去的努力将我带到了那里:getBBox()
基于 fiddle
路径的边界框
直接挖掘元素的路径数据 d="..."
应该可行。一条 svg 线基本上是一组 x,y
点。 假设绝对坐标没有平移也没有大的贝塞尔曲线,这是我的 D3js 生成的 svg 线的情况,我在这个数据中找到了两者的最小值和最大值 x
和 y
.
为此,我得到了 d="..."
svg 行或多行代码。为了简单起见,我粗鲁地删除了可能的相对跳转,例如 h30
或 v20
,因为我从未在我的 D3js 输出中看到任何跳转,然后清除字母(又名 svg commands
:M、L、H、 V,C,S,Q,T,A,Z),简化空格和跳行,然后用剩余的空格分割。我得到一个干净的坐标数组。
需要注意的重要一点是,我的选择器直接针对 单个非翻译路径。
var getBBox = function(selector){
var xmin, xmax, ymin, ymax,p;
// clean up path
var t = d3.select(selector).attr("d"); // get svg line's code
console.log(t)
t = t.replace(/[a-z].*/g," ") // remove relative coords, could rather tag it for later processing to absolute!
.replace(/[\sA-Z]+/gi," ").trim().split(" "); // remove letters and simplify spaces.
console.log(t)
for(var i in t){ // set valid initial values
if(t[i].length>1){
p = t[i].split(",");
xmin = xmax = p[0]; ymin = ymax = p[1]; }
}
for(var i in t){ // update xmin,xmax,ymin,ymax
p = t[i].split(",");
if(!p[1]){ p[0]=xmin; p[1] = ymin;} // ignore relative jumps such h20 v-10
xmin = Math.min(xmin, p[0]);
xmax = Math.max(xmax, p[0]);
ymin = Math.min(ymin, p[1]);
ymax = Math.max(ymax, p[1]);
} return [[xmin,ymax],[xmax,ymin]]; // [[left, bottom], [right, top]] as for https://github.com/mbostock/d3/wiki/Geo-Paths#bounds
}
var bb = getBBox("path");
组边界框
对于多路径组,您可能需要遍历 svg DOM 以循环组中的每条路径以更新 xmin、ymin、xmax、ymax。
翻译元素
要处理翻译后的元素,请进一步调整。
备选方案
可能存在其他更好的方法。请记住检查 getBBox()
和 getBoundingClientRect()
在您的上下文中是否可用,因为它们是原生的并且非常方便。
getBBox
/getBoundingClientRect
/getClientRect
在NodeJS+JSDOM中不起作用的原因是计算SVG(或HTML)元素的这些值涉及大量的计算。
首先,必须解析 <style>
元素中的所有 CSS 代码(这已经很重要了)。然后必须应用 CSS 选择器、级联和继承规则来了解元素的大小、位置或线宽。甚至在您知道所有样式 属性 值之后,您还需要进行一些 non-trivial 数学运算来计算边界框:不同 SVG 变换函数的定义、这些函数的组合、SVG 基元的边界框和贝塞尔曲线.浏览器支持所有这些(它们必须支持,以便绘制元素),但 JSDOM 并不意味着所有这些。
还好,canvg is a JavaScript implementation of most of SVG, which uses a <canvas>
element to draw the image. It does support most of the above, and although it does not have an interface for giving you those data, fortunately it has very nice (and MIT licensed) code, so hopefully you can copy and reuse parts of it. As of now, the code is written in a single file, and it has CSS parsing, applying cascading rules, path data parsing, definitions of SVG transforms, applying transformations, and bezier curve bounding box calculation. That is, almost everything you need to calculate bounding boxes :) It does not, however, support CSS selectors, but it can reuse another library。但不幸的是,据我所知,canvg 未 准备好用于 NodeJS 中的 运行,您可能需要一些调整。
然而 canvgc, an SVG to JS compiler, which contains an older version of canvg,它 是 在 NodeJS 中 运行 的能力。所以从那开始更容易。
以下代码适用于 Chromium:
var node = window.d3.selectAll('#L1 > *:nth-child(2)');
var bbox = node.node().getBBox();
console.log(bbox) // {height: 44, width: 44, y: -13, x: 144}
但不是 nodejs + jsdom:
"TypeError: Object [ PATH ] has no method 'getBBox' "
米。 Bostock 指出 JSDOM doesn't support getBBox()
使用什么 D3js 替换来获取 #L1 > *:nth-child(2)
的边界框?
过去的努力将我带到了那里:getBBox()
基于 fiddle
路径的边界框
直接挖掘元素的路径数据 d="..."
应该可行。一条 svg 线基本上是一组 x,y
点。 假设绝对坐标没有平移也没有大的贝塞尔曲线,这是我的 D3js 生成的 svg 线的情况,我在这个数据中找到了两者的最小值和最大值 x
和 y
.
为此,我得到了 d="..."
svg 行或多行代码。为了简单起见,我粗鲁地删除了可能的相对跳转,例如 h30
或 v20
,因为我从未在我的 D3js 输出中看到任何跳转,然后清除字母(又名 svg commands
:M、L、H、 V,C,S,Q,T,A,Z),简化空格和跳行,然后用剩余的空格分割。我得到一个干净的坐标数组。
需要注意的重要一点是,我的选择器直接针对 单个非翻译路径。
var getBBox = function(selector){
var xmin, xmax, ymin, ymax,p;
// clean up path
var t = d3.select(selector).attr("d"); // get svg line's code
console.log(t)
t = t.replace(/[a-z].*/g," ") // remove relative coords, could rather tag it for later processing to absolute!
.replace(/[\sA-Z]+/gi," ").trim().split(" "); // remove letters and simplify spaces.
console.log(t)
for(var i in t){ // set valid initial values
if(t[i].length>1){
p = t[i].split(",");
xmin = xmax = p[0]; ymin = ymax = p[1]; }
}
for(var i in t){ // update xmin,xmax,ymin,ymax
p = t[i].split(",");
if(!p[1]){ p[0]=xmin; p[1] = ymin;} // ignore relative jumps such h20 v-10
xmin = Math.min(xmin, p[0]);
xmax = Math.max(xmax, p[0]);
ymin = Math.min(ymin, p[1]);
ymax = Math.max(ymax, p[1]);
} return [[xmin,ymax],[xmax,ymin]]; // [[left, bottom], [right, top]] as for https://github.com/mbostock/d3/wiki/Geo-Paths#bounds
}
var bb = getBBox("path");
组边界框
对于多路径组,您可能需要遍历 svg DOM 以循环组中的每条路径以更新 xmin、ymin、xmax、ymax。
翻译元素
要处理翻译后的元素,请进一步调整。
备选方案
可能存在其他更好的方法。请记住检查 getBBox()
和 getBoundingClientRect()
在您的上下文中是否可用,因为它们是原生的并且非常方便。
getBBox
/getBoundingClientRect
/getClientRect
在NodeJS+JSDOM中不起作用的原因是计算SVG(或HTML)元素的这些值涉及大量的计算。
首先,必须解析 <style>
元素中的所有 CSS 代码(这已经很重要了)。然后必须应用 CSS 选择器、级联和继承规则来了解元素的大小、位置或线宽。甚至在您知道所有样式 属性 值之后,您还需要进行一些 non-trivial 数学运算来计算边界框:不同 SVG 变换函数的定义、这些函数的组合、SVG 基元的边界框和贝塞尔曲线.浏览器支持所有这些(它们必须支持,以便绘制元素),但 JSDOM 并不意味着所有这些。
还好,canvg is a JavaScript implementation of most of SVG, which uses a <canvas>
element to draw the image. It does support most of the above, and although it does not have an interface for giving you those data, fortunately it has very nice (and MIT licensed) code, so hopefully you can copy and reuse parts of it. As of now, the code is written in a single file, and it has CSS parsing, applying cascading rules, path data parsing, definitions of SVG transforms, applying transformations, and bezier curve bounding box calculation. That is, almost everything you need to calculate bounding boxes :) It does not, however, support CSS selectors, but it can reuse another library。但不幸的是,据我所知,canvg 未 准备好用于 NodeJS 中的 运行,您可能需要一些调整。
然而 canvgc, an SVG to JS compiler, which contains an older version of canvg,它 是 在 NodeJS 中 运行 的能力。所以从那开始更容易。