I have such SVG:
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="630"
height="430"
>
<rect
rx="0"
ry="0"
width="100%"
height="100%"
fill="rgba(92, 95, 108, 1.0)"
/>
<line x1="0" y1="176" x2="630" y2="176" style="stroke:rgb(255,0,0);stroke-width:2" />
<g
transform="scale(0.2) translate(1300.370746612549 818.9994964599609)"
style="outline: black solid 2px;"
>
<path
fill="rgba(198, 199, 205, 1.0)"
d="M414.578,62.68L93.932,122.853c-22.91,4.448-33.326,15.876-33.326,35.457v221.359 c0,19.581,6.1,39.759,32.654,35.457l154.561-29.073l-9.639,63.268l110.875-82.335l65.695-12.019 c23.564-4.135,36.643-15.875,36.643-35.457V98.137C451.395,78.556,434.16,62.68,414.578,62.68z M354.711,235.385 c-5.002,53.956-55.426,97.697-112.623,97.697c-57.203,0-99.508-43.741-94.506-97.697c5.004-53.969,55.428-97.711,112.623-97.711 C317.408,137.674,359.721,181.416,354.711,235.385z M215.049,282.888c-3.109-20.511-6.363-56.128-9.063-76.632 c-1.416-10.769,4.857-14.876,15.098-12.905c28.838,5.537,82.398,16.59,82.398,16.59c11.623,1.416,17.576,11.9,6.578,20.643 c-19.873,15.793-56.912,48.904-78.846,61.547C224.207,296.168,216.229,290.638,215.049,282.888z"
/>
</g>
</svg>
As you can see, there is a group element with a scale of 0.2. Also, this group is "touching" the line. This is fine. However, after changing the scale to 0.25, the group element suddenly moves into different location.
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="630"
height="430"
>
<rect
rx="0"
ry="0"
width="100%"
height="100%"
fill="rgba(92, 95, 108, 1.0)"
/>
<line x1="0" y1="176" x2="630" y2="176" style="stroke:rgb(255,0,0);stroke-width:2" />
<g
transform="scale(0.25) translate(1300.370746612549 818.9994964599609)"
style="outline: black solid 2px;"
>
<path
fill="rgba(198, 199, 205, 1.0)"
d="M414.578,62.68L93.932,122.853c-22.91,4.448-33.326,15.876-33.326,35.457v221.359 c0,19.581,6.1,39.759,32.654,35.457l154.561-29.073l-9.639,63.268l110.875-82.335l65.695-12.019 c23.564-4.135,36.643-15.875,36.643-35.457V98.137C451.395,78.556,434.16,62.68,414.578,62.68z M354.711,235.385 c-5.002,53.956-55.426,97.697-112.623,97.697c-57.203,0-99.508-43.741-94.506-97.697c5.004-53.969,55.428-97.711,112.623-97.711 C317.408,137.674,359.721,181.416,354.711,235.385z M215.049,282.888c-3.109-20.511-6.363-56.128-9.063-76.632 c-1.416-10.769,4.857-14.876,15.098-12.905c28.838,5.537,82.398,16.59,82.398,16.59c11.623,1.416,17.576,11.9,6.578,20.643 c-19.873,15.793-56.912,48.904-78.846,61.547C224.207,296.168,216.229,290.638,215.049,282.888z"
/>
</g>
</svg>
My question is: how to update x and y coordinates so that the group is still touching the line?
This is my solution: I'm putting the path inside the g element and I'm scaling the path. I'm using theg.getBBox() values to change the x and y of the <use> element.
let bb = testg.getBBox()
let x = 315 - bb.x - bb.width/2;
let y = 176 - bb.y;
theUse.setAttributeNS(null,"x", x);
theUse.setAttributeNS(null,"y", y);
theRange.addEventListener("input", ()=>{
thePath.setAttributeNS(null, "transform", `scale(${parseInt(theRange.value)/100})`)
let bb = testg.getBBox()
let x = 315 - bb.x - bb.width/2;
let y = 176 - bb.y;
theUse.setAttributeNS(null,"x", x);
theUse.setAttributeNS(null,"y", y);
})
<input id="theRange" type="range" value ="25" /><svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="630"
height="430"
>
<defs>
<g id="testg">
<path id="thePath" transform="scale(.45)"
fill="rgba(198, 199, 205, 1.0)"
d="M414.578,62.68L93.932,122.853c-22.91,4.448-33.326,15.876-33.326,35.457v221.359 c0,19.581,6.1,39.759,32.654,35.457l154.561-29.073l-9.639,63.268l110.875-82.335l65.695-12.019 c23.564-4.135,36.643-15.875,36.643-35.457V98.137C451.395,78.556,434.16,62.68,414.578,62.68z M354.711,235.385 c-5.002,53.956-55.426,97.697-112.623,97.697c-57.203,0-99.508-43.741-94.506-97.697c5.004-53.969,55.428-97.711,112.623-97.711 C317.408,137.674,359.721,181.416,354.711,235.385z M215.049,282.888c-3.109-20.511-6.363-56.128-9.063-76.632 c-1.416-10.769,4.857-14.876,15.098-12.905c28.838,5.537,82.398,16.59,82.398,16.59c11.623,1.416,17.576,11.9,6.578,20.643 c-19.873,15.793-56.912,48.904-78.846,61.547C224.207,296.168,216.229,290.638,215.049,282.888z"
/></g>
</defs>
<rect
rx="0"
ry="0"
width="100%"
height="100%"
fill="rgba(92, 95, 108, 1.0)"
/>
<line x1="0" y1="176" x2="630" y2="176" style="stroke:rgb(255,0,0);stroke-width:2" />
<line x1="315" y1="0" x2="315" y2="100%" style="stroke:rgb(255,0,0);stroke-width:2" />
<use id="theUse" xlink:href="#testg" y="161" />
</svg>
temporarily remove the transform attribute completely
find out the upper left corner of the path element
move the path such that its upper left corner sits at (0, 0)
define a transform attribute that translates the group to the place where you want the upper left corner to sit permanently
to the right in the transform attribute, define the scale of your choice.
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="630"
height="430"
>
<rect
rx="0"
ry="0"
width="100%"
height="100%"
fill="rgba(92, 95, 108, 1.0)"
/>
<line x1="0" y1="176" x2="630" y2="176" style="stroke:rgb(255,0,0);stroke-width:2" />
<g
transform="translate(260 176) scale(0.25)"
style="outline: black solid 2px;"
>
<path transform="translate(-60.6, -62.7)"
fill="rgba(198, 199, 205, 1.0)"
d="M414.578,62.68L93.932,122.853c-22.91,4.448-33.326,15.876-33.326,35.457v221.359 c0,19.581,6.1,39.759,32.654,35.457l154.561-29.073l-9.639,63.268l110.875-82.335l65.695-12.019 c23.564-4.135,36.643-15.875,36.643-35.457V98.137C451.395,78.556,434.16,62.68,414.578,62.68z M354.711,235.385 c-5.002,53.956-55.426,97.697-112.623,97.697c-57.203,0-99.508-43.741-94.506-97.697c5.004-53.969,55.428-97.711,112.623-97.711 C317.408,137.674,359.721,181.416,354.711,235.385z M215.049,282.888c-3.109-20.511-6.363-56.128-9.063-76.632 c-1.416-10.769,4.857-14.876,15.098-12.905c28.838,5.537,82.398,16.59,82.398,16.59c11.623,1.416,17.576,11.9,6.578,20.643 c-19.873,15.793-56.912,48.904-78.846,61.547C224.207,296.168,216.229,290.638,215.049,282.888z"
/>
</g>
</svg>
So, how do you find out the upper left corner of the path element? Load it into a browser and, on the Javascript command line, execute
document.querySelector('path').getBBox()
I have the following html and get strange results with getBoundingClientRect, if there are svg elements inside:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<svg>
<g transform="translate(10,10) scale(1)">
<g class="nodes">
<g style="inline" transform="translate(20,20)">
<rect style="stroke: rgb(170, 170, 170); stroke-width: 1; fill: rgb(248, 248, 248);" width="100" height="90"></rect>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(182, 204, 216); stroke-width: 0;" x="0" y="0" height="20" width="100"></rect>
<text class="nodetext" x="3" y="15">Text 1</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 172); stroke-width: 0;" x="0" y="22" height="20" width="100"></rect>
<text class="nodetext" x="3" y="37">Test 2</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 180); stroke-width: 0;" x="0" y="44" height="20" width="100"></rect>
<text class="nodetext" x="3" y="59">Test 3</text>
</g>
<g class="nodebox">
<rect class="noderect" style="fill: rgb(236, 163, 154); stroke: rgb(212, 139, 130); stroke-width: 2;" x="0" y="66" height="20" width="100"></rect>
<text class="nodetext" x="3" y="81">Test 4</text>
<g class="nodeicon">
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" x="80" y="68">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</svg>
</g>
</g>
</g>
</g>
</g>
</svg>
</body>
</html>
I get a much greater rectangle than I would expect in Firefox.
When I inspect the objects, the displayed bounding box for the inner svg element is fine, but the surrounding g element (class nodeicon) is outside.
If I remove this g element, the next surrounding g element is outside.
The following picture shows this:
It looks like the offset of the svg is applied twice.
Is getBoundingClientRect the correct way to get the position and size of elements (e.g. the g element with class nodes) for this? Is there something wrong with the HTML or svg element or did I run into a Firefox bug?
I am using the current version of Firefox (58.0 64bit).
A problem that you have here is that the svg tag nested inside the g tag (.nodeicon) is starting a new viewport context. Strictly speaking it should not be nested inside a g tag anyway, but regardless, it isn't really necessary as you're using it as a method of grouping the two elements inside it - which is the purpose of the g tag.
Try removing the svg tag nested inside .nodeicon, and move the coordinates in that svg's x and y attributes to a transform attribute on the g tag.
i.e:
<g class="nodeicon" transform="translate(80, 68)">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</g>
I have this HTML code, which is invoking my javascript code. The code is for a gauge. In the javascript code, I am trying to access a SVG file, and modifying the needle (of the gauge) to display the desired value. The code is working fine. However, I do not wish to call "object id" in HTML. I want to access SVG file through javascript directly, instead of using object id in HTML. I tried using el.setAttribute('data', 'gauge.svg'); But then svg_doc isn't able to retrieve the SVG image and modify the needle. Any help would be highly appreciated.
PS : I tried my best to be as thorough in explaining the problem. However, please let me know if I am unclear somewhere.
This is Gauge.png image which is embedded in the svg code I have pasted below https://sphotos-b.xx.fbcdn.net/hphotos-snc6/179594_10150982737360698_1827200234_n.jpg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
</svg>
HTML+Javascript code
<head>
<title>SVG Gauge example</title>
<script>
function update1(){
var scale=100;
var value;
var value1 = 69;
var el=document.getElementById('gauge1');
if (!el) return;
/* Get SVG document from HTML element */
var svg_doc = el.contentDocument;
if (!svg_doc) return;
/* Rotate needle to display given value */
var needle_el = svg_doc.getElementById('needle');
if (!needle_el) return;
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
}
document.addEventListener('load', update1, true);
</script>
</head>
<div>
<object id="gauge1" type="image/svg+xml" data="gauge.svg" width="127" height="122"/>
</div>
</html>
As robertc already mentioned, you can embed the javascript code into your SVG file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
<script type="text/javascript">
var scale=100;
var value;
var value1 = 69;
/* Rotate needle to display given value */
var needle_el = document.getElementById('needle');
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
</script>
</svg>
I've put the code below the actual SVG contents so that the document is already loaded when the script is executed.
Then, you can view the SVG file directly e.g. in Firefox (I've tested it right now).