I am trying to merge multiple frames from a GIF to convert them into spritesheet. I am somehow able to extract frames using libgif.js here is my code. The Canvas in which i want to put all my images is empty and is at the end i dont know what is wrong with it.
<img hidden src="https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif" rel:auto_play="0"
rel:rubbable="0" />
<div id="frames">
</div>
<canvas id="myCanvas" style="border:1px solid #d3d3d3;">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://unpkg.com/jsgif@1.0.2/libgif.js"></script>
<script>
$(document).ready(function () {
$('img').each(function (idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({ gif: img_tag, progressbar_height: 0 });
rub.load(function () {
for (var i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
var canvas = cloneCanvas(rub.get_canvas());
$("#frames").append('<img id = "' + i + '"src= "' + canvas + '">');
}
for (var i = 0; i <= total; i++) {
id = i.toString();
var img = document.getElementById(id);
window.onload = function () {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.drawImage(img, 10, 10);
}
}
});
}
});
});
function cloneCanvas(oldCanvas) {
var img = oldCanvas.toDataURL("image/png");
return img;
}
</script>
The main issue in your solution is that you are looping through all inserted frame-images and using window.onload
with the expectation to draw images on the canvas. However, you are assigning windows.onload
on the image elements after the images have already been attached to the dom in the previous (for i = 0)
iteration, through $("#frames").append(...
. This means that your onload
handlers are getting registered after the event has already fired.
I rearranged your code into the snippet below to make sure that onload
is registered before appending the image-frames into the document.
In this solution here I created the "spritesheet" by putting the image-frames vertically one-after-the-other. Tweak the solution accordingly to create your desired "spritesheet layout".
The tricky part is that you need to set a width
and height
for the canvas in a dynamic fashion, which means you have to wait for all image-frames to be loaded
into the document and meassure their collective widths and heights.
The algorithm should be:
onload
canvas.width
and canvas.height
depending on
your desired layout (in my vertical-one-ofter-the-other solution
this means to use the maximal image width as canvas width and the
sum of all image heights as height. If you need a different
spritesheet layout then you need to adapt this logic differently)In my solution I did not implement this algorith, I rather hard-coded the canvas width and height after manually calculating the max-img-width and total-img-height for your particular gif example.
$(document).ready(function () {
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
let canvasWidth = 0;
let canvasHeight = 0;
$('img').each(function (idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({ gif: img_tag, progressbar_height: 0 });
rub.load(function () {
for (let i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
var canvas = cloneCanvas(rub.get_canvas());
const img = $('<img id = "' + i + '"src= "' + canvas + '">');
img.on('load', function() {
// draw the image
ctx.drawImage(this, 0, canvasHeight);
// extend canvas width, if newly loaded image exceeds current width
canvasWidth = $(this).width() > canvasWidth ? $(this).width() : canvasWidth;
// add canvas height by the newly loade image height value
canvasHeight += $(this).height();
// resize sprite-canvas to host the new image
canvas.width = canvasWidth;
canvas.height = canvasHeight;
});
$("#frames").append(img);
}
});
}
});
});
function cloneCanvas(oldCanvas) {
var img = oldCanvas.toDataURL("image/png");
return img;
}
Gif:
<img hidden src="https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif" rel:auto_play="0"
rel:rubbable="0" />
<hr />
Frames:
<div id="frames">
</div>
<hr />
Canvas:
<hr />
<canvas id="myCanvas" style="border:1px solid #d3d3d3;" width="400" height="17600">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://unpkg.com/jsgif@1.0.2/libgif.js"></script>