async function run() {
const spriteSheetURL = document.getElementById('input').value;
const originalWidth = Number(document.getElementById('originalWidth').value);
const originalHeight = Number(document.getElementById('originalHeight').value) || originalWidth;
const width = Number(document.getElementById('width').value);
const height = Number(document.getElementById('height').value) || width;
const fps = Number(document.getElementById('fps').value) || 10;
const sprites = await makeCanvases(spriteSheetURL, originalWidth, originalHeight, width, height);
const blob = await renderGif(sprites, fps);
const url = URL.createObjectURL(blob);
const img = document.createElement('img');
img.setAttribute('src', url);
img.className = 'mb-2';
const btn = document.createElement('button');
btn.innerHTML = '<i class="far fa-save mr-1"></i>Save';
btn.className = 'button is-small';
btn.onclick = () => {
const a = document.createElement('a');
a.href = url;
a.download = `${url.slice(url.lastIndexOf('/') + 1)}.gif`;
a.click();
};
const item = document.createElement('div');
const removeBtn = document.createElement('button');
removeBtn.className = 'absolute';
removeBtn.style.top = 0;
removeBtn.style.right = 0;
removeBtn.innerHTML = '<i class="far fa-times-circle text-gray-500"></i>'
removeBtn.onclick = () => {
document.body.removeChild(item);
URL.revokeObjectURL(url)
};
item.className = 'inline-block text-center relative';
item.appendChild(img)
item.appendChild(btn);
item.appendChild(removeBtn);
document.body.appendChild(item);
}
async function renderGif(frames, fps) {
const workerScript = await loadWorkerScript();
const gif = new GIF({
workers: 2,
workerScript,
quality: 1,
});
frames.forEach(frame => gif.addFrame(frame, {
delay: 1000 / fps,
}));
return new Promise((resolve, reject) => {
gif.on('finished', resolve);
try {
gif.render();
} catch(e) {
reject(e);
}
});
}
async function makeCanvases(spriteSheetUrl, originalWidth, originalHeight, canvasWidth, canvasHeight) {
const spriteSheet = await loadImage(spriteSheetUrl);
spriteSheet.crossOrigin = '';
let x = 0;
let y = 0;
let sprites = [];
while (y < spriteSheet.height) {
x = 0;
while (x < spriteSheet.width) {
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(spriteSheet, x, y, originalWidth, originalHeight, 0, 0, canvasWidth, canvasHeight);
const isEmpty = ctx.getImageData(0, 0, canvasWidth, canvasHeight).data.every(channel => channel === 0);
if (!isEmpty) {
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(spriteSheet, x, y, originalWidth, originalHeight, 0, 0, canvasWidth, canvasHeight);
sprites.push(canvas);
}
x += originalWidth;
}
y += originalHeight;
}
return sprites;
}
function loadImage(imgUrl) {
const img = new Image();
img.crossOrigin = 'Anonymous';
return new Promise((resolve, reject) => {
img.onload = function() {
resolve (this);
};
img.error = reject;
img.src = imgUrl;
})
}
async function loadWorkerScript() {
const { data } = await axios.get('https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.worker.js', {
responseType: 'blob',
});
const content = await data.text();
const blob = new Blob([content], {
type: 'application/javascript',
});
return URL.createObjectURL(blob);
}
document.getElementById('run').onclick = run;
View Compiled