【问题标题】:Can you export all frames from -svg to raster images -jpg, png,.. using JS您可以使用 JS 将所​​有帧从 -svg 导出到光栅图像 -jpg、png、..
【发布时间】:2019-01-21 04:29:10
【问题描述】:

众所周知,我们可以使用 SVGElement.pauseAnimations() 方法暂停 svg 动画,我们还可以使用 SVGElement.setCurrentTime() 方法设置动画当前时间 - 以秒为单位的第一个参数。一切正常,但我的问题是,您可以将暂停的帧导出到光栅图像 -jpg, png 吗?

示例(这里我们创建一个带有动画的 svg 并在时间 - 0.958 秒处暂停动画)

let svg = document.getElementById('testSvg');
svg.pauseAnimations();
svg.setCurrentTime(0.958); // keyframes times: 0s, 0.458s, 0.958s
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body>

<svg id="testSvg" image-rendering="auto" baseProfile="basic" version="1.1" x="0px" y="0px" width="550" height="400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
   <g id="Scene-1" overflow="visible" transform="translate(-56 -145.5)">
    <g display="none" id="Layer3_0_FILL">
     <path fill="#F00" stroke="none" d="M116.3 159.95L116.3 220.95 232.8 220.95 232.8 159.95 116.3 159.95Z" test="Scene 1"/>
     <animate attributeName="display" repeatCount="indefinite" dur="1s" keyTimes="0;.958;1" values="none;inline;inline"/>
   </g>
   <g display="none" id="Layer2_0_FILL">
     <path fill="#0F0" stroke="none" d="M116.3 159.95L116.3 220.95 232.8 220.95 232.8 159.95 116.3 159.95Z" test="Scene 1"/>
     <animate attributeName="display" repeatCount="indefinite" dur="1s" keyTimes="0;.458;.958;1" values="none;inline;none;none"/>
   </g>
   <g id="Layer1_0_FILL">
     <path fill="#0F0" stroke="none" d="M78.05 139.95L78.05 240.95 271 240.95 271 139.95 78.05 139.95Z" test="Scene 1"/>
     <animate attributeName="display" repeatCount="indefinite" dur="1s" keyTimes="0;.458;1" values="inline;none;none"/>
   </g>
 </g>
</svg>
 
</body>
</html>

现在,如果我们使用 XMLSerializer 将 svg 设置为图像的 src。

let image = document.createElement( "image" );
let xml = new XMLSerializer().serializeToString(svg);
let data = "data:image/svg+xml;base64," + btoa(xml);
image.src = data;
document.body.appendChild();

图像已设置,但动画正常播放,现在已暂停。那么有没有办法在图像标签中暂停动画。或者使用暂停的 svg 元素并以某种方式从中导出光栅图像。

【问题讨论】:

    标签: javascript image svg export


    【解决方案1】:

    我能想到的最好方法是读取动画属性并将它们设置为动画图像的克隆。

    这不是小事。 SMIL 动画可以针对 XML 和 CSS 属性,两者的语法不同。此外,您必须仔细查看 XML 属性的名称是否与属性名称匹配。情况可能并非总是如此。那么,基本流程是这样的:

    • 虽然没有说明暂停动画是异步完成的,但我惊讶地发现它似乎是。您必须延迟读取属性,例如使用setTimeout

    • 对于 CSS 表示属性,

      window.getComputedStyle(element)[attribute]
      
    • 对于 XML 属性,

      element[attribute].animVal.valueAsString || element[attribute].animVal
      

      这有另一个复杂性:SVG 有一个用于处理单元的专用接口。例如,当您获取一个长度值时,您需要使用animVal.valueAsString 获取属性字符串。对于numberstring 列表,或者对于transforms,它变得更加复杂,因为这些列表没有实现Iterable 接口。

    这是一个适用于所列属性的示例。为了便于识别,我在&lt;animate&gt; 元素上设置了attributeType,并在目标元素上设置了一个id,因为您需要在原始元素(处于暂停动画状态)及其克隆(即不是)。我已经在画布上绘制了,因此您可以立即获得可用的光栅图像。

    const svg = document.getElementById('testSvg');
    const canvas = document.querySelector('canvas');
    const ctx = canvas.getContext("2d");
    
    svg.pauseAnimations();
    svg.setCurrentTime(0.35);
    
    setTimeout(() => {
      // clone svg
      const copy = svg.cloneNode(true);
      // remove the animations from the clone
      copy.querySelectorAll('animate').forEach(animate => animate.remove());
    
      // query all animate elements on the original
      svg.querySelectorAll('animate').forEach(animate => {
        // target element in original
        const target = animate.targetElement;
        const attr = animate.getAttribute('attributeName');
        const type = animate.getAttribute('attributeType');
        // target element in copy
        const copyTarget = copy.getElementById(target.id);
        // differentiate attribute type
        if (type === 'XML') {
          const value = target[attr].animVal.valueAsString || target[attr].animVal;
          copyTarget.setAttribute(attr, value);
        } else if (type === 'CSS') {
          const value = window.getComputedStyle(target)[attr];
          copyTarget.style[attr] = value
        }
      });
      svg.unpauseAnimations();
    
      const xml = new XMLSerializer().serializeToString(copy);
      const data = "data:image/svg+xml;base64," + btoa(xml);
      const image = new Image();
      // image load is asynchronuous
      image.onload = () => ctx.drawImage(image, 0, 0);
      image.src = data;
    },0);
    <svg id="testSvg" width="200" height="200">
      <rect id="animationTarget" x="50" y="50" width="100" height="100"
            rx="0" fill="red">
        <animate attributeType="XML" attributeName="rx"
          dur="1s" keyTimes="0;0.5;1" values="0;50;0" />
        <animate attributeType="CSS" attributeName="fill"
          dur="1s" keyTimes="0;0.25;0.75" values="red;green;red" calcMode="discrete" />
      </rect>
    </svg>
    <canvas width="200" height="200"></canvas>

    【讨论】:

    • 非常感谢,你帮了我很多!!!要使用 animateTransform 标签管理动画,当我得到它的 -animVal 时,我收到 SVGTransformList,然后我循环遍历列表中的所有 SVGTransform 元素,然后我得到它的 SVGMatrix 。然后组合所有矩阵值 - a,b, c,d,e,f 转换成一个字符串,这样我就可以使用属性 -additive="sum" 的多个动画。
    • 执行此操作的复杂程度令人震惊,毕竟浏览器已经可以查找和暂停合并的 SVG 动画并将外部 SVG 图像导出为画布,但不能同时进行跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 2012-02-14
    • 2014-10-18
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    相关资源
    最近更新 更多