Templarian

design.develop.deploy[.drink]

NodeJS SVG to Animated Gif

Needed an to take an SVG and individually grab frames of the same SVG and place them into an animated gif. This lib simplifies it to await gif.addFrame(svgString, delay).

img-min--1--1

The SvgGif util below simplified the use of gif-encoder, convert-svg-to-png, and png-js.

const fs = require('fs');
const GifEncoder = require('gif-encoder');
const { convert } = require('convert-svg-to-png');
const Png = require('png-js');

function decode(data) {
    return new Promise((resolve) => {
        new Png(data).decode((pixels) => {
            resolve(pixels);
        });
    });
}

class SvgGif {
    constructor({ width, height, fileName = 'img.gif', repeat = 0 }) {
        // Create GIF
        this.gif = new GifEncoder(width, height);
        this.gif.setRepeat(0);
        this.gif.on('error', (err) => console.log(err));

        // Collect output
        const file = fs.createWriteStream('img.gif');
        this.gif.pipe(file);
        this.gif.writeHeader();
    }

    async addFrame(svg, delay = 1000) {
        this.gif.setDelay(delay);
        const data = await convert(svg);
        const pixels = await decode(data);
        this.gif.addFrame(pixels);
    }

    finish() {
        this.gif.finish();
    }
}

module.exports = SvgGif;

Example usage:

const SvgGif = require('./SvgGif');

const width = 1280;
const height = 720;

var svg = `
    ... place full svg file contents here ...
`;

(async () => {

    const gif = new SvgGif({
        width,
        height,
        fileName: 'img.gif'
    });

    console.log(`Generating...`);
    
    // Render 1 frame for 1 second
    await gif.addFrame(svg, 1000);
    
    // Modify SVG here and render frame
    await gif.addFrame(svg, 1000);
    
    // Done
    gif.finish();
    console.log(`Done.`);
})();