Is it possible to avoid SVG viewing? (or how to position the HTML DIV over the scaled / transformed SVG?)

In this document:

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden
}
#content {
  width: 100%;
  height: 100%
}
      

<div id="content">
  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="90%" viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet">
    <rect style="fill:none;stroke:black" width="540" height="157" x="690" y="26"></rect>
    <text style="font-size:148px;text-align:center;text-anchor:middle;fill:black;stroke:none" x="960" y="145">Test</text>
    <text style="font-size:112px;text-align:center;text-anchor:middle;fill:black;stroke:none" x="960" y="340">Lorem ipsum etc etc</text>
    <foreignObject x="10" y="726" width="1901" height="347">
      <div xmlns="http://www.w3.org/1999/xhtml" style="width:99.6%;height:97.7%;border:4px solid blue">
        <p style="text-align:center">Hello World, from HTML inside SVG.</p>
      </div>
    </foreignObject>
  </svg>
  <div style="width:99%;border:4px solid blue">
    <p style="text-align:center">Hello World, from HTML outside SVG.</p>
  </div>
</div>
      

Run codeHide result


I am using a viewBox to scale the SVG to an arbitrary size so that it fills the browser window (normally 90% height on svg is 100%, I scaled it down here for illustrative purposes). Inside this SVG, there is a foreignObject containing some HTML to be rendered.

As it stands, the rendered HTML output is rescaled to match the coordinates of the viewBox; notice how the border width and text size for the in-svg content differ from the outside, and that it changes when the window is resized.

I want the location and size to foreignObject

dictate the bounding rectangle of the inner div, but I don't want it to actually rewrite the content, just to pay for them. Is there a way to do this?

(The text at the top will also rescan with the window, but that's desirable in this case.)

Please note that I cannot move or delete the viewBox. I can put the inner div outside the svg and use JavaScript to size it, but I don't know how to set its bounding box (but not scale) where it would be if it were inside.

(The timid thing that I don't understand is that I have to specify the width / height of the divs as less than 100% or it cuts the border, both inside and out. This could only be a Chrome thing, although it doesn't really matter , I'm just curious.)


Edit Following AmeliaBR's answer , this is the code I came up with:

function svgTransform(x, y, matrix, svg) {
  var p = svg.createSVGPoint();
  p.x = x;
  p.y = y;
  return p.matrixTransform(matrix);
}

function svgScreenBounds(svgElement) {
  var matrix = svgElement.getScreenCTM();
  var r = svgElement.getBBox();
  var leftTop = svgTransform(r.x, r.y, matrix, svgElement.ownerSVGElement);
  var rightBottom = svgTransform(r.x + r.width, r.y + r.height, matrix, svgElement.ownerSVGElement);
  return {
    x: leftTop.x,
    y: leftTop.y,
    width: rightBottom.x - leftTop.x,
    height: rightBottom.y - leftTop.y
  };
}

function adjustOverlay() {
  var placeholder = document.getElementById('placeholder');
  var overlay = document.getElementById('overlay');
  var bounds = svgScreenBounds(placeholder);
  overlay.style.left = bounds.x + 'px';
  overlay.style.top = bounds.y + 'px';
  overlay.style.width = bounds.width + 'px';
  overlay.style.height = bounds.height + 'px';
  overlay.style.display = 'block';
}
      

html,body { height: 100%; margin: 0; padding: 0; overflow: hidden }
#content { width: 100%; height: 100% }
#overlay {
  display: none;
  position: absolute;
  border:4px solid blue;
  box-sizing: border-box;
}
      

<body onload="adjustOverlay()" onresize="adjustOverlay()">
  <div id="content">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet">
      <rect style="fill:none;stroke:black" width="540" height="157" x="690" y="26"></rect>
      <text style="font-size:148px;text-align:center;text-anchor:middle;fill:black;stroke:none" x="960" y="145">Test</text>
      <text style="font-size:112px;text-align:center;text-anchor:middle;fill:black;stroke:none" x="960" y="340">Lorem ipsum etc etc</text>
      <rect style="fill:yellow;stroke:black" width="1901" height="100" x="10" y="618" rx="20" ry="20"></rect>
      <text style="font-size:64px;text-align:center;text-anchor:middle;fill:black;stroke:none" x="960" y="685">Bottom banner text</text>
      <rect style="visibility:hidden" x="10" y="726" width="1901" height="347" id="placeholder"></rect>
    </svg>
  </div>
  <div id="overlay">
    <p style="text-align:center">Hello World, from HTML outside SVG.</p>
  </div>
</body>
      

Run codeHide result


It seems to behave as expected. (And the reason for the width less than 100% was that I needed it box-sizing: border-box

- which I tried on element styling html

before asking the question, but it had no effect, apparently it should be applied directly, not inherited.)

+3


source to share


2 answers


Unfortunately, the answer to your question is no; The viewBox changes the entire coordinate system, including the px or pt unit definition, for all of its children.

The content in is <foreignObject>

rendered in a screen buffer (temporary image) of the same width and height in px, since the foreignObject's width and height are in the local SVG coordinate system, and then the result is converted to fit the screen, as if you had drawn the image (JPEG or PNG) in the transformed SVG coordinate system.



If using Javascript is an option, the best solution for text reading and responsive graphics is to have absolutely positioned elements <div>

overlaid on top of the SVG. This CodePen I've put together around the tips should get you started with how to transform SVG coordinate system and page coordinate system. In particular, you will want to read the SVGPoint object and getScreenCTM()

.

Despite a lot of work, this approach will also work around cross-browser foreignObject issues, which are not supported at all in IE, and which are quite erratic (for example, not always getting the scrollbars they want) in other browsers.

+2


source


An alternative solution is to place the bottom DIV (outside the SVG) in the SVG area.

<div style="width:99%;border:4px solid blue; position:relative;top:-100px;">



Scaling foreignObject

inside SVG is correct. It still doesn't make sense to have a DIV inside SVG inside HTML (confusing way of coding IMHO) when you can simplify it. Or, if you want to control the size of the text inside the SVG, use tags <text...>

.

+1


source







All Articles