13.12.2018

Capturing SVG frames with advanced effects(update of capturing frames in complex SVG animations)

In the approach in the older article its shows up, it has some limitations. The procedure is working only for animations, which changes path points directly, so removing them does not affect the current image frame. But for many animations, which changes opacity, colors etc. the frame stays in implicit state.

The key is quite simple - just use window.getComputedStyle - it working also for SVG.

The principle is, just copy the computed style from paused SVG animation to snapshotted SVG, like this:


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

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

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

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

/* capture the processed style */
/* paths in original svg */
var pathsOrig = this.svg_root.getElementsByTagName('path');
/* paths in snapped svg */
var pathsSnap = svg_xml.getElementsByTagName('path');

for (var sf = 0; sf < pathsOrig.length; sf++) {
    var cStyle = getComputedStyle(pathsOrig[sf]);

    /* update style state */
    pathsSnap[sf].style.fill = cStyle.fill;
    pathsSnap[sf].style.opacity = cStyle.opacity;
}

/* 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'});

                

(You might well be needed to add desired styles, other SVG elements etc.)

Here is the full updated code:
(and example below)


/* 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;

    /* 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();

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

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

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

        /* capture the processed style */
        /* paths in original svg */
        var pathsOrig = this.svg_root.getElementsByTagName('path');
        /* paths in snapped svg */
        var pathsSnap = svg_xml.getElementsByTagName('path');

        for (var sf = 0; sf < pathsOrig.length; sf++) {
            var cStyle = getComputedStyle(pathsOrig[sf]);

            /* update style state */
            pathsSnap[sf].style.fill = cStyle.fill;
            pathsSnap[sf].style.opacity = cStyle.opacity;
        }

        /* 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();

        /* 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, time, 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, 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.getElementById("image_stack").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('id_of_some_svg'), 30, 1);

/* start snapshotting */
item_ref.make_step(0, 0);

                

And here some demonstration (see the snapped flashes):



© 2019 Dzejkob games | info@dzejkobgames.eu