D3js:找到路径的边界框(没有getBBox())?

时间:2015-04-20 11:36:49

标签: node.js d3.js jsdom

以下代码适用于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

enter image description here

2 个答案:

答案 0 :(得分:3)

getBBox / getBoundingClientRect / getClientRect在NodeJS + JSDOM中不起作用的原因是计算SVG(或HTML)元素的这些值涉及大量计算。 / p>

首先,必须解析<style>元素中的所有CSS代码(这已经不是一件容易的事了)。然后必须应用CSS选择器,级联和继承规则以了解元素具有的大小,位置或行宽。即使知道了所有样式属性值,也需要做一些非平凡的数学计算边界框:定义不同的SVG变换函数,这些函数的组合,SVG基元的边界框和贝塞尔曲线。浏览器支持所有这些(他们必须,为了绘制元素),但JSDOM根本不适用于所有这些。

但幸运的是, canvg 是大多数SVG的JavaScript实现,它使用<canvas>元素绘制图像。它确实支持上述大部分内容,虽然它没有为您提供这些数据的接口,幸运的是它有非常好的(和MIT许可的)代码,所以希望您可以复制和重用它的一部分。截至目前,代码编写在一个文件中,它包含CSS parsingapplying cascading rulespath data parsingdefinitions of SVG transformsapplying transformations和{{3 }}。也就是说,几乎所有你需要计算边界框的东西:)然而,它并不支持CSS选择器,而是它bezier curve bounding box calculation。但遗憾的是,据我所知,canvg 已准备好在NodeJS中运行,您可能需要进行一些调整。

can reuse another library 是一个SVG到JS编译器,其中包含canvgc 能够在NodeJS中运行。所以从这开始就更容易了。

答案 1 :(得分:2)

路径的边界框

直接挖掘元素的路径数据d="..."应该有效。 svg行基本上是一组x,y点。 假设没有平移的绝对坐标或大贝塞尔曲线,这是我的D3js生​​成的svg行的情况,我在这个数据中找到x和{的最小值和最大值{1}}。

为此,我获得y svg行或多行代码。为了简单起见,我粗略地删除了可能的相对跳跃,例如d="..."h30因为我从未在D3js输出中看到过任何内容,然后清除字母(又名v20:M,L,H, V,C,S,Q,T,A,Z),简化空间和线跳跃,然后由剩余空间分开。我得到一个干净的坐标数组。

重要的是要注意,我的选择器直接定位单个非翻译路径

svg commands

JSfiddle DEMO

组边界框

对于多个路径的组,您可能希望遍历svg DOM以循环组的每个路径,以便更新xmin,ymin,xmax,ymax。

翻译元素

要处理已翻译的元素,请进一步调整。

替代

可能存在其他更好的方法。请务必检查您的上下文中是否有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"); getBBox(),因为它们是原生且非常方便。