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

时间:2018-03-31 04:28:02

标签: javascript image dom svg svg-animate

我无法使用img标记为我导入的svg设置动画。我无法对svg进行硬编码,因为我的项目在使用webpack进行预处理时会生成它。遗憾的是,我似乎无法通过svg标记获取文件,因为我不知道任何" src"或" href"属性。

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

1 个答案:

答案 0 :(得分:2)

这实际上取决于如何你的动画。

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

  • SMIL Animation - 遗憾的是没有得到广泛支持(但你用[svg-animate]标记,所以让我们把它作为第一种情况)。
  • CSS Animation - 随着SVG2的出现,我敢打赌这些会变得越来越普遍。
  • 基于脚本的动画。

<svg>代码中嵌入<img> documentElement时,这些行为如何表现?

<img>中的SMIL动画。

在支持浏览器时,这些浏览器会正常运行,就好像你的SVG没有嵌入一样 您将面临的唯一限制是

  • 您不会收到任何用户手势,因此element.click相同的事件无法正常工作
  • 对于不支持SMIL动画的浏览器(IE ...),您无法回退到基于脚本的动画。

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

&#13;
&#13;
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;
}
&#13;
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
&#13;
<img src="">
<object></object>
<div>Try to click the black rectangle in both the <code>&lt;img></code> and <code>&lt;object></code> tags.
&#13;
&#13;
&#13;

<img>中的CSS动画。

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

&#13;
&#13;
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;
}
&#13;
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
&#13;
<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.
&#13;
&#13;
&#13;

<img>中基于脚本的动画。

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

&#13;
&#13;
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;
}
&#13;
img{
  border: 1px solid green;
}
object{
  border: 1px solid blue;
}
&#13;
<img src="">
<object></object>
&#13;
&#13;
&#13;

所以基本上,<img>中的SVG有很多限制,使用其他容器都会让人不知所措。
现在,每个容器都有自己的限制:

  • <iframe>不会调整其内容大小,默认情况下也会带边框和其他一些丑陋的东西。
  • <object><embed>将在不可见(display: none)时被webkit浏览器卸载,并且不会在任何浏览器中缓存...

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

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

由于我们在这里,<img>中SVG的另一个限制是它无法在自己的标记之外加载任何资源,所有内容都需要直接包含在其中,甚至是字体和光栅图像