将SVG动画转换为图像序列

我正在尝试从SVG动画渲染图像。
这是我的代码。
https://jsfiddle.net/d3bhfa1L/2/

它会生成图像序列,但无法捕获SVG中的动画。

我实际上想将SVG动画转换为mp4格式,但找不到任何直接解决方案。因此,尝试将SVG动画转换为图像序列,然后将图像序列转换为mp4视频。

/* uniformly named URL object */
var DOMURL = window.URL || window.webkitURL || window;

/* our snapshotting class */
function svg_snapshot(svg_ref,fps,seconds) {

    /* DOM object element */
    this.svg_ref = svg_ref;

    /* svg xml root */
    // this.svg_root = svg_ref.contentDocument.documentElement;
    this.svg_root = svg_ref;

    /* frames per second */
    this.fps = fps;

    /* total animation duration in seconds */
    this.seconds = seconds;

    this.svg_root.pauseAnimations();

    this.make_step = function (step,time) {
        if (time > this.seconds * 1000) {
            // animation ended
            return false;
        }

        /* pause for snapshot */
        this.svg_root.pauseAnimations();

        /* save actual svg state as XML */
        var svg_xml = this.svg_root.outerhtml;

        /* disable animation elements with simple replacing */
        svg_xml = svg_xml.replace(new RegExp('<animate','g'),'<not_anim');

        /* save as blob */
        var svg_data = new Blob([svg_xml],{
            type: 'image/svg+xml'
        });

        /* create data url (creates browsers interal blob: data link) */
        var data_url = DOMURL.createObjectURL(svg_data);

        /* create bitmap */
        var img = new Image();

        /* save class reference */
        var self = this;

        /* mount load process */
        img.onload = function () {

            self.make_step_next(step,time,this);

        };

        /* set image url */
        img.src = data_url;

    };

    this.make_step_next = function (step,img) {

        /* create canvas */
        var canvas = document.createElement("canvas");

        canvas.setattribute("width",this.svg_ref.clientWidth);
        canvas.setattribute("height",this.svg_ref.clientHeight);

        canvas.style.border = "1px solid black";

        /* get canvas 2d contextr */
        var ctx = canvas.getcontext('2d');

        /* drav loaded image onto it */
        ctx.drawImage(img,0);

        /* here we can get dataURL (base64 encoded url with image content) */
        var dataURL = canvas.toDataURL('image/png');

        /*
            and here you can do whatever you want - send image
            by ajax (that base64 encoded url which you can decode
            on serverside) or draw somewhere on page
        */
        var finalImg = document.createElement("IMG");

        finalImg.src = dataURL;
        finalImg.style.border = "1px solid black";

        document.body.appendChild(finalImg);


        /*
            let animation continue - before image is loaded,the
            animation is paused - by this is achieved perfect
            timing in this serial process
        */
        this.svg_root.unpauseAnimations();

        var self = this;
        var interval = 1000 / this.fps; // one frame interval

        setTimeout(function () {
            self.make_step(step + 1,time + interval);
        },interval);

    };

}

/* usage - parameters: SVG DOM ref,frames per second,duration in seconds */
var item_ref = new svg_snapshot(document.getElementById('Content-R'),30,1);

/* start snapshotting */
item_ref.make_step(0,0);
kaiyum88 回答:将SVG动画转换为图像序列

所有 css 都不适用于 svg 元素,它们具有有效的特定属性。 尝试使用 gsap 动画引擎。

  1. 尝试使用 SVG

  2. 使用 gsap 为 svg 对象制作动画

  3. gsap 更新事件时,导出 svg 帧(可以使用 SVG.js 库) 您可以使用以下函数导出:

     function saveSvg(svgData) {
         var svgBlob = new Blob([svgData],{type: "image/svg+xml;charset=utf-8"});
         console.log(svgBlob);
         var svgUrl = URL.createObjectURL(svgBlob);
         var downloadLink = document.createElement("a");
         downloadLink.href = svgUrl;
         downloadLink.download = "newesttree.svg";
         document.body.appendChild(downloadLink);
         downloadLink.click();
         document.body.removeChild(downloadLink);
     }
    
     // Call it like this(USING SVG.JS LIBRARY):
     let main_svg = SVG('#id_of_your_svg_root');
     saveSvg(main_svg.svg());
    
  4. 以上将下载svg文件

  5. 使用 ffmpeg 等 svg 编码器将文件合并为视频

,

我知道这是一岁了,但现在我需要解决这个问题,因为实际的 chrome 在动画(路径数据等)期间停止更新 dom 中的 SVG 元素属性,因此代码没有记录器工作。 (顺便说一句,我是示例代码的作者)

现在可能没有办法从 SVG SMIL 动画中捕捉帧。但在 SVG CSS 动画中,window.getComputedStyle 是可能的。

我看到你的动画是这样完成的 - 所以我用它来弄清楚,这里是更新的工作示例(在 Chrome 中):

    /* uniformly named URL object */
    var DOMURL = window.URL || window.webkitURL || window;

    /* our snapshotting class */
    function svg_snapshot(svg_ref,fps,seconds,correction) {

        var self = this;

        /* DOM object element */
        this.svg_ref = svg_ref;

        /* svg xml root */
        //this.svg_root = svg_ref.contentDocument.documentElement;
        this.svg_root = svg_ref;

        /* frames per second */
        this.fps = fps;

        /* total animation duration in seconds */
        this.seconds = seconds;

        /* frame msec correction (to fix getComputedStyle bug which shifts a little animation) */
        this.correction = correction;

        /* list of possible elements (it probably not complete) */
        this.elements = ['path','circle','g'];

        /* list of possible styles */
        this.styleKeys = ['d','fill','fill-opacity','stroke','stroke-dasharray','stroke-dashoffset','stroke-linecap','stroke-linejoin','stroke-misterlimit','stroke-opacity','stroke-width','opacity','transform'];

        this.svg_element_list = {};

        /* load specific element lists from svg */
        this.elements.forEach(function(elementName) {

            var sf,els = self.svg_root.getElementsByTagName(elementName);

            self.svg_element_list[elementName] = [];

            for (sf = 0; sf < els.length; sf++) {
                self.svg_element_list[elementName].push({
                    "ref": els.item(sf),"style": null
                });
            }
        });

        this.toggle_pause = function(flag) {
            var self = this;

            /* pause and save computed style when paused */
            this.elements.forEach(function(elementName) {
                self.svg_element_list[elementName].forEach(function(c) {
                    c["ref"].style.animationPlayState = flag ? "paused" : "";
                    c["style"] = flag ? getComputedStyle(c["ref"]) : null;
                });
            });
        };

        this.toggle_pause(true);

        this.make_step = function(step) {

            /* pause for snapshot */
            this.toggle_pause(true);

            if (step > this.fps * this.seconds) {
                // animation ended
                return false;
            }

            /* save object reference */
            var self = this;

            /* capture snapped svg xml string */
            var xml_data = this.svg_root.outerHTML;

            /* parse snapped svg frame as object DOM structure */
            var svg_xml = (new DOMParser()).parseFromString(xml_data,"text/xml");

            /* remove style elements (it may damage snapshotted svg and will be rewritten imidietly by inline styles) */
            var sf,styleEls = svg_xml.getElementsByTagName('style');

            for (sf = styleEls.length - 1; sf >= 0; sf--) {
                styleEls[sf].parentNode.removeChild(styleEls[sf]);
            }

            /* loop trough possible elements and copy */
            this.elements.forEach(function(elementName) {

                /* capture the processed style */
                /* paths in snapped svg */
                var elsSnap = svg_xml.getElementsByTagName(elementName);

                for (var sf = 0; sf < self.svg_element_list[elementName].length; sf++) {
                    var cStyle = self.svg_element_list[elementName][sf]["style"];

                    /* enforce computed styles state */
                    self.styleKeys.forEach(function(stKey) {
                        elsSnap[sf].style[stKey] = cStyle.getPropertyValue(stKey);
                    });
                }
            });

            /* save as blob with a help of XMLserializer (using object DOM structure) */
            var svg_data = new Blob([(new XMLSerializer()).serializeToString(svg_xml)],{type: 'image/svg+xml'});

            /* create data url (creates browsers interal blob: data link) */
            var data_url = DOMURL.createObjectURL(svg_data);

            /* create bitmap */
            var img = new Image();

            /* mount load process */
            img.onload = function() {

                self.make_step_next(step,this);

            };

            /* set image url */
            img.src = data_url;

        };

        this.make_step_next = function(step,img) {
            var self = this;

            /* create canvas */
            var canvas = document.createElement("canvas");

            canvas.setAttribute("width",this.svg_ref.clientWidth);
            canvas.setAttribute("height",this.svg_ref.clientHeight);

            canvas.style.border = "1px solid black";

            /* get canvas 2d contextr */
            var ctx = canvas.getContext('2d');

            /* drav loaded image onto it */
            ctx.drawImage(img,0);

            /* here we can get dataURL (base64 encoded url with image content) */
            var dataURL = canvas.toDataURL('image/png');

            /* and here you can do whatever you want - send image by ajax (that base64 encoded url which you can decode on serverside) or draw somewhere on page */
            var finalImg = document.createElement("IMG");

            finalImg.style.border = "1px solid black";

            /* add frame number to image alt */
            /* it might represent file name etc. */
            var frameId = "000" + step;
            finalImg.alt = "frame" + frameId.substr(frameId.length - 3);

            finalImg.onload = function() {
                document.getElementById("image_stack").appendChild(finalImg);

                self.make_step_go(step + 1);
            };

            finalImg.src = dataURL;
        };

        this.make_step_go = function(step) {
            var self = this;
            var interval = 1000 / this.fps - this.correction; // one frame interval

            setTimeout(function() {
                self.make_step(step);
            },interval);

            /* let animation continue - before image is loaded,the animation is paused - by this is achieved perfect timing in this serial process */
            this.toggle_pause(false);
        };
    }

Jsfiddle:https://jsfiddle.net/p275rmah/

注意:window.getComputedStyle 方法有一个错误或意外行为,它会在每次读取属性值时以某种方式向前移动动画。除了对时间间隔进行一些修正之外,我没有找到任何其他方法来防止这种情况发生 - 这将针对每个不同的 SVG。

该代码可能不适用于所有 SVG 动画 - 但有些情况可以通过更新 this.styleKeys 和 this.elements 属性轻松解决。

本文链接:https://www.f2er.com/3071829.html

大家都在问