mirror of
https://github.com/vibegif/vibegif.lol.git
synced 2026-04-07 02:12:12 +00:00
Refactor: Extract GIF assembler
This commit is contained in:
60
src/services/gif.js
Normal file
60
src/services/gif.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
const WORKER_CDN = 'https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.worker.js';
|
||||||
|
let workerBlobUrl = null;
|
||||||
|
|
||||||
|
async function getWorkerUrl() {
|
||||||
|
if (workerBlobUrl) return workerBlobUrl;
|
||||||
|
const res = await fetch(WORKER_CDN);
|
||||||
|
const blob = await res.blob();
|
||||||
|
workerBlobUrl = URL.createObjectURL(blob);
|
||||||
|
return workerBlobUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadImage(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = () => reject(new Error('Failed to load frame image'));
|
||||||
|
img.src = src;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function assembleGif(frames, fps) {
|
||||||
|
if (!frames?.length) throw new Error('No frames to assemble.');
|
||||||
|
if (!window.GIF) throw new Error('GIF.js not loaded.');
|
||||||
|
|
||||||
|
const workerScript = await getWorkerUrl();
|
||||||
|
const images = await Promise.all(frames.map(loadImage));
|
||||||
|
|
||||||
|
const w = images[0].naturalWidth;
|
||||||
|
const h = images[0].naturalHeight;
|
||||||
|
const delay = Math.round(1000 / fps);
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) throw new Error('Could not create 2D canvas context.');
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const gif = new window.GIF({
|
||||||
|
workers: 2,
|
||||||
|
quality: 10,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
workerScript,
|
||||||
|
repeat: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const image of images) {
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.fillRect(0, 0, w, h);
|
||||||
|
ctx.drawImage(image, 0, 0, w, h);
|
||||||
|
gif.addFrame(ctx, { copy: true, delay });
|
||||||
|
}
|
||||||
|
|
||||||
|
gif.on('finished', (blob) => resolve(blob));
|
||||||
|
gif.on('error', (err) => reject(err));
|
||||||
|
gif.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user