我正在尝试使用JavaScript在不同的svg元素之间画线。 svg元素位于预先加载的svg图像(使用inkScape创建)中,该图像已内联加载。
为了绘制线条,我开始迷恋于找到现有svg元素相对于svg文档本身的正确坐标。
例如,请参见以下片段。 我正在尝试实现FindSVGPosition函数。
$(function() {
$("[id^=sys_]").addClass("sysGroup");
$(".sysGroup").click(function(e) {
var sysElement = $(this);
var id = $(sysElement).attr("id");
var sysName = id.substr(4);
var metaNameId = "meta_" + sysName;
var metaElmnt = $("#" + metaNameId);
if (metaElmnt != undefined) {
//showCenter('system1');
if ($(metaElmnt).is(":visible")) {
$(metaElmnt).hide("fast");
} else {
var position = findDOMPosition(sysName);
$(metaElmnt).css(
"top",
window.scrollY + position.height + position.y + 2
);
$(metaElmnt).css("left", window.scrollX + position.x);
$(metaElmnt).show("fast");
}
}
});
function drawPath(fromSystemName, toSystemName) {
var pathBetween = findPathBetween(fromSystemName, toSystemName);
var newLine = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
newLine.setAttribute(
"id",
pathBetween.fromSystemName + "_" + pathBetween.toSystemName
);
newLine.setAttribute("x1", pathBetween.fromX);
newLine.setAttribute("y1", pathBetween.fromY);
newLine.setAttribute("x2", pathBetween.toX);
newLine.setAttribute("y2", pathBetween.toY);
newLine.setAttribute("stroke", "red");
$("#svg8").append(newLine);
return pathBetween;
}
function findPathBetween(fromSystemName, toSystemName) {
var fromPosition = findSVGPosition(fromSystemName);
var toPosition = findSVGPosition(toSystemName);
if (fromPosition == null || toPosition == null) return null;
//center to center
var response = {
fromSystem: fromSystemName,
fromX: fromPosition.x + fromPosition.width / 2,
fromY: fromPosition.top + fromPosition.height / 2,
fromSide: "center",
toSystem: toSystemName,
toX: toPosition.x + toPosition.width / 2,
toY: toPosition.top + toPosition.height / 2,
toSide: "center"
};
return response;
}
function findSVGPosition(systemName) {
//d.x, d.y, d.top, d.right, d.bottom, d.height
}
function findDOMPosition(systemName) {
//d.x, d.y, d.top, d.right, d.bottom, d.height
var systemElement = $("#sys_" + systemName);
if (systemElement == null) return null;
return $(systemElement)
.get(0)
.getBoundingClientRect();
}
});
#systemDiagram{
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.meta{
width: 350px;
font-size: smaller;
}
p, ul{
margin-top: 0px;
}
.metaHeader{
font-size: large;
}
text:hover{
cursor: default;
}
.sysGroup:hover{
cursor: pointer;
}
.sysGroup text:hover{
cursor: pointer;
}
#info-box, .meta {
display: none;
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
background-color: #eaeded;
border: 2px solid #239b56;
border-radius: 5px;
padding: 5px;
font-family: arial;
}
<script src="https://code.jquery.com/jquery-2.2.4.min.js">
</script>
<div>
<p>lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum. lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum. lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum.</p>
<p>lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum. lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum. lorem ipsum, lorem ipsum, lorem ipsum - lorem other ipsum.</p>
</div>
<div id="info-box"></div>
<div class="meta" id="meta_system1">
<div><span class="metaHeader">Description</span></div>
<p>description</p>
</div>
<div class="meta" id="meta_system2">
<div>Something here for system2</div>
</div>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14in"
height="8.5in"
viewBox="0 0 355.6 215.9"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="drawing.02.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.0404412"
inkscape:cx="485.6749"
inkscape:cy="543.80097"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="in" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="systemGroups" />
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="ancillary"
style="display:none"
sodipodi:insensitive="true">
<rect
style="display:inline;opacity:0.4;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.23668619"
id="rect175"
width="115.25188"
height="27.459745"
x="127.27588"
y="14.725033" />
</g>
<g
inkscape:label="systems"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-81.1)"
style="display:inline">
<g
id="sys_system1"
transform="translate(15.595292,0.08990834)"
inkscape:label="#g164">
<rect
style="fill:#000080;fill-opacity:1;stroke-width:0.1389997"
y="102.51232"
x="48.695206"
height="12.662203"
width="23.878363"
id="rect47"
inkscape:label="#rect4524-9" />
<text
id="text51"
y="110.01817"
x="51.767498"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#ffffff;fill-opacity:1;stroke-width:0.26458332"
y="110.01817"
x="51.767498"
id="tspan49"
sodipodi:role="line">System1</tspan></text>
</g>
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect95"
width="177.46696"
height="27.528765"
x="10.156241"
y="95.21151" />
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect97"
width="133.36749"
height="39.555889"
x="19.777945"
y="94.142426" />
<g
id="sys_system2"
inkscape:label="#g90"
transform="translate(-5.0449918)">
<rect
style="fill:#aa8800;fill-opacity:1;stroke-width:0.13352577"
y="139.75128"
x="70.257324"
height="12.662203"
width="22.034693"
id="rect47-1"
inkscape:label="#rect4524-9" />
<text
id="text51-2"
y="147.25714"
x="73.32962"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#000000;fill-opacity:1;stroke-width:0.26458332"
y="147.25714"
x="73.32962"
id="tspan49-8"
sodipodi:role="line">System2</tspan></text>
</g>
</g>
</svg>
我能够找到svg元素相对于DOM的坐标,并在这些元素下正确放置div。
我能够在任意起点和终点使用javascript在svg上画一条线。
我无法访问相对于svg图像的现有svg元素的svg属性(x,y,高度,宽度)。
我可以找到的大多数示例都使用svg元素的contentDocument。但就我而言,无论我尝试了什么-contentDocument为null。
由于我不会在页面加载时触发此操作-我有信心在代码运行时svg应该完全加载到dom中。
似乎不需要任何库就可以轻松实现,但是我一直在试图弄清楚它太久了,真的可以使用一些帮助。
答案 0 :(得分:3)
首先,由于SVG中元素上的所有transform
属性,您正在为自己做麻烦。我的第一个建议是摆脱它们。
在Inkscape中执行此操作的最简单方法是取消组合,然后重新组合对象。
如果这样做,您的功能很简单:
function findSVGPosition(systemName) {
return document.getElementById(systemName).getBBox();
}
getBBox()
函数返回SVG元素的边界框。但是边界框不考虑元素或其父元素的变换。如果存在变换,则边界将不会有用。这就是为什么我们在步骤1中摆脱它们的原因。
接下来,修复findPathBetween()
中的错字:.top
应该.y
。
最后,您需要调用drawPath()
函数。
drawPath('sys_system1', 'sys_system2');
但是该行位于框的顶部。因此,您可能需要执行.prepend()
而不是.append()
。因此,该行位于框的前面-下方。
$("#svg8").prepend(newLine);
然后,您最终得到这个。
function drawPath(fromSystemName, toSystemName) {
var pathBetween = findPathBetween(fromSystemName, toSystemName);
var newLine = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
newLine.setAttribute(
"id",
pathBetween.fromSystemName + "_" + pathBetween.toSystemName
);
newLine.setAttribute("x1", pathBetween.fromX);
newLine.setAttribute("y1", pathBetween.fromY);
newLine.setAttribute("x2", pathBetween.toX);
newLine.setAttribute("y2", pathBetween.toY);
newLine.setAttribute("stroke", "red");
$("#svg8").prepend(newLine);
return pathBetween;
}
function findPathBetween(fromSystemName, toSystemName) {
var fromPosition = findSVGPosition(fromSystemName);
var toPosition = findSVGPosition(toSystemName);
if (fromPosition == null || toPosition == null) return null;
//center to center
var response = {
fromSystem: fromSystemName,
fromX: fromPosition.x + fromPosition.width / 2,
fromY: fromPosition.y + fromPosition.height / 2,
fromSide: "center",
toSystem: toSystemName,
toX: toPosition.x + toPosition.width / 2,
toY: toPosition.y + toPosition.height / 2,
toSide: "center"
};
return response;
}
function findSVGPosition(systemName) {
return document.getElementById(systemName).getBBox();
}
drawPath('sys_system1', 'sys_system2');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14in"
height="8.5in"
viewBox="0 0 355.6 215.9"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="line.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.0404412"
inkscape:cx="166.09894"
inkscape:cy="543.80097"
inkscape:document-units="mm"
inkscape:current-layer="svg8"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1378"
inkscape:window-x="1592"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="in" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="systemGroups"
style="display:inline" />
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="ancillary"
style="display:none"
sodipodi:insensitive="true">
<rect
style="display:inline;opacity:0.4;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.23668619"
id="rect175"
width="115.25188"
height="27.459745"
x="127.27588"
y="14.725033" />
</g>
<g
id="g864"
inkscape:label="systems">
<g
id="sys_system1">
<rect
style="fill:#000080;fill-opacity:1;stroke-width:0.1389997"
y="21.502226"
x="64.290497"
height="12.662203"
width="23.878363"
id="rect47"
inkscape:label="#rect4524-9" />
<text
id="text51"
y="29.008078"
x="67.362793"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#ffffff;fill-opacity:1;stroke-width:0.26458332"
y="29.008078"
x="67.362793"
id="tspan49"
sodipodi:role="line">System1</tspan></text>
</g>
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect95"
width="177.46696"
height="27.528765"
x="10.156241"
y="14.111509" />
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect97"
width="133.36749"
height="39.555889"
x="19.777945"
y="13.042425" />
<g
id="sys_system2">
<rect
style="fill:#aa8800;fill-opacity:1;stroke-width:0.13352577"
y="58.651283"
x="65.212334"
height="12.662203"
width="22.034693"
id="rect47-1"
inkscape:label="#rect4524-9" />
<text
id="text51-2"
y="66.157143"
x="68.28463"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#000000;fill-opacity:1;stroke-width:0.26458332"
y="66.157143"
x="68.28463"
id="tspan49-8"
sodipodi:role="line">System2</tspan></text>
</g>
</g>
</svg>
这是一个更好的版本,不需要您删除Inkscape中的所有变换。
在此版本中,我们选择行端点,然后使用getCTM()
查找从元素到页面的总变换。
稍微复杂一点是getCTM()
包含所有转换。包括由viewBox
缩放引起的变换。但是我们不希望转换包含最后一步。因此,要解决此问题,我们要做的是为根getCTM
元素调用<svg>
,然后使用它来取消转换的viewBox
部分。
function drawPath(fromSystemName, toSystemName) {
var pathBetween = findPathBetween(fromSystemName, toSystemName);
var newLine = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
newLine.setAttribute(
"id",
pathBetween.fromSystem + "_" + pathBetween.toSystem
);
newLine.setAttribute("x1", pathBetween.fromX);
newLine.setAttribute("y1", pathBetween.fromY);
newLine.setAttribute("x2", pathBetween.toX);
newLine.setAttribute("y2", pathBetween.toY);
newLine.setAttribute("stroke", "red");
$("#svg8").prepend(newLine);
return pathBetween;
}
function findPathBetween(fromSystemName, toSystemName) {
var fromPosition = findSVGPosition(fromSystemName);
var toPosition = findSVGPosition(toSystemName);
if (fromPosition == null || toPosition == null) return null;
//center to center
var response = {
fromSystem: fromSystemName,
fromX: fromPosition.x,
fromY: fromPosition.y,
fromSide: "center",
toSystem: toSystemName,
toX: toPosition.x,
toY: toPosition.y,
toSide: "center"
};
return response;
}
function findSVGPosition(systemName) {
var elem = document.getElementById(systemName);
var bbox = elem.getBBox();
var point = elem.ownerSVGElement.createSVGPoint();
point.x = bbox.x + bbox.width / 2;
point.y = bbox.y + bbox.height / 2;
var elemToViewportTransform = elem.getCTM(); // includes SVG scaling
var svgToViewportTransform = getViewportTransform(elem);
return point.matrixTransform(elemToViewportTransform) // apply elem transform
.matrixTransform(svgToViewportTransform.inverse()); // but take off the svg scaling
}
function getViewportTransform(elem) {
if (elem.ownerSVGElement.getCTM) {
var result = elem.ownerSVGElement.getCTM()
if (result != null)
return result;
}
// Workaround for Firefox and other browsers that don't support transform
// on the `<svg> element yet (it's an SVG2 thing).
var svg = elem.ownerSVGElement;
var g = document.createElementNS(svg.namespaceURI, "g");
svg.appendChild(g);
var result = g.getCTM();
svg.removeChild(g);
return result;
}
drawPath('sys_system1', 'sys_system2');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14in"
height="8.5in"
viewBox="0 0 355.6 215.9"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="drawing.02.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.0404412"
inkscape:cx="485.6749"
inkscape:cy="543.80097"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="in" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="systemGroups" />
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="ancillary"
style="display:none"
sodipodi:insensitive="true">
<rect
style="display:inline;opacity:0.4;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.23668619"
id="rect175"
width="115.25188"
height="27.459745"
x="127.27588"
y="14.725033" />
</g>
<g
inkscape:label="systems"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-81.1)"
style="display:inline">
<g
id="sys_system1"
transform="translate(15.595292,0.08990834)"
inkscape:label="#g164">
<rect
style="fill:#000080;fill-opacity:1;stroke-width:0.1389997"
y="102.51232"
x="48.695206"
height="12.662203"
width="23.878363"
id="rect47"
inkscape:label="#rect4524-9" />
<text
id="text51"
y="110.01817"
x="51.767498"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#ffffff;fill-opacity:1;stroke-width:0.26458332"
y="110.01817"
x="51.767498"
id="tspan49"
sodipodi:role="line">System1</tspan></text>
</g>
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect95"
width="177.46696"
height="27.528765"
x="10.156241"
y="95.21151" />
<rect
style="fill:none;fill-opacity:1;stroke-width:0.26458332"
id="rect97"
width="133.36749"
height="39.555889"
x="19.777945"
y="94.142426" />
<g
id="sys_system2"
inkscape:label="#g90"
transform="translate(-5.0449918)">
<rect
style="fill:#aa8800;fill-opacity:1;stroke-width:0.13352577"
y="139.75128"
x="70.257324"
height="12.662203"
width="22.034693"
id="rect47-1"
inkscape:label="#rect4524-9" />
<text
id="text51-2"
y="147.25714"
x="73.32962"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#000000;fill-opacity:1;stroke-width:0.26458332"
y="147.25714"
x="73.32962"
id="tspan49-8"
sodipodi:role="line">System2</tspan></text>
</g>
</g>
</svg>