人生就像一场马拉松比赛,你永远不知道你要跑多久才能到达终点。常年写文章,文章里常常都会用到GIF动图来演示代码运行的效果图,越是容易获得的东西越不容易被重视,习惯于打开GIF应用来录制动图的我,突发奇想,能否自己开发一个在线的动图生成工具呢。说干就干,要干就干的漂亮。在本文中,我将向您展示如何使用Canvas和gif.js库创建自己的GIF动图生成工具。
一、工具拥有的能力
在本文中,我们将创建一个GIF动图生成工具,该工具可以让用户在Canvas上绘制动画,并将序列化的帧转换为GIF图像。该工具具有以下功能:
可以选择
多页面成画
或
一次成画
模式
一次成画
模式下能够记录用户在画布上绘制的所有笔画
多页面成画
模式下可以将笔画添加到动画列表中,并生成GIF动画
可以下载生成的GIF文件
可以调整画笔大小和颜色
可以上传图片作为动图元素
二、演示效果
1. 工具使用方法
进入工具会提示选择绘制动图模式有两种:’
多页面成画
‘ : ‘
一次成画
‘
😀
一次成画
:指一个画布上连续绘制,每一笔绘制的过程都会录制动图。
😀
多页面成画是
:指每次绘制完一个页面需要切换下一个页面绘制下一帧,不会记录笔画。
页面左侧“动图列表展示每一帧动图”只有选择了多页面成画,才会展示。
多页面成画模式下,要将画布上绘制的内容添加到动图列表,然后点击生成,才会生成动图,并在页面下方展示。
在
一次成画
模式下,直接在画布上绘制,画完后,直接点击生成GIF按钮。
其他用法可以通过使用过程中,自行摸索,例如画笔大小,上传图片,画笔颜色等。
2. 运行效果
体验链接:
GIF动画在线生成工具 (forrestyuan.github.io)
代码仓库:
github.com/forrestyuan…
—–多页面模式—–
—-一次成画模式—-
三、所用技术
在本文中,我们主要使用了以下技术来搭建我们的GIF动图生成工具:
gif.js:
一个基于JavaScript的库,可用于在Web应用程序中创建GIF动画。它允许您使用Canvas API绘制动画,并将序列化的帧转换为GIF图像。
gif.worker.js:gif.js库的一个Web Worker,可在后台处理GIF编码,从而确保浏览器最佳性能。
lodash.js:
一个JavaScript实用工具库,提供了许多实用的函数。它包含了很多有用的功能,例如数组操作、对象操作、函数操作、字符串操作等等。
四、核心代码实现
完整代码可以到我的GitHub仓库阅读哈!
点我去代码仓库,阅读完整源码
1. 创建gif.js实例
为了使用gif.js库来创建GIF动画,我们需要创建一个
gif.js
实例。我们可以使用以下代码创建一个实例:
const gif = new GIF({
workers: 2,
quality: 5,
debug: true,
width: canvas.width,
height: canvas.height
这段代码使用gif.js
库创建了一个GIF实例对象。其中,workers
属性指定了两个Web Worker,quality
属性指定了GIF的质量级别,width
和height
属性指定了GIF图像的宽度和高度。我们可以使用这个实例对象来添加帧并生成GIF动画。
2. 将帧添加到GIF动画中
要将帧添加到GIF动画中,我们需要将Canvas上的每一个像素数据复制一份保存到GIF中,gif.js
提供不止一种方法,我在开发的过程中,每种方法都去尝试了,尝试来尝试去,终究还是直接利用画布的像素数据拷贝最方便快捷,不过,这也跟需求有关,如果不是基于canvas来开发,那就另当别论了。
gif.addFrame(ctx, { copy: true, delay: 200 })
这段代码使用了gif.js
库中的addFrame()
方法将帧添加到了GIF动画中。其中,ctx
参数是Canvas上下文,delay
参数是帧之间的延迟时间(以毫秒为单位)。copy
参数指定是否将Canvas缓冲区复制到新的帧中。如果设置为false
,则只保存Canvas上下文的引用。这意味着,如果在添加帧时更改了Canvas上下文,则这些更改将反映在所有帧中。如果设置为true
,则会将Canvas缓冲区复制到新的帧中,从而确保每个帧都是独立的。
3. 生成GIF动画
当我们添加了所有的帧后,我们可以使用以下代码来生成GIF动画,首先,我们在try
块中调用gif.render()
方法来生成GIF动画。一旦生成完成,gif.on("finished")
方法将被调用。在这里,我们创建一个<img>
元素和一个下载链接<a>
元素,并将其添加到页面上。最后,我们调用reset()
方法来重置应用程序的状态。
function generateGif() {
try {
LoadingModal.style.display = 'flex';
gif.on('finished', function (blob) {
LoadingModal.style.display = 'none';
const img = document.createElement('img');
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
img.src = url;
link.href = url;
link.textContent = '点击下载动图'
link.download = 'myGif.gif';
link.appendChild(img)
gifResListBox.appendChild(link);
reset();
gif.render();
} catch (error) {
LoadingModal.style.display = 'none';
4. 生成GIF帧列表
GIF帧列表,只有在多页面成画
模式下才有作用,这个时候,一个画布内容为一帧,通过点击“添加到动图中”按钮调用gif.addFrame
方法保存帧。然后,使用forEach()方法遍历gif.frames
中的每一帧。对于每一帧,我们创建一个与GIF图像相同大小的ImageData对象,并将其传递给Canvas上下文的putImageData()
方法。创建一个新的Canvas元素,并将其添加到绘制列表中。最后将ImageData
对象绘制到新的Canvas元素中。
drawListBox.innerHTML = '';
gif.frames.forEach((frame, index) => {
let w = gif.options.width;
let h = gif.options.height;
const imageData = new ImageData(frame.data, w, h);
const tempCavnas = document.createElement('canvas');
tempCavnas.width = w;
tempCavnas.height = h;
tempCavnas.style.cssText = 'width:100px;height:100px';
const ctx = tempCavnas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
drawListBox.appendChild(tempCavnas);
5. 画布内容绘制
在画布上绘制内容,监听三个事件mousedown
、mousemove
、mouseup
,可谓是三剑客,在mousedown
事件中调用beginPath()
方法来开始新的路径,并使用moveTo()
方法将路径的起点移动到鼠标的位置。将isDrawing
变量设置为true
,表示用户正在绘制
在mousemove
事件中检查当前是否正在绘制。如果不是,则返回。如果正在绘制,则我们设置画笔的线宽和颜色,并使用lineTo()
方法将路径移动到鼠标的当前位置,然后用stroke()
方法来绘制路径。
当绘制模式为once
,则将当前帧添加到动画列表中。最后,在mouseup
事件中将isDrawing
变量设置为false
,表示绘制已完成。
canvas.addEventListener('mousedown', (event) => {
ctx.beginPath();
ctx.moveTo(event.offsetX, event.offsetY);
isDrawing = true;
canvas.addEventListener('mousemove', (event) => {
if (!isDrawing) return;
ctx.lineWidth = brushSizeSelector.value;
ctx.strokeStyle = colorSelector.value;
ctx.lineTo(event.offsetX, event.offsetY)
ctx.stroke();
if (drawMode === 'once') {
addFunc()
canvas.addEventListener('mouseup', () => {
isDrawing = false;
在多页面成画
模式下绘制新的帧,需要清空Canvas元素。
context.clearRect(0, 0, canvas.width, canvas.height);
6. 上传图片作为动图元素
为了上传图片,我们可以使用以下代码:
function handleImageUpload(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function (event) {
const img = new Image();
img.src = event.target.result;
img.onload = function () {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
reader.readAsDataURL(file);
在这里,我们使用FileReader
对象来读取上传的文件,并将其转换为Base64图像。然后,我们创建一个新的Image
对象,并在图像加载完成后将其绘制到Canvas元素上。
五、有待提升的点
至此,我们一起创建了一个简单的GIF动图生成工具。然而,还有许多可以改进的地方。
可以写出更好看的UI页面。
添加橡皮擦工具,或者回退、前进的功能。
在多页面成画模式下,可以通过点击左侧GIF帧列表的某一帧,进行编辑。
可以调用摄像头拍照来放到canvas中进行帧创作
添加文字文案特效
表情包功能
动画特效,例如爆照效果等。
这里只是列了一些更有趣的功能,一千个读者一千个哈姆雷特,相信大家有更有趣的实现功能,出于时间有限,和知识为了带大家知道怎么去开发一个GIF生成工具,本人并没有花太多精力去开发完这些功能,有兴趣的小伙伴,到我的github上**fork**
我的代码过去,修改,完善,弄出新鲜玩意来。
六、写在文末
本文主要介绍了如何使用gif.js和lodash.js库来开发一个基于canvas的GIF动图生成工具。文中详细地介绍了如何使用这些库来创建GIF实例、将帧添加到GIF动画中、生成GIF动画、生成GIF帧列表、上传图片作为动图元素等。同时,还介绍了一些有待提升的点,例如UI页面更美观、添加橡皮擦工具、在多页面成画模式下编辑GIF帧等。欢迎有兴趣的小伙伴到作者的github上fork代码并进行修改完善。
本文正在参加「金石计划」
原文链接:https://juejin.cn/post/7221541217048625208 作者:forrest酱