Skip to content

Instantly share code, notes, and snippets.

@jbmoelker
Created April 5, 2016 07:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jbmoelker/816b5307f6913334d4217f2fe719eb5b to your computer and use it in GitHub Desktop.
Save jbmoelker/816b5307f6913334d4217f2fe719eb5b to your computer and use it in GitHub Desktop.
[POC] Animated SVG to GIF
'use strict';
/**
* Script converts an animated SVG (`fileIn`) into an animated GIF (`fileOut`)
* with the given `duration`, `fps`, `width` and `height.
* This is just a proof-of-concept. Script needs an API etc etc.
*/
// config which should come from cli args
const fileIn = 'animation.svg';
const fileOut = 'animation.gif';
const duration = 2000; // ms
const fps = 60;
const width = 320; // px
const height = 320; // px
// dependencies
const fs = require('fs');
const GifEncoder = require('gifencoder');
const phantom = require('phantom');
const pngFileStream = require('png-file-stream');
const Promise = require('bluebird');
const mkdirp = Promise.promisify(require('mkdirp'));
const rmdir = Promise.promisify(require('rimraf'));
// internal vars
let instance;
const encoder = new GifEncoder(width, height);
let page;
const tempDir = 'frames/';
/**
* create page with source svg
* render each frame to a png
* stitch all pngs together into a gif
*/
phantom.create()
.then(_instance => {
instance = _instance;
return instance.createPage();
})
.then(_page => {
page = _page;
page.viewportSize = { width, height };
return page.open(`file://${__dirname}/${fileIn}`);
})
.then(() => mkdirp(tempDir))
.then(() => renderFrames())
.then(() => page.close())
.then(() => instance.exit())
.then(() => renderGif())
.then(() => rmdir(tempDir))
.catch(err => console.error(err));
/**
* Render each frame to a png in temp dir.
* @returns {Promise}
*/
function renderFrames() {
return new Promise((resolve, reject) => {
let frameIndex = 0;
const interval = setInterval(() => {
page.render(`${tempDir}${frameIndex}.png`).catch(reject);
frameIndex ++;
if (frameIndex > duration/fps) {
clearInterval(interval);
resolve();
}
}, 1000/fps);
});
}
/**
* Stich png frames from temp dir together and write to gif
*/
function renderGif() {
return new Promise((resolve, reject) => {
pngFileStream(`${tempDir}?.png`)
.pipe(encoder.createWriteStream({ repeat: -1, delay: 1000/fps, quality: 10 }))
.pipe(fs.createWriteStream(fileOut))
.on('error', err => reject(err))
.on('finish', () => resolve());
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment