Last active
February 12, 2025 13:51
-
-
Save graup/75c44e975f3baf4f95b6e3f35f0fdb83 to your computer and use it in GitHub Desktop.
Revisions
-
graup renamed this gist
Apr 16, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
graup revised this gist
Apr 16, 2024 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ We use `playwright` to capture screenshots of our animated SVG (or really anything that can be loaded into a browser), then use `sharp` to convert pngs into webps, then `node-webpmux` to create the animated webp. 1. Install dependencies 2. `yarn ts-node capture.ts` 3. `yarn ts-node writeWebP.ts` -
graup created this gist
Apr 16, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,3 @@ 1. Install dependencies 2. `yarn ts-node capture.ts` 3. `yarn ts-node writeWebP.ts` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,27 @@ // Convert svg into frames by capturing screenshots with Playwright import { chromium, Browser, Page } from 'playwright'; const dir = 'frames/'; async function captureFrames(url: string, totalFrames: number, frameDurationMs: number): Promise<void> { const browser: Browser = await chromium.launch(); const page: Page = await browser.newPage(); await page.goto(url); const svg = page.locator('svg'); const boundingBox = await svg.boundingBox(); for (let i = 0; i < totalFrames; i++) { await page.screenshot({ path: `${dir}frame${i}.png`, clip: boundingBox }); await page.waitForTimeout(frameDurationMs); } await browser.close(); } const url = "file:///path to your svg"; const duration = 2000; const fps = 20; const totalFrames = (duration / 1000) * fps; captureFrames(url, totalFrames, duration / totalFrames); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ { "name": "svg-to-webp", "author": "Paul Grau <[email protected]>", "license": "ISC", "dependencies": { "@types/node": "^20.12.7", "node-webpmux": "^3.2.0", "playwright": "^1.43.1", "sharp": "^0.33.3", "ts-node": "^10.9.2", "typescript": "^5.4.5" } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,51 @@ // Make animated webp file from individual frames // @ts-ignore: missing type import { Image } from 'node-webpmux'; import * as sharp from 'sharp'; import { promises as fs } from 'fs'; async function getOriginalFrames(inputDirectory: string) { const filePaths = await fs.readdir(inputDirectory).then(files => files.flatMap(file => file.endsWith('.png') ? `${inputDirectory}/${file}` : []) ); filePaths.sort((a, b) => a.localeCompare(b, 'en', { numeric: true, ignorePunctuation: true })); return filePaths; } async function convertToWebP(inputFilePaths: string[]) { return await Promise.all(inputFilePaths.map(async filePath => { const webpFile = `${filePath}.webp`; await sharp(filePath).toFile(webpFile); return webpFile; })); } async function createWebPAnimation(inputDirectory: string, outputPath: string, delay: number): Promise<void> { const originFilePaths = await getOriginalFrames(inputDirectory); const framePaths = await convertToWebP(originFilePaths); await Image.initLib(); const firstFrame = new Image(); await firstFrame.load(await fs.readFile(framePaths[0])); firstFrame.convertToAnim(); const frames = await Promise.all( framePaths.map(async (path) => { const frame = new Image(); await frame.load(await fs.readFile(path)); return frame; }) ); await firstFrame.save(outputPath, { frames: frames.map(frame => ({ img: frame, delay, })) }); } createWebPAnimation("frames", 'animation.webp', 100);