mirror of
https://github.com/ful1e5/Bibata_Cursor.git
synced 2025-05-24 20:14:29 -04:00
🖼️ Animated pngs render
This commit is contained in:
parent
05a7d723f6
commit
3116d8f50d
4 changed files with 141 additions and 45 deletions
|
@ -1,8 +1,10 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import puppeteer, { Browser } from "puppeteer";
|
||||
import puppeteer, { Browser, ElementHandle, Page } from "puppeteer";
|
||||
|
||||
import { frameNumber } from "./util/frameNumber";
|
||||
import { matchImages } from "./util/matchImages";
|
||||
import { toHTML } from "./util/toHTML";
|
||||
|
||||
class BitmapsGenerator {
|
||||
|
@ -26,34 +28,6 @@ class BitmapsGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
// private async screenshot(
|
||||
// element: ElementHandle<Element>
|
||||
// ): Promise<string | void | Buffer> {
|
||||
// return element.screenshot({
|
||||
// omitBackground: true,
|
||||
// encoding: "binary",
|
||||
// });
|
||||
// }
|
||||
|
||||
// private async stopAnimation(page: Page) {
|
||||
// // @ts-ignore
|
||||
// await page._client.send("Animation.setPlaybackRate", {
|
||||
// playbackRate: 0,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private async resumeAnimation(page: Page, playbackRate: number = 0.1) {
|
||||
// // @ts-ignore
|
||||
// await page._client.send("Animation.setPlaybackRate", {
|
||||
// playbackRate,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private async saveFrameImage(key: string, frame: Buffer) {
|
||||
// const out_path = path.resolve(outDir, key);
|
||||
// fs.writeFileSync(out_path, frame, { encoding: "binary" });
|
||||
// }
|
||||
|
||||
/**
|
||||
* Prepare headless browser.
|
||||
*/
|
||||
|
@ -63,17 +37,15 @@ class BitmapsGenerator {
|
|||
headless: true,
|
||||
});
|
||||
}
|
||||
public async generate(
|
||||
browser: Browser,
|
||||
content: string,
|
||||
out: string,
|
||||
animated: boolean = false
|
||||
) {
|
||||
|
||||
private async getSvgElement(
|
||||
page: Page,
|
||||
content: string
|
||||
): Promise<ElementHandle<Element>> {
|
||||
if (!content) {
|
||||
throw new Error(`${content} File Read error`);
|
||||
}
|
||||
|
||||
const page = await browser.newPage();
|
||||
const html = toHTML(content);
|
||||
await page.setContent(html);
|
||||
|
||||
|
@ -82,12 +54,109 @@ class BitmapsGenerator {
|
|||
if (!svg) {
|
||||
throw new Error("svg element not found!");
|
||||
}
|
||||
|
||||
if (animated) {
|
||||
console.log("animated");
|
||||
} else {
|
||||
await svg.screenshot({ omitBackground: true, path: out });
|
||||
return svg;
|
||||
}
|
||||
|
||||
public async generateStatic(
|
||||
browser: Browser,
|
||||
content: string,
|
||||
key: string
|
||||
) {
|
||||
const page = await browser.newPage();
|
||||
const svg = await this.getSvgElement(page, content);
|
||||
|
||||
const out = path.resolve(this.bitmapsDir, key);
|
||||
|
||||
console.log("Saving", key, "...");
|
||||
await svg.screenshot({ omitBackground: true, path: out });
|
||||
await page.close();
|
||||
}
|
||||
|
||||
private async screenshot(
|
||||
element: ElementHandle<Element>
|
||||
): Promise<Buffer | string> {
|
||||
const buffer = await element.screenshot({
|
||||
encoding: "binary",
|
||||
omitBackground: true,
|
||||
});
|
||||
|
||||
if (!buffer) {
|
||||
throw new Error("SVG element screenshot not working");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private async stopAnimation(page: Page) {
|
||||
//@ts-ignore
|
||||
await page._client.send("Animation.setPlaybackRate", {
|
||||
playbackRate: 0,
|
||||
});
|
||||
}
|
||||
|
||||
private async resumeAnimation(page: Page, playbackRate: number) {
|
||||
//@ts-ignore
|
||||
await page._client.send("Animation.setPlaybackRate", {
|
||||
playbackRate,
|
||||
});
|
||||
}
|
||||
|
||||
private async saveFrameImage(key: string, frame: Buffer | string) {
|
||||
const out_path = path.resolve(this.bitmapsDir, key);
|
||||
fs.writeFileSync(out_path, frame);
|
||||
}
|
||||
|
||||
public async generateAnimated(
|
||||
browser: Browser,
|
||||
content: string,
|
||||
key: string,
|
||||
options: {
|
||||
playbackRate: number;
|
||||
diff: number;
|
||||
frameLimit: number;
|
||||
framePadding: number;
|
||||
} = {
|
||||
playbackRate: 0.3,
|
||||
diff: 0,
|
||||
frameLimit: 300,
|
||||
framePadding: 3,
|
||||
}
|
||||
) {
|
||||
const page = await browser.newPage();
|
||||
const svg = await this.getSvgElement(page, content);
|
||||
await this.stopAnimation(page);
|
||||
|
||||
let index = 1;
|
||||
let breakRendering = false;
|
||||
let prevImg: Buffer | string;
|
||||
|
||||
// Rendering frames till `imgN` matched to `imgN-1` (When Animation is done)
|
||||
while (!breakRendering) {
|
||||
if (index > options.frameLimit) {
|
||||
throw new Error("Reached the frame limit.");
|
||||
}
|
||||
|
||||
this.resumeAnimation(page, options.playbackRate);
|
||||
const img: string | Buffer = await this.screenshot(svg);
|
||||
this.stopAnimation(page);
|
||||
|
||||
if (index > 1) {
|
||||
// @ts-ignore
|
||||
const diff = matchImages(prevImg, img);
|
||||
if (diff <= options.diff) {
|
||||
breakRendering = !breakRendering;
|
||||
}
|
||||
}
|
||||
const number = frameNumber(index, options.framePadding);
|
||||
const frame = `${key}-${number}.png`;
|
||||
|
||||
console.log("Saving", frame, "...");
|
||||
this.saveFrameImage(frame, img);
|
||||
|
||||
prevImg = img;
|
||||
++index;
|
||||
}
|
||||
|
||||
await page.close();
|
||||
}
|
||||
}
|
||||
export { BitmapsGenerator };
|
||||
|
|
7
bitmapper/packages/core/src/util/frameNumber.ts
Normal file
7
bitmapper/packages/core/src/util/frameNumber.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const frameNumber = (index: number, padding: number) => {
|
||||
let result = "" + index;
|
||||
while (result.length < padding) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
};
|
11
bitmapper/packages/core/src/util/matchImages.ts
Normal file
11
bitmapper/packages/core/src/util/matchImages.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import Pixelmatch from "pixelmatch";
|
||||
import { PNG } from "pngjs";
|
||||
|
||||
export const matchImages = (img1: Buffer, img2: Buffer): number => {
|
||||
const { data: img1Data, width, height } = PNG.sync.read(img1);
|
||||
const { data: imgNData } = PNG.sync.read(img2);
|
||||
|
||||
return Pixelmatch(img1Data, imgNData, null, width, height, {
|
||||
threshold: 0.1,
|
||||
});
|
||||
};
|
|
@ -24,16 +24,25 @@ const main = async () => {
|
|||
const png = new BitmapsGenerator(bitmapsDir);
|
||||
const browser = await png.getBrowser();
|
||||
|
||||
SVG.getStatic().forEach(async (svg) => {
|
||||
for (const svg of SVG.getStatic()) {
|
||||
const key = `${path.basename(svg, ".svg")}.png`;
|
||||
console.log("Saving", key, "...");
|
||||
const out = path.resolve(bitmapsDir, key);
|
||||
|
||||
let content = fs.readFileSync(svg, "utf-8");
|
||||
content = SVGHandler.colorSvg(content, color);
|
||||
|
||||
await png.generate(browser, content, out);
|
||||
});
|
||||
await png.generateStatic(browser, content, key);
|
||||
}
|
||||
|
||||
for (const svg of SVG.getAnimated()) {
|
||||
const key = `${path.basename(svg, ".svg")}.png`;
|
||||
|
||||
let content = fs.readFileSync(svg, "utf-8");
|
||||
content = SVGHandler.colorSvg(content, color);
|
||||
|
||||
await png.generateAnimated(browser, content, key);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
};
|
||||
|
||||
main();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue