Offline, Browser-Based Implementation: Creating a GIF Image from Scratch with Custom Canvas Frames
GIF images have been a popular format for animation and image sequences on the web for many years. While there are various online tools and software available to create GIFs, it's also possible to create them offline and browser-based using JavaScript. In this article, we will explore a step-by-step guide on how to create a GIF image from scratch using custom Canvas frames. We will leverage three JavaScript plugins - GIFEncoder.js, LZWEncoder.js, and NeuQuant.js - originally obtained from the jsgif GitHub repository by Kevin Kwok, the creator of these plugins.
We will demonstrate an alternative use case of these plugins, showcasing their versatility beyond their primary functionalities. By following this guide, you will learn how to generate GIFs programmatically, customize the appearance of frames, and merge them into a single GIF file. The resulting GIF will be entirely offline and browser-based, allowing you to have full control over the creation process.
Let's dive into the implementation details and code examples to create your own GIF image from scratch using JavaScript and Canvas frames.
Below are the plugins which are used in the code to create an animated gif from images with custom canvas frame:
GIFEncoder.js: GIFEncoder.js is a JavaScript library that provides functionality for encoding image frames into the GIF format. It allows you to create, modify, and encode each frame of the GIF image. This plugin handles the low-level encoding process, including compression and generating the final GIF file.
LZWEncoder.js: LZWEncoder.js is another JavaScript library that implements the Lempel-Ziv-Welch (LZW) compression algorithm. This algorithm is widely used for compressing data in GIF images. LZWEncoder.js works in conjunction with GIFEncoder.js to compress the image data efficiently and reduce the file size of the resulting GIF.
NeuQuant.js: NeuQuant.js is a JavaScript implementation of the NeuQuant algorithm, which is used for quantization and color mapping in GIF images. It analyzes the colors present in the image frames and creates an optimized color palette for efficient color representation. NeuQuant.js helps to ensure that the GIF image produced has accurate and visually pleasing colors.
b64.js: b64.js is a JavaScript library that provides functions for encoding and decoding data in Base64 format. In the context of creating a GIF image, b64.js is used to encode the binary data of the merged frames into Base64 format. This encoded data can then be used to display the GIF image or create a download link.
(Optional) colorbrewer.js: colorbrewer.js is a plugin that provides a collection of pre-defined color palettes for data visualization. It can be used as a reference for selecting colors for the GIF frames. Including colorbrewer.js is optional but can be helpful if you want to base the color selections for the GIF frames on established color palettes.
By including these plugins in your HTML file, you will have the necessary tools and functionalities to create a GIF image from scratch and customize its appearance using JavaScript and Canvas frames.
Steps to Create an Animated GIF
Below we have step by step guide which is to be followed to create an animated gif:
Step 1: Include the necessary JavaScript plugins
First, you need to include the following JavaScript plugins in your HTML file:
<script type="text/javascript" src="LZWEncoder.js"></script><script type="text/javascript" src="NeuQuant.js"></script><script type="text/javascript" src="GIFEncoder.js"></script><script type="text/javascript" src="b64.js"></script>
Additionally, you can optionally include the colorbrewer.js plugin for color reference:
<script type='text/javascript' src='js/colorbrewer.js'></script>
Step 2: Render ColorBrewer for reference
To render all the color palettes present in colorbrewer.js, you can use the following code:
var colorbrewerDisplay = document.getElementById('colorbrewerDisplay');
var allGradientArr = Object.keys(colorbrewer);
var colorbrewerDisplayHtmlStr = '';
for (var colorGradient of allGradientArr) {
colorbrewerDisplayHtmlStr += '<table class="colorGradientTable">';
var colorPaletteObj = colorbrewer[colorGradient];
var combi = Object.keys(colorPaletteObj);
colorbrewerDisplayHtmlStr += `<tr><td colspan='${combi.length}'>
<small><u>${colorGradient}</u></small></td></tr>`;
colorbrewerDisplayHtmlStr += `<tr>`;
for (var noOfColors of combi) {
var allColorsArr = colorPaletteObj[noOfColors];
colorbrewerDisplayHtmlStr += `<th class="blendTH">
<small>${noOfColors}</small><span class="blend"
style="background:${allColorsArr.join('"></span><span
class="blend" style="background:')}"></span></th>`;
colorbrewerDisplayHtmlStr += `<th class="blendTH"><small>
</small><span class="blend">
<small>${allColorsArr.join('</small></span><span
class="blend"><small>')}</small></span></th>`;
}
colorbrewerDisplayHtmlStr += '</tr>';
colorbrewerDisplayHtmlStr += '</table>';
}
colorbrewerDisplay.innerHTML = colorbrewerDisplayHtmlStr;
This code renders all the color palettes present in colorbrewer.js and displays them in an HTML DOM element with the id "colorbrewerDisplay".
Step 3: Create a HTML Canvas element
To initialize a <canvas></canvas> element in JavaScript, use the following code:
const canvas = document.createElement('canvas');
const w = 150;
const h = 150;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
This code creates a canvas element with a width and height of 150 pixels.
Step 4: Instantiate GIF
Encoder instance and define variables Instantiate a GIFEncoder instance and define other necessary variables as follows:
const encoder = new GIFEncoder(w, h);
encoder.setRepeat(0);
encoder.setDelay(500); // in milliseconds
encoder.start();
const noOfFrames = 4;
const colorPalette = ['#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0'];
const baseColor = '#023858';
const slice = h / noOfFrames;
const squareBackground = () => {
ctx.fillStyle = baseColor;
ctx.fillRect(0, 0, w, h);
};
const generateSquareFrame = (frameIndex) => {
squareBackground();
ctx.fillStyle = colorPalette[frameIndex];
ctx.fillRect(slice * frameIndex, 0, slice, h);
encoder.addFrame(ctx);
};
This code sets up the GIFEncoder instance with the specified width, height, repeat option, and delay between frames. It also defines the number of frames, color palette, base color, and functions for generating each frame.
Step 5: Generate and merge all frames to create GIF output
To generate and merge all the frames into a GIF output, use the following code:
for (let f = 0; f < noOfFrames; f++) {
generateSquareFrame(f);
}
encoder.finish();
This code generates each frame by calling the generateSquareFrame function in a loop. Finally, it calls encoder.finish() to complete the encoding process.
Step 6: Extract the merged frames and display the output GIF
To extract the merged frames as a GIF file and display it, use the following code:
var fileType = 'image/gif';
var readableStream = encoder.stream();
var binary_gif = readableStream.getData();
var b64Str = 'data:' + fileType + ';base64,' + encode64(binary_gif);
// Display the output GIF
document.getElementById('outputGif').src = b64Str;
This code converts the merged frames into Base64 format and assigns the resulting URL to the src attribute of an HTML <img> element with the id "outputGif".
Step 7: Create a download link for the GIF file
To create a download link for the GIF file, use the following code:
let dwnlnk = document.createElement('a');
dwnlnk.download = fileName;
dwnlnk.innerHTML = `💾 <small>Save</small>`;
dwnlnk.className = 'btn btn-outline-dark';
dwnlnk.href = b64Str;
// Append the download link to the document
document.body.appendChild(dwnlnk);
This code creates an <a> element with the necessary attributes for downloading the GIF file. It sets the download attribute to the desired file name, adds a download icon and label to the link, and sets the href attribute to the Base64 data URL.
Here is the full code to create an animated GIF
Here's the updated code with that addition:
<!DOCTYPE html>
<html>
<head>
<title>Creating GIF from Custom Canvas Frames</title>
</head>
<body>
<div id="colorbrewerDisplay"></div>
<canvas id="canvas"></canvas>
<img id="outputGif" alt="Output GIF" />
<script type="text/javascript" src="LZWEncoder.js"></script>
<script type="text/javascript" src="NeuQuant.js"></script>
<script type="text/javascript" src="GIFEncoder.js"></script>
<script type="text/javascript" src="b64.js"></script>
<script type="text/javascript" src="js/colorbrewer.js"></script>
<script type="text/javascript">
// Step 1: Render ColorBrewer for reference
var colorbrewerDisplay = document.getElementById('colorbrewerDisplay');
var allGradientArr = Object.keys(colorbrewer);
var colorbrewerDisplayHtmlStr = '';
for (var colorGradient of allGradientArr) {
colorbrewerDisplayHtmlStr += '<table class="colorGradientTable">';
var colorPaletteObj = colorbrewer[colorGradient];
var combi = Object.keys(colorPaletteObj);
colorbrewerDisplayHtmlStr += `<tr><td colspan='${combi.length}'><small><u>${colorGradient}</u></small></td></tr>`;
colorbrewerDisplayHtmlStr += `<tr>`;
for (var noOfColors of combi) {
var allColorsArr = colorPaletteObj[noOfColors];
colorbrewerDisplayHtmlStr += `<th class="blendTH"><small>${noOfColors}</small><span class="blend"style="background:${allColorsArr.join('"></span><span class="blend" style="background:')}"></span></th>`;
colorbrewerDisplayHtmlStr += `<th class="blendTH"><small> </small><span class="blend"><small>${allColorsArr.join('</small></span><span class="blend"><small>')}</small></span></th>`;
}
colorbrewerDisplayHtmlStr += '</tr>';
colorbrewerDisplayHtmlStr += '</table>';
}
colorbrewerDisplay.innerHTML = colorbrewerDisplayHtmlStr;
// Step 2: Create a HTML Canvas element
const canvas = document.getElementById('canvas');
const w = 150;
const h = 150;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Step 3: Instantiate GIFEncoder instance and define variables
const encoder = new GIFEncoder(w, h);
encoder.setRepeat(0);
encoder.setDelay(500); // in milliseconds
encoder.start();
const noOfFrames = 4;
const colorPalette = ['#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0'];
const baseColor = '#023858';
// Set the color palette for the encoder
const nq = new NeuQuant(colorPalette, 10);
encoder.setGlobalPalette(nq.indexedPixels, nq.colorMap.length);
const slice = h / noOfFrames;
const squareBackground = () => {
ctx.fillStyle = baseColor;
ctx.fillRect(0, 0, w, h);
};
const generateSquareFrame = (frameIndex) => {
squareBackground();
ctx.fillStyle = colorPalette[frameIndex];
ctx.fillRect(slice * frameIndex, 0, slice, h);
encoder.addFrame(ctx);
};
// Step 4: Generate & Merge all frames to create GIF output
for (let f = 0; f < noOfFrames; f++) {
generateSquareFrame(f);
}
encoder.finish();
// Step 5: Extract the merged frames and display the output GIF
var fileType = 'image/gif';
var readableStream = encoder.stream();
var binary_gif = readableStream.getData();
var b64Str = 'data:' + fileType + ';base64,' + encode64(binary_gif);
document.getElementById('outputGif').src = b64Str;
// Step 6: Create a download link for the GIF file
let dwnlnk = document.createElement('a');
dwnlnk.download = 'output.gif';
dwnlnk.innerHTML = `💾 <small>Save</small>`;
dwnlnk.className = 'btn btn-outline-dark';
dwnlnk.href = b64Str;
document.body.appendChild(dwnlnk);
</script>
</body>
</html>
Save the code as an HTML file and open it in a web browser. It should now properly display the output GIF and provide a download link for the file.
That's it! You have now recreated the article's implementation for creating a GIF image from scratch using custom Canvas frames.
Comments