我的 SVG map 有问题。 我使用 jVectorMap 创建自定义 map ,我需要在字段中心写入每个字段的名称。 示例为:JSFiddle Example (放大右侧查看文字)
我可以用这个函数找到每个字段的中心:
jvm.Map.prototype.getRegionCentroid = function(region){
if(typeof region == "string")
region = this.regions[region.toUpperCase()];
var bbox = region.element.shape.getBBox(),
xcoord = (bbox.x + bbox.width/2),
ycoord = (bbox.y + bbox.height/2);
return [xcoord, ycoord];
};
但我的问题是我想旋转文本以将其与相关字段的顶行对齐。 我尝试过使用 getCTM() 函数,但它总是为每个字段提供相同的值。
如何找到每个字段的正确旋转 Angular ?
谢谢大家!
最佳答案
看起来像squeamish ossifrage在这方面打败了我,他们所说的也正是我的方法......
解决方案
本质上找到每个区域路径中最长的线段,然后调整文本方向以与该线段对齐,同时尝试确保文本不会颠倒(!)
示例
这是一个sample jsfiddle
在 fiddle 的 $(document).ready()
函数中,我向所有区域添加标签,但您会注意到某些区域的质心不在区域或非直边会导致问题 - 稍微修改 map 可能是最简单的解决方法。
说明
以下是我编写的用于演示原理的 3 个函数:
addOrientatedLabel(regionName)
- 向 map 的指定区域添加标签。getAngleInDegreesFromRegion(regionName)
- 获取区域最长边缘的 AngulargetLengthSquared(startPt,endPt)
- 获取线段长度的平方(比获取长度更有效)。
addOrientatedLabel()
使用平移变换将标签放置在质心处,并将文本旋转到与区域中最长线段相同的 Angular 。在 SVG 中,变换从右到左解析,因此:
transform="translate(x,y) rotate(45)"
被解释为先旋转,然后平移。这个顺序很重要!
它还使用 text-anchor="middle"
和 dominant-baseline="middle"
,如 squeamish ossifrage 所解释的那样。如果不这样做将导致文本在其区域内未对齐。
getAngleInDegreesFromRegion()
是完成所有工作的地方。它使用选择器获取区域的 SVG 路径,然后循环路径中的每个点。每当发现一个点是线段的一部分(而不是移动到或其他指令)时,它就会计算线段的平方长度。如果线段的平方长度是迄今为止最长的,则它会存储其详细信息。我使用平方长度,因为这样可以节省执行平方根运算(它仅用于比较目的,因此平方长度就可以了)。
请注意,我将 longestLine
数据初始化为水平数据,这样如果该区域根本没有线段,您至少会获得水平文本。
一旦我们有了最长的线,我就用 Math.atan2 计算它相对于 x 轴的 Angular ,并用 (angle/Math.PI) 将其从弧度转换为 SVG 的度数)* 180
。最后一个技巧是确定该 Angular 是否会将文本上下旋转,如果是,则再旋转 180 度。
注意
我以前没有使用过 SVG,所以我的 SVG 代码可能不是最佳的,但它已经过测试,并且适用于主要由直线段组成的所有区域 - 当然,您需要为生产应用程序添加错误检查!
代码
function addOrientatedLabel(regionName) {
var angleInDegrees = getAngleInDegreesFromRegion(regionName);
var map = $('#world-map').vectorMap('get', 'mapObject');
var coords = map.getRegionCentroid(regionName);
var svg = document.getElementsByTagName('g')[0]; //Get svg element
var newText = document.createElementNS("http://www.w3.org/2000/svg","text");
newText.setAttribute("font-size","4");
newText.setAttribute("text-anchor","middle");
newText.setAttribute("dominant-baseline","middle");
newText.setAttribute('font-family', 'MyriadPro-It');
newText.setAttribute('transform', 'translate(' + coords[0] + ',' + coords[1] + ') rotate(' + angleInDegrees + ')');
var textNode = document.createTextNode(regionName);
newText.appendChild(textNode);
svg.appendChild(newText);
}
这是我在给定 map 区域路径中查找最长线段的方法:
function getAngleInDegreesFromRegion(regionName) {
var svgPath = document.getElementById(regionName);
/* longest edge will default to a horizontal line */
/* (in case the shape is degenerate): */
var longestLine = { startPt: {x:0, y:0}, endPt: {x:100,y:0}, lengthSquared : 0 };
/* loop through all the points looking for the longest line segment: */
for (var i = 0 ; i < svgPath.pathSegList.numberOfItems-1; i++) {
var pt0 = svgPath.pathSegList.getItem(i);
var pt1 = svgPath.pathSegList.getItem(i+1);
if (pt1.pathSegType == SVGPathSeg.PATHSEG_LINETO_ABS) {
var lengthSquared = getLengthSquared(pt0, pt1);
if( lengthSquared > longestLine.lengthSquared ) {
longestLine = { startPt:pt0, endPt:pt1, lengthSquared:lengthSquared};
}
}/* end if dealing with line segment */
}/* end loop through all pts in svg path */
/* determine angle of longest line segement relative to x axis */
var dY = longestLine.startPt.y - longestLine.endPt.y;
var dX = longestLine.startPt.x - longestLine.endPt.x;
var angleInDegrees = ( Math.atan2(dY,dX) / Math.PI * 180.0);
/* if text would be upside down, rotate through 180 degrees: */
if( (angleInDegrees > 90 && angleInDegrees < 270) || (angleInDegrees < -90 && angleInDegrees > -270)) {
angleInDegrees += 180;
angleInDegrees %= 360;
}
return angleInDegrees;
}
请注意,如果路径是使用 PATHSEG_LINETO_ABS
SVG 命令创建的,我的 getAngleInDegreesFromRegion()
方法将仅考虑路径中最长的直线...您将需要更多功能来处理不由直线组成的区域。您可以通过将曲线视为直线来进行近似:
if (pt1.pathSegType != SVGPathSeg.PATHSEG_MOVETO_ABS )
但会出现一些极端情况,因此修改 map 数据可能是最简单的方法。
最后,这是为了完整性而必须采用的平方距离方法:
function getLengthSquared(startPt, endPt ) {
return ((startPt.x - endPt.x) * (startPt.x - endPt.x)) + ((startPt.y - endPt.y) * (startPt.y - endPt.y));
}
希望这足够清楚,可以帮助您入门。
关于javascript - SVG 查找路径的旋转 Angular ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27557040/