mirror of
https://github.com/ful1e5/Bibata_Cursor.git
synced 2025-05-25 04:24:33 -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 fs from "fs";
|
||||||
import path from "path";
|
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";
|
import { toHTML } from "./util/toHTML";
|
||||||
|
|
||||||
class BitmapsGenerator {
|
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.
|
* Prepare headless browser.
|
||||||
*/
|
*/
|
||||||
|
@ -63,17 +37,15 @@ class BitmapsGenerator {
|
||||||
headless: true,
|
headless: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public async generate(
|
|
||||||
browser: Browser,
|
private async getSvgElement(
|
||||||
content: string,
|
page: Page,
|
||||||
out: string,
|
content: string
|
||||||
animated: boolean = false
|
): Promise<ElementHandle<Element>> {
|
||||||
) {
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new Error(`${content} File Read error`);
|
throw new Error(`${content} File Read error`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = await browser.newPage();
|
|
||||||
const html = toHTML(content);
|
const html = toHTML(content);
|
||||||
await page.setContent(html);
|
await page.setContent(html);
|
||||||
|
|
||||||
|
@ -82,12 +54,109 @@ class BitmapsGenerator {
|
||||||
if (!svg) {
|
if (!svg) {
|
||||||
throw new Error("svg element not found!");
|
throw new Error("svg element not found!");
|
||||||
}
|
}
|
||||||
|
return svg;
|
||||||
if (animated) {
|
|
||||||
console.log("animated");
|
|
||||||
} else {
|
|
||||||
await svg.screenshot({ omitBackground: true, path: out });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 };
|
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 png = new BitmapsGenerator(bitmapsDir);
|
||||||
const browser = await png.getBrowser();
|
const browser = await png.getBrowser();
|
||||||
|
|
||||||
SVG.getStatic().forEach(async (svg) => {
|
for (const svg of SVG.getStatic()) {
|
||||||
const key = `${path.basename(svg, ".svg")}.png`;
|
const key = `${path.basename(svg, ".svg")}.png`;
|
||||||
console.log("Saving", key, "...");
|
|
||||||
const out = path.resolve(bitmapsDir, key);
|
|
||||||
|
|
||||||
let content = fs.readFileSync(svg, "utf-8");
|
let content = fs.readFileSync(svg, "utf-8");
|
||||||
content = SVGHandler.colorSvg(content, color);
|
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();
|
main();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue