将animateTransform SVG的转换转化为动态.gif图像。

huangapple go评论67阅读模式
英文:

Convert animateTransform SVG's transformation into animated .gif image

问题

Is it possible to convert SVG transformation into an animated GIF image?

英文:

tl;dr Is it possible to "convert" SVG transformation into animated GIF image?


I have this simple loader, an animated gears that are using animateTransform transfomation to "achieve its goals" (roate gears):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" baseProfile="full" width="213" height="180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <image id="gear1" x="0" y="0" width="92" height="92" xlink:href="gear1.svg"/>
    <image id="gear2" x="57.5" y="57.5" width="122" height="122" xlink:href="gear2.svg" transform="rotate(16.875 118.5 118.5)"/>
    <image id="gear3" x="149" y="24" width="64" height="64" xlink:href="gear3.svg"/>

    <animateTransform xlink:href="#gear1" attributeName="transform" attributeType="XML" type="rotate" from="0 46 46" to="360 46 46" dur="6s" repeatCount="indefinite"/>
    <animateTransform xlink:href="#gear2" attributeName="transform" attributeType="XML" type="rotate" to="16.875 118.5 118.5" from="376.875 118.5 118.5" dur="8s" repeatCount="indefinite"/>
    <animateTransform xlink:href="#gear3" attributeName="transform" attributeType="XML" type="rotate" from="0 181 56" to="360 181 56" dur="4s" repeatCount="indefinite"/>
</svg>

将animateTransform SVG的转换转化为动态.gif图像。

Is there any way to "convert" this piece of XML code (SVG file) into animated .gif?

Services I have tried so far:

I have also tried to simply record this animation, as a screencast, using LICEcap, but to no avail. Animation is very "choppy" even when screen-casting at 24 FPS and I have problem capturing the animation from first to last frame (to get correct "gluing" point), to achieve endless animation.

Can you shed some light here? Is converting SVG transformation into animated image possible?

EDIT: Since all described (and probably all other) on-line services fails to convert SVG transformation into a file then I assume this is a programming question. I need a console script or piece of programming language that will take the this SVG code (as is or modified) as an input and will produce an image as an output.

Unfortunately this makes such question probably too broad, because I have no knowledge to suggest which programming or script language can be used to achieve this task.

答案1

得分: 2

以下是您要翻译的内容:

"With some modifications to your SVG you can use the following code to generate a series of images. The images can then be downloaded and converted to a Gif using a tool like ImageMagick (you need something like this command: how to make a high quality animated image with imagemagick - Stack Overflow).

You can see that I changed the SVG a bit. So, this script will only work if the animateTransform element is a child of the element that needs to be animated (so get rid of the ids and the href attribute). In your case, the animateTransform element can probably (I didn't test and it doesn't work here in SO) be a child of the image element, but all files need to run from the same web server because of cross-origin issues. An alternative approach could be to embed the gear1, 2 and 3 SVGs into the main SVG (and then let the animateTransform element be a child of each svg element (yes, embed SVG elements directly in the main SVG)).

The script generates a lot of links. Each link refers to an image. Right-click the link and select "Save Link as..." and save all the images in a directory. You probably do not need all of them now that a gear doesn't need a full rotation to make an infinite loop gif."

希望这对您有所帮助。

英文:

With some modifications to your SVG you can use the following code to generate a series of images. The images can then be downloaded and converted to a Gif using a tool like ImageMagick (you need something like this command: how to make a high quality animated image with imagemagick - Stack Overflow).

You can see that I changed the SVG a bit. So, this script will only work if the animateTransform element is a child of the element that needs to be animated (so get rid of the ids and the href attribute). In you case the animateTransform element can probably (I didn't test and it doesn't work here in SO) be a child of the image element, but all files need to run from the same webserver because of cross origin issues. An alternative approch could be to embed the gear1, 2 and 3 SVGs into the main SVG (and then let the animateTransform element be a child of each svg element (yes, embed SVG elements directly in the main SVG)).

The script generates a lot of links. Each link refer to an image. Right click the link and select "Save Link as..." and save all the images in a directory. You probably do not need all of them now that a gear don't need a full rotation to make a infinite loop gif.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var svgcontainer, svg, canvas, ctx, output, interval;
var num = 101;

const nsResolver = prefix =&gt; {
  var ns = {
    &#39;svg&#39;: &#39;http://www.w3.org/2000/svg&#39;,
    &#39;xlink&#39;: &#39;http://www.w3.org/1999/xlink&#39;
  };
  return ns[prefix] || null;
};

const takeSnap = function() {
  // get all animateTransform elements
  let animateXPath = document.evaluate(&#39;//svg:*[svg:animateTransform]&#39;, svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  // store all animateTransform animVal.matrix in a dataset attribute
  Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i =&gt; {
    let node = animateXPath.snapshotItem(i);
    let mStr = [...node.transform.animVal].map(animVal =&gt; {
      let m = animVal.matrix;
      return `matrix(${m.a} ${m.b} ${m.c} ${m.d} ${m.e} ${m.f})`;
    }).join(&#39; &#39;);
    node.dataset.transform = mStr;
  });

  // get all animate elements
  animateXPath = document.evaluate(&#39;//svg:animate&#39;, svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  // store all animate properties in a dataset attribute on the target for the animation
  Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i =&gt; {
    let node = animateXPath.snapshotItem(i);
    let propName = node.getAttribute(&#39;attributeName&#39;);
    let target = node.targetElement;
    let computedVal = getComputedStyle(target)[propName];
    target.dataset[propName] = computedVal;
  });

  // create a copy of the SVG DOM
  let parser = new DOMParser();
  let svgcopy = parser.parseFromString(svg.outerHTML, &quot;application/xml&quot;);

  // find all elements with a dataset attribute
  animateXPath = svgcopy.evaluate(&#39;//svg:*[@*[starts-with(name(), &quot;data&quot;)]]&#39;, svgcopy, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  // copy the animated property to a style or attribute on the same element
  Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i =&gt; {
    let node = animateXPath.snapshotItem(i);
    // for each data-
    for (key in node.dataset) {
      if (key == &#39;transform&#39;) {
        node.setAttribute(key, node.dataset[key]);
      } else {
        node.style[key] = node.dataset[key];
      }
    }
  });

  // find all animate and animateTransform elements from the copy document
  animateXPath = svgcopy.evaluate(&#39;//svg:*[starts-with(name(), &quot;animate&quot;)]&#39;, svgcopy, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  // remove all animate and animateTransform elements from the copy document
  Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i =&gt; {
    let node = animateXPath.snapshotItem(i);
    node.remove();
  });

  // create a File object
  let file = new File([svgcopy.rootElement.outerHTML], &#39;svg.svg&#39;, {
    type: &quot;image/svg+xml&quot;
  });
  // and a reader
  let reader = new FileReader();

  reader.addEventListener(&#39;load&#39;, e =&gt; {
    /* create a new image assign the result of the filereader
    to the image src */
    let img = new Image();
    // wait got load
    img.addEventListener(&#39;load&#39;, e =&gt; {
      // update canvas with new image
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = &#39;white&#39;;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(e.target, 0, 0);
      // create PNG image based on canvas
      //let img = new Image();
      //img.src = canvas.toDataURL(&quot;image/png&quot;);
      //output.append(img);
      let a = document.createElement(&#39;A&#39;);
      a.textContent = `Image-${num}`;
      a.href = canvas.toDataURL(&quot;image/png&quot;);
      a.download = `Image-${num}`; 
      num++;
      output.append(a);
    });
    img.src = e.target.result;
  });
  // read the file as a data URL
  reader.readAsDataURL(file);
};

document.addEventListener(&#39;DOMContentLoaded&#39;, e =&gt; {
  svgcontainer = document.getElementById(&#39;svgcontainer&#39;);
  canvas = document.getElementById(&#39;canvas&#39;);
  output = document.getElementById(&#39;output&#39;);
  ctx = canvas.getContext(&#39;2d&#39;);

  let parser = new DOMParser();
  let svgdoc = parser.parseFromString(svgcontainer.innerHTML, &quot;application/xml&quot;);
  canvas.width = svgdoc.rootElement.getAttribute(&#39;width&#39;);
  canvas.height = svgdoc.rootElement.getAttribute(&#39;height&#39;);

  //svgcontainer.innerHTML = svgdoc.rootElement.outerHTML;
  svg = svgcontainer.querySelector(&#39;svg&#39;);
  //console.log(svg);

  // set interval
  interval = setInterval(takeSnap, 50);

  // get all 
  let animateXPath = document.evaluate(&#39;//svg:*[starts-with(name(), &quot;animate&quot;)]&#39;, svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  let animationArr = Object.keys([...Array(animateXPath.snapshotLength)]).map(i =&gt; {
    let node = animateXPath.snapshotItem(i);
    return new Promise((resolve, reject) =&gt; {
      node.addEventListener(&#39;endEvent&#39;, e =&gt; {
        resolve();
      });
    });
  });
  Promise.all(animationArr).then(value =&gt; {
    clearInterval(interval);
  });
});

<!-- language: lang-html -->

&lt;div style=&quot;display:flex&quot;&gt;
  &lt;div id=&quot;svgcontainer&quot;&gt;
    &lt;svg width=&quot;213&quot; height=&quot;180&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
      &lt;rect width=&quot;92&quot; height=&quot;92&quot; fill=&quot;orange&quot;&gt;
        &lt;animateTransform attributeName=&quot;transform&quot; attributeType=&quot;XML&quot; type=&quot;rotate&quot; from=&quot;0 46 46&quot; to=&quot;360 46 46&quot; dur=&quot;6s&quot; repeatCount=&quot;1&quot;/&gt;
      &lt;/rect&gt;
      &lt;rect x=&quot;57.5&quot; y=&quot;57.5&quot; width=&quot;122&quot; height=&quot;122&quot; fill=&quot;green&quot;&gt;
        &lt;animateTransform attributeName=&quot;transform&quot; attributeType=&quot;XML&quot; type=&quot;rotate&quot; to=&quot;16.875 118.5 118.5&quot; from=&quot;376.875 118.5 118.5&quot; dur=&quot;8s&quot; repeatCount=&quot;1&quot;/&gt;
      &lt;/rect&gt;
      &lt;rect x=&quot;149&quot; y=&quot;24&quot; width=&quot;64&quot; height=&quot;64&quot; fill=&quot;navy&quot;&gt;
        &lt;animateTransform attributeName=&quot;transform&quot; attributeType=&quot;XML&quot; type=&quot;rotate&quot; from=&quot;0 181 56&quot; to=&quot;360 181 56&quot; dur=&quot;4s&quot; repeatCount=&quot;1&quot;/&gt;
      &lt;/rect&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;canvas id=&quot;canvas&quot; width=&quot;200&quot; height=&quot;200&quot;&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Exported PNGs:&lt;/p&gt;
&lt;div id=&quot;output&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月18日 07:17:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76276767.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定