【问题标题】:Convert rendered SVG image to inline SVG without reloading external file?在不重新加载外部文件的情况下将渲染的 SVG 图像转换为内联 SVG?
【发布时间】:2019-08-04 19:27:15
【问题描述】:

herehere 这两个问题相似但又不相同。

假设这样的元素链接到外部 SVG 图像:

<div style="background-image: url("/images/tags/icon/star.svg");"></div>

如果图像已经呈现为背景图像,有哪些选项可以将此 SVG 图像转换为内联 SVG(文档中的其他位置)?

是否只有 this answer 中建议的重新加载外部文件的选项?

目标是尽可能跳过加载 SVG 文件。

【问题讨论】:

  • 图片应该被浏览器缓存。无论您在页面上使用多少次图像。如果标题设置正确,则仅加载一次。占位符的工作原理..
  • @bigless 理解,但这是否意味着唯一的选择是按照链接答案的建议使用 AJAX?
  • 你想改变颜色吗?我评论了你的目标 - 只加载一次文件
  • @bigless 是的,改变颜色所以需要内联 SVG 元素。最好的方法是什么?
  • 不,还有很多其他的解决方案,但是是的,如果最终您希望它成为 DOM 的一部分,那么 ajax 是最简单的。现在,您可能还希望将其 sand-boxed,在这种情况下,您可能希望使用

标签: javascript html svg


【解决方案1】:

还有其他解决方案,但如果您计划在文档中附加 SVG,AJAX 确实是最简单的。
但是,请注意这可能不是一个好主意。
您可能知道,SVG 是文档,它们的节点可以具有id 属性。 id 属性在每个文档中必须是唯一的,因此如果您附加两次相同的 svg 文档,其中包含带有 id 的节点,您将有重复的 id 并且可能会出现问题。

const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
  <defs>
    <rect id="rect1" x="0" y="0" width="50" height="50" fill="red"/>
  </defs>
  <use xlink:href="#rect1" stroke="green"/>
</svg>`;

function appendSVGDoc(markup, parent) {
  const doc = (new DOMParser).parseFromString(markup, 'image/svg+xml');
  const docEl = document.adoptNode(doc.documentElement); parent.appendChild(docEl);
}
// append a first version
appendSVGDoc(svg, document.body);
onclick = e => {
  // let's do some modifs
  document.getElementById('rect1').setAttribute('fill', 'yellow');
  onclick = e => {
    // append an other one
    appendSVGDoc(svg, document.body);
    // guess what color is the rect here?
  };
};
<p>click to modify the svg element.</p>
<p>click again to add a new copy of the original svg</p>

另一个例子?如果您的 svg 文档包含样式表,则此样式表的规则将影响您的整个文档:

const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="200">
  <style>
    :root {
      font: 40px Serif;
    }
    rect {
      fill: green;
    }
  </style>
  <text y="50">I'm a big text in an svg image.</text>
  <rect x="25" y="75" width="50" height="50"/>
</svg>`;

function appendSVGDoc(markup, parent) {
  const doc = (new DOMParser).parseFromString(markup, 'image/svg+xml');
  const docEl = document.adoptNode(doc.documentElement); parent.appendChild(docEl);
}
onclick = e => {
  appendSVGDoc(svg, document.body);
};
<p>I am a simple html text content, and here is a simple svg &lt;rect> that should be red: <svg width="50" height="50"><rect x="0" y="0" height="50" width="50" fill="red"/></svg></p>

<p> click to append the external SVG </p>

所以为了避免这种情况,我只能建议您在

function appendSVGDoc(blob, parent) {
  return new Promise((res, rej) => {
    const url = URL.createObjectURL(blob);
    const iframe = document.createElement('iframe');
    iframe.classList.add('svg-viewer');
    iframe.onload = e => {
      const doc = iframe.contentDocument;
      const docEl = doc.documentElement;
      // iframe doesn't auto size like <object> would
      iframe.width = docEl.getAttribute('width') || '100%';
      iframe.height = docEl.getAttribute('height') || '100%';
      res(doc);
    };
    iframe.onerror = rej;
    iframe.src = url;
    parent.appendChild(iframe);
  });
}

fetch(url)
  .then(resp => resp.blob())
  .then(blob => appendSVGDoc(blob, document.body));
.then(svgdoc => {
    // here manipulate the svg document
  })
  .catch(console.error);

StackSnippets® 过度保护(源自空)阻止我们访问内部 iframe 内容,我不得不将最后一个示例外包给 jsfiddle

【讨论】:

  • 嗨@Kaiido!沙盒是理想的,但 iFrame 方法会产生跨域错误:stackoverflow.com/questions/55192060/…。你有什么建议吗?
  • @Crashalot 这里我没有使用 dataURLs,只是复制这里的代码或小提琴中的代码?
  • 好的,谢谢,没有意识到使用 blob 会有所作为!会试试的,非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
  • 2017-12-20
  • 1970-01-01
  • 1970-01-01
  • 2019-07-18
  • 2011-09-30
  • 2020-05-18
相关资源
最近更新 更多