2

I am working on svg.

<div id="wrap"  style="transform: scale(2); width:300;height:300;">
  <svg id="real" style="border:red 10px solid;" >
    <rect/>
  </svg>
</div>
var e = document.getElementById("real");
//an api to get scale from e directly ??

My question is, how get the scale of this svgElement?

I tried e.getCTM() and e.getScreen(). The matrix does not contain the scale information.

Please give me some advice.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
hjyssg
  • 61
  • 1
  • 4
  • I suggest updating the title to something like "*How to get the transform style inherited from a parent?*", as this is not really specific to SVG. – Jeroen Feb 22 '16 at 06:14
  • But see my answer below, showing that the question _is_ specific to svg. – Andrew Willems Feb 22 '16 at 07:21

2 Answers2

2

Another SO answer shows how to get the scale of an element relative to the document using element.getBoundingClientRect().width / element.offsetWidth. However, the svg element does not have the offsetWidth property. Therefore, one solution is to temporarily prepend a "offsetWidth-able" element to the svg element, e.g. a div, read the ratio on this temporary helper, and then delete it. On the off chance that the inserted element will have an unexpected scale transform applied to it through CSS (as shown in the code snippet), this inserted element should have a style of transform: scale(1); applied to it directly.

Update: A further complication arises when a CSS transform including a scale is applied directly to the svg element itself. In this case, parsing of the style is required which can be accomplished using getComputedStyle and getPropertyValue, and extracting the scale value from the returned matrix string. This then needs to be multiplied by the relative scale of the prepended div.

Further Update: This solution does not appear to currently work in Safari because the browser is not recognizing insertAdjacentHTML in spite of the fact that the documentation I can find says it should. (Is this a Safari bug or am I missing something?) However, the solution does work in Firefox and Chrome.

var e = document.getElementById("real");
var computedTransform = window.getComputedStyle(e).getPropertyValue("transform");
if (computedTransform === "none") {
  eDirScale = 1;
} else {
  var matrix = computedTransform.match(/matrix\(([^\)]*)\)/)[1].split(/, *| +/);
  var scaleX = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
  var scaleY = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]);
  if (scaleX !== scaleY) throw "non-uniform scaling";
  eDirScale = scaleX;
}
var eRelScale = e.getBoundingClientRect().width / e.offsetWidth;
e.insertAdjacentHTML('beforebegin', '<div id="temporaryPrepended" style="transform: scale(1)"></div>');
var prepended = document.getElementById("temporaryPrepended");
var pRelScale = prepended.getBoundingClientRect().width / prepended.offsetWidth;
prepended.parentNode.removeChild(prepended);
var scale = pRelScale * eDirScale;

document.write("<p>scale directly on 'svg': " + eDirScale + "</p>");
document.write("<p>scale on 'svg' relative to document: " + eRelScale + ", i.e. doesn't work in e.g. Firefox and, while it might work in Chrome (in Feb 2016), apparently won't in the near future</p>");
document.write("<p>scale on prepended 'div' relative to document: " + pRelScale + "</p>");
document.write("<p>total scale on 'svg' relative to document: " + scale + "</p>");
div {
  transform: scale(4);
}
<div id="wrap"  style="transform: scale(2); width:300;height:300;">
  <svg id="real" style="transform: translate(10px, 10px) scale(1.5); border:red 10px solid; opacity: 0.3;" >
    <rect/>
  </svg>
</div>

When you run the code snippet above, you may have to scroll down in the snippet window or click on "Full page" to see the results.

Community
  • 1
  • 1
Andrew Willems
  • 11,880
  • 10
  • 53
  • 70
  • Thank you for your answer. But Chrome is going to deprecate offsetWidth from svgElement 2016 April. You can found the warning message in the console. So I am sorry that this code will not work in future Chrome. – hjyssg Feb 23 '16 at 02:33
  • Actually, @hjyssg, I'm trying to show how an alternative is required (which I demonstrate) precisely _because_ you are correct, i.e. `offsetWidth` can't be relied upon to work on an `svgElement` like it can on HTML elements. I had actually tested my original code in Firefox, where it already doesn't work. I didn't know it currently worked in Chrome but won't in the near future (and thank you for that info). I thus updated my reporting message to better show browser differences. In any case, please look again at my solution to see how (I think) it _does_ solve your problem. – Andrew Willems Feb 23 '16 at 03:15
0

This is not possible, or better put the `real element is not being transformed. If it were to inherit the tranformation, it would be scaled twice effectively.

You would be looking for getComputedStyle, but it'll tell you the same. For reference, a property that is inherited is included here:

var e = document.getElementById("real");
console.log(window.getComputedStyle(e)["transform"]);
console.log(window.getComputedStyle(e)["color"]);
<div id="wrap"  style="transform: scale(2); color: blue; width:300; height:300;">
<svg id="real" style="border: red 10px solid;" >
     <rect/>
</svg>
</div>
Jeroen
  • 60,696
  • 40
  • 206
  • 339