Yagnik Vadaliya
Yagnik Vadaliya

Reputation: 11

how to convert my animated SVG in to GIF fromat

currently, this approach launches svg in the browser through puppeteer and then takes snapshots in PNGs and after that combines those PNGs and makes gif but this process is resource-intensive and also not get the solution that i wanted

this is my current approach, it is working but not efficient


let fs = require("fs");
let child_process = require("child_process");
const { exec } = require("child_process");

let puppeteer = require("puppeteer");

const usage = "usage: node index.js <svgPath> <duration> <fps> <outDir>";
const imgExtention = "png";
const imgType = "png";

async function main() {
  let [svgPath, duration, fps, outDir] = process.argv;

  svgPath = "react-flow-canvas.svg";
  duration = 4;
  fps = 10;
  outDir = "output";
  if (outDir === undefined) {
    console.error("outDir is not defined");
    console.log(usage);
    process.exit(2);
  }
  const svg = fs.readFileSync(svgPath, "utf-8");

  duration = parseFloat(duration);
  fps = parseInt(fps);
  console.log("duration: " + duration + " s, fps: " + fps);
  const totalFrames = Math.floor(fps * duration);
  const digits = Math.floor(Math.log10(totalFrames)) + 1;
  console.log("totalFrames: " + totalFrames);

  process.chdir(outDir);
  await createFrames(svg, fps, totalFrames, digits);
  convertToMP4(fps, totalFrames, digits);
}

async function createFrames(svg, fps, totalFrames, digits) {
  svg = svg.replace("--play-state: running;", "--play-state: paused;");

  let browser = await puppeteer.launch({
    headless: true,
    args: ["--no-sandbox", "--font-render-hinting=none"],
  });

  let page = await browser.newPage();
  await page.goto("about:blank");
  await page.setContent(svg);

  let renderSettings = {
    type: imgType,
    omitBackground: false,
  };

  console.log("creating frames");
  for (let i = 1; i <= totalFrames; ++i) {
    let result = await page.evaluate(function (startVal) {
      document
        .getElementsByTagName("svg")[0]
        .style.setProperty("--start", startVal);
    }, "" + (i - 1) / fps + "s");

    await page.waitForTimeout(1);

    let outputElem = await page.$("svg");
    let prefix = ("" + i).padStart(digits, "0");
    renderSettings.path = prefix + "." + imgExtention;
    await outputElem.screenshot(renderSettings);
    if (i % fps === 0 || i === totalFrames) {
      console.log("progress: " + prefix + " / " + totalFrames);
    }
  }

  await browser.close();
  return totalFrames, digits;
}

function convertToMP4(fps, totalFrames, digits) {
  console.log("running ffmpeg");
  let output = child_process.execFileSync(
    "ffmpeg",
    [
      "-hide_banner",
      "-loglevel",
      "warning",
      "-y",
      "-framerate",
      "" + fps,
      "-i",
      "%0" + digits + "d." + imgExtention,
      "-c:v",
      "libx264",
      "-vf",
      "fps=" + fps,
      "-pix_fmt",
      "yuv420p",
      "output.mp4",
    ],
    { encoding: "utf8" }
  );
  console.log(output);

  exec("ffmpeg -i output.mp4 -qscale 200 output.gif");
}

main();

Please help me to do that in an efficient manner

Upvotes: 1

Views: 475

Answers (1)

Yagnik Vadaliya
Yagnik Vadaliya

Reputation: 11

I got better solution for this

const puppeteer = require("puppeteer");
const { PuppeteerScreenRecorder } = require("puppeteer-screen-recorder");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
let fs = require("fs");

const wait = (ms) => new Promise((res) => setTimeout(res, ms));

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    args: ["--no-sandbox", "--font-render-hinting=none"],
  });
  try {
    const page = await browser.newPage();
    await page.setViewport({
      width: 1920,
      height: 1080,
      deviceScaleFactor: 3,
    });
    const svg = fs.readFileSync("react-flow-canvas.svg", "utf-8");
    // await page.goto("about:blank");
    await page.setContent(svg);
    const Config = {
      fps: 25,
    };

    const recorder = new PuppeteerScreenRecorder(page, Config);

    // await page.goto("https://tailwindcss.com/");

    await recorder.start("video.mp4");
    await wait(1700);
    // await page.waitForTimeout(1);
    await recorder.stop();

    await exec("ffmpeg -i video.mp4 -qscale 0 animated.gif");
  } catch (e) {
    console.log(e);
  } finally {
    await browser.close();
  }
})();

Upvotes: 0

Related Questions