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>
I'm using a viewBox to rescale an arbitrary-size SVG so that it fills the browser window (normally the 90% height on the svg is 100%; I've reduced it here for illustrative purposes). Within that SVG is a foreignObject containing some HTML to be rendered.
As it stands, the rendered HTML output is being rescaled to fit the viewBox coordinates as well; notice how the border width and text size for the inside-svg content is different from that outside, and that it changes when the window is resized.
I want the location and size of the foreignObject
to define the bounding box of the internal div, but I don't want it to actually rescale the contents, just to reflow them. Is there a way to do this?
(The text at the top also rescales with the window, but this is desired in that case.)
Note that I can't move or remove the viewBox. I can put the internal div outside of the svg and use JavaScript to size it, but I don't know how to set its bounding box (but not scale) to where it would be if it were inside.
(An unrelated thing that I don't understand is that I have to specify the width/height of the divs as less than 100% or it crops the border, both inside and outside. This might just be a Chrome thing though and isn't really important; I'm just curious.)
Edit After AmeliaBR's answer, this is the code I've come 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>
It seems to behave as expected. (And the reason for the less-than-100% widths was because I needed box-sizing: border-box
-- which I had tried on the html
element's style before asking the question, but it had no effect there; apparently it needs to be applied directly rather than inherited.)
Unfortunately, the answer to your question is "no"; the viewBox changes the entire coordinate system, including the definition of a px or pt unit, for all its children.
The content in <foreignObject>
is rendered to a screen buffer (temporary image) the same width and height in px as the foreignObject's width and height is in the local SVG coordinate system, and then the result is transformed to fit on the screen, the same as if you'd drawn a (JPEG or PNG) image in a transformed SVG coordinate system.
If using Javascript is an option, the best solution to have readable text and a responsive graphic is to have absolutely positioned <div>
elements superimposed over the SVG. This CodePen I put together about tooltips should help get you started on how to convert between the SVG coordinate system and the page coordinate system. In particular, you'll want to read up on the SVGPoint object and the getScreenCTM()
function.
Although more work, this approach will also circumvent the cross-browser problems with foreignObject, which are not supported at all in IE, and which are rather erratic (e.g., not always getting scrollbars you need) in other browsers.
Alternative solution is to position the lower DIV (outside SVG) over the SVG area.
<div style="width:99%;border:4px solid blue; position:relative;top:-100px;">
The scaling of foreignObject
inside SVG is correct. It still doesn't make sense to have DIV inside SVG inside HTML (convoluted way of coding IMHO) when you can simplify it. Or if you want control text size inside SVG, use the <text...>
tags.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With