【问题标题】:How can I animate a SVG who is not hard coded?如何为没有硬编码的 SVG 设置动画?
【发布时间】:2018-09-10 01:47:42
【问题描述】:

我无法为使用 img 标签导入的 svg 制作动画。我无法对 svg 进行硬编码,因为我的项目在使用 webpack 进行预处理期间会生成它。可悲的是,我似乎无法通过svg 标签获取我的文件,因为我不知道任何“src”或“href”属性。

如何为非硬编码的 SVG 设置动画?

【问题讨论】:

  • 有很多指南可以帮助您解决此类问题。您需要的最重要的 svg attr 是 translation 并使用 javascript 移动它。
  • 任何有用资源的链接将不胜感激^_^
  • d3 是您的最佳选择。你可以找到它的 youtube 指南。只需查找 d3 转换。我知道 d3 是一个库,但是当您阅读语法时,您会发现语法本质上与 vanilla javascript 相同。

标签: javascript image dom svg svg-animate


【解决方案1】:

这真的取决于你的动画是如何驱动的。


生成动画 SVG 基本上有三种方法:

  • SMIL Animation - 不幸的是, 并没有得到广泛的支持(但是您确实使用 [svg-animate] 进行了标记,所以我们将其作为第一种情况)。
  • CSS Animation - 随着 SVG2 的出现,我敢打赌这些会变得越来越普遍。
  • 基于脚本的动画。

当它们的<svg> documentElement 嵌入到<img> 标记中时,它们的行为如何?


<img> 中的 SMIL 动画。

在支持的浏览器中,它们会正常运行,就好像您的 SVG 没有嵌入一样。
您将面临的唯一限制是

  • 您不会收到任何用户手势,因此 element.click 和类似事件将不起作用
  • 对于不支持 SMIL 动画 (IE...) 的浏览器,您不能回退到基于脚本的动画。

这两个限制都不会影响在 <object><embed><iframe> 中加载的 SVG,因此如果需要,您可以使用它来代替。

var svgStr = `
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
    <rect id="rect" x="-30" y="0" width="30" height="50">
      <!-- this will work normally, even in an <img> -->
      <animate attributeType="XML" attributeName="x" from="-30" to="50"
        begin="0s" dur="10s" repeatCount="indefinite"/>
      <!-- this will not work in <img>, but will in <object>/<iframe>/<embed> -->
      <animate attributeType="XML" attributeName="fill" from="blue" to="red"
        begin="rect.click"
        dur="1s" repeatCount="1"/>
    </rect>
    <!-- js-based workaround won't work in <img> but will in <object>/<iframe>/<embed> -->
    <script src="https://cdn.rawgit.com/FakeSmile/FakeSmile/23c5ceae/smil.user.js"><\/script>
  </svg>`;

loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));

function loadSVG(container) {
  var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
  container.src = container.data = url;
}
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to click the black rectangle in both the <code>&lt;img></code> and <code>&lt;object></code> tags.

&lt;img&gt; 中的 CSS 动画。

就像 SMIL 动画一样,它们应该在支持浏览器的情况下工作,具有相同的用户手势限制和相同的可能方式(使用其他容器):

var svgStr = `
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
    <rect id="rect" x="0" y="0" width="30" height="50"/>
    <defs>
      <style>
        #rect {
          /* this will work normally, even in an img */
          animation: move 10s linear infinite;
        }
        #rect:hover {
          /* this will not work in img, but will in object/iframe/embed */
          animation: move 10s linear infinite, color 1s 1;
        }
        @keyframes move {
          from {
            transform: translate(-30px, 0px);
          }
          to {
            transform: translate(50px, 0px);
          }
        }
        @keyframes color {
          from {
            fill: blue;
          }
          to {
            fill: red;
          }
        }
      </style>
    </defs>
  </svg>`;

loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));

function loadSVG(container) {
  var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
  container.src = container.data = url;
}
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to mouse hover the black rectangle in both the <code>&lt;img></code> and <code>&lt;object></code> tags.

&lt;img&gt; 中基于脚本的动画。

这些根本行不通。嵌入在 &lt;img&gt; 标记中的 SVG 文档无法编写脚本。 要解决此问题,请使用&lt;object&gt;&lt;embed&gt;&lt;iframe&gt; 元素作为容器。

var svgStr = `
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
    <rect id="rect" x="0" y="0" width="30" height="50"/>
    <script type="application/javascript">  
      // will simply never work in img
      var x = 0, rect = document.getElementById('rect');
      function anim() {
        x = (x + 1) % 80;
        rect.setAttribute('x', x - 30);
        requestAnimationFrame(anim);
      }
      anim();
    <\/script>
  </svg>`;

loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));

function loadSVG(container) {
  var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
  container.src = container.data = url;
}
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
<img src="">
<object></object>

所以基本上,&lt;img&gt; 中的 SVG 有很多限制,所有这些都可能被其他容器所淹没。
现在,每个容器都会有自己的限制:

  • &lt;iframe&gt; 不会根据其内容调整大小,它还会默认带有边框和其他一些丑陋的东西。
  • &lt;object&gt;&lt;embed&gt; 在不可见时会被 webkit 浏览器卸载 (display: none),并且不会缓存在任何浏览器中...

当然,也可以通过 AJAX 获取 SVG 标记并将其加载到实际的 HTML 页面中,但我个人不建议这样做:

  • 您需要确保没有重复的 id 元素,
  • 您需要确保所有 CSS 规则都足够具体,不会影响页面中的其他元素,
  • 您必须确保只加载受信任的代码,因为脚本会运行,也就是说您对 XSS 攻击敞开大门。

既然我们在这里,&lt;img&gt; 中 SVG 的另一个限制是它不能加载它自己的标记之外的任何资源,所有东西都需要直接包含在其中,甚至是字体和光栅图像。

【讨论】:

  • 哇,答案真棒!谢谢!您指出的 CSS 方法的问题是您需要对 SVG 进行硬编码。我想我必须选择 SMIL,Erk!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
  • 2018-08-26
  • 2017-09-29
  • 2021-05-09
相关资源
最近更新 更多