Warning: session_start(): open(/var/lib/php/sessions/sess_17rjt7meglcegmvftf7570isf5, O_RDWR) failed: No space left on device (28) in /var/www/html/dzejkobgames.eu/script/unit.php on line 2
How to export frames from animated SVG | Dzejkob games - indie game developers

1.11.2017

How to export frames from animated SVG (or convert animated SVG to GIF, APNG)

(2.2020) Note: this method of exporting frames from SVG is for SMIL animations and is obsolete and not working in the actual version of chrome. New CSS animation recommended - see updated article here.

I was surprised, that after few hours of searching i couldn't find any simple solution how to export frames from SVG animation. There also none open software or library that can interpret and play SVG format - only browsers have that capability.

So that's an opportunity for some invention. With javascript, its possible to start/play/pause animation and you can render it onto 2d canvas. It's little tricky to keep animation state when rendering, but its possible.

Here is relatively simple code with comments: ( you need no external libraries - but modern browser recommended - tested on latest Chrome )


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

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

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

                    

That's the principle. With those frames you could build them into animation in different format ( for example using convert ) or use them directly - to increase SVG performance or use it on some platform, where SVG isn't supported.

And here some live demonstration:



© 2024 Dzejkob games | info@dzejkobgames.eu | YouTube channel | Itch.io


Warning: Unknown: open(/var/lib/php/sessions/sess_17rjt7meglcegmvftf7570isf5, O_RDWR) failed: No space left on device (28) in Unknown on line 0

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php/sessions) in Unknown on line 0