是否可以转义SVG视图框?(或者如何在缩放/转换的SVG上定位HTML DIV?)



在本文档中:

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>

我正在使用viewBox来重新缩放任意大小的SVG,以便它填充浏览器窗口(通常SVG上90%的高度是100%;为了便于说明,我在这里缩小了它)。SVG中有一个foreignObject,其中包含一些要呈现的HTML。

目前,渲染的HTML输出也正在重新缩放以适应viewBox坐标;请注意内部svg内容的边框宽度和文本大小与外部的不同,并且在调整窗口大小时会发生变化。

我希望foreignObject的位置和大小可以定义内部div的边界框,但我不希望它实际重新缩放内容,只是为了回流它们。有办法做到这一点吗?

(顶部的文本也会随着窗口重新缩放,但在这种情况下这是需要的。)

请注意,我无法移动或删除viewBox。我可以将内部div放在svg之外,并使用JavaScript对其进行大小调整,但我不知道如何将其边界框(但不知道缩放)设置为它在里面的位置。

(我不明白的一件无关的事情是,我必须将div的宽度/高度指定为小于100%,否则它会在内部和外部裁剪边界。不过,这可能只是Chrome的问题,并不重要;我只是好奇。)


编辑AmeliaBR回答后,这是我想出的代码:

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>

它的行为似乎和预期的一样。(宽度不到100%的原因是我需要box-sizing: border-box——在提出问题之前,我曾在html元素的样式上尝试过,但没有效果;显然,它需要直接应用,而不是继承。)

不幸的是,您的问题的答案是"否";viewBox会更改其所有子项的整个坐标系,包括px或pt单位的定义。

<foreignObject>中的内容被渲染到屏幕缓冲区(临时图像),其宽度和高度(以px为单位)与foreignObject在本地SVG坐标系中的宽度和高度相同,然后将结果转换为适合屏幕,就像在转换后的SVG坐标系绘制(JPEG或PNG)图像一样。

如果可以选择使用Javascript,那么拥有可读文本和响应图形的最佳解决方案是在SVG上叠加绝对定位的<div>元素。我把这个关于工具提示的CodePen放在一起,应该可以帮助您开始了解如何在SVG坐标系和页面坐标系之间转换。特别是,您需要了解SVGPoint对象和getScreenCTM()函数。

尽管做了更多的工作,但这种方法也将避免foreignObject的跨浏览器问题,这些问题在IE中根本不受支持,在其他浏览器中也相当不稳定(例如,并不总是获得所需的滚动条)。

另一种解决方案是将较低的div(在SVG之外)放置在SVG区域上。

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

SVG中foreignObject的缩放是正确的。当你可以简化的时候,在HTML中的SVG中包含DIV(编码IMHO的复杂方式)仍然没有意义。或者,如果你想在SVG中控制文本大小,可以使用<text...>标记。

最新更新