纯前端js给表情包gif加文字,你就是“表情包大战”最靓的仔!

前言

表情包大家都用过,很多表情包上会有些辅助文字,来表达更多的内容含义。通过加文字特效,可能只用几个表情包,就能来场"表情包大战"。

有很多网站和工具可以操作gif,包括加文字、压缩、合成、制作表情包等等,但是大多需要登陆、看广告、甚至收费才能用,不如自己做一个。下面就介绍如何通过纯前端js来给gif表情包加文字。

思路是:先用第三方库把gif按帧分解为多个图片,然后再利用canvas插入图片再加文字,最后再利用第三方库把多个canvas合成一个gif文件。

用到的第三方库:

这些第三方库,有的有npm package,有的没有,没有的可以直接在github上下载js文件,比如 omggif.js,然后可以script引用,也可以import url方式引用。

gif 转换成多个 canvas

利用第三方库把gif文件分解为多个图片或canvas。

方案1:omggif

omggif:github.com/deanm/omggi...

omggif 可以逐帧解析gif,没有文档,可以看官方例子

逻辑:先读取gif buffer,然后通过omggif读取每个帧图片数据,然后画在canvas上,然后在每个canvas上添加文字,最后生成canvas list。

js 复制代码
async function gif2canvas(url) {
    var response = await fetch(url);
    var blob = await response.blob();
    var arrayBuffer = await blob.arrayBuffer();
    var intArray = new Uint8Array(arrayBuffer);
    var reader = new GifReader(intArray);
    var info = reader.frameInfo(0);

    return new Array(reader.numFrames()).fill(0).map((_, k) => {
        var image = new ImageData(info.width, info.height);
        reader.decodeAndBlitFrameRGBA(k, image.data);

        var canvas = document.createElement('canvas');
        canvas.width = info.width;
        canvas.height = info.height;
        var context = canvas.getContext('2d');
        context.putImageData(image, 0, 0);

        // draw text
        context.font = `normal 30px sans-serif`;
        context.fillStyle = 'white';
        context.textAlign = 'center';
        context.textBaseline = 'top';
        context.fillText('不错不错!', info.width / 2, info.height - 50);

        return canvas;
    });
}

方案2:libgif

libgif:github.com/buzzfeed/li...

libgif 也可以逐帧解析gif,然后读取每帧图像,可以获取canvas,也可以支持播放、暂停等功能。但是不太稳定,经测试相比omggif能解析成功的gif图片不多,大部分都会抛错

逻辑:跟方案1类似,只是它传入的参数是image dom对象。

js 复制代码
function gif2canvas2(image) {
    return new Promise(resolve => {
        image.setAttribute('rel:animated_src', image.src);
        image.setAttribute('rel:auto_play', '0');
        var rub = new SuperGif({ gif: image, max_width: image.width });
        rub.load(function () {
            var list = [];
            for (var i = 1; i <= rub.get_length(); i++) {
                rub.move_to(i);
                var canvas = rub.get_canvas();

                var newCanvas = document.createElement('canvas');
                newCanvas.width = image.width;
                newCanvas.height = image.height;
                var context = newCanvas.getContext('2d');
                context.drawImage(canvas, 0, 0);

                // draw text
                context.font = `normal 30px sans-serif`;
                context.fillStyle = 'white';
                context.textAlign = 'center';
                context.textBaseline = 'top';
                context.fillText('不错不错!', info.width / 2, info.height - 50);
                list.push(newCanvas);
            }
            resolve(list);
        });
    });
}

多个 canvas 合成 gif 文件

利用第三方库把多个图片或者canvas,合成一个gif文件。

方案1:gif.js

gif.js:github.com/jnordberg/g...

gif.js 可以把多个frame合并成一个gif,且利用web workers提高解析速度。注意还需要把gif.worker.js文件放在站点根路径下,或者用workerScript来自定义路径。

js 复制代码
function canvas2gif(canvasList, { width, height }) {
    return new Promise(resolve => {
        var gif = new GIF({
            workers: 2,
            quality: 10,
            width: width,
            height: height,
            workerScript: '/lib/gif.worker.js',
        });
        canvasList.forEach(canvas => gif.addFrame(canvas, { delay: 100 }));

        gif.on('finished', function (blob) {
            var url = URL.createObjectURL(blob);
            resolve(url);
        });
        gif.render();
    });
}

方案2:gifshot

gifshot:github.com/michael-ben...

gifshotgif.js类似,它还可以合成视频或者多个gif,而且本身还支持添加文字。

用gifshot只支持图片格式转gif,所以需要canvas先转image,再调用api。

js 复制代码
function canvas2gif2(canvasList, { width, height }) {
    return new Promise(resolve => {
        var loadImages = canvasList.map(canvas => {
            var src = canvas.toDataURL('image/jpeg');
            return loadImage(src);
        });

        Promise.all(loadImages).then(images => {
            gifshot.createGIF({
                images,
                width: width,
                height: height,
                gifWidth: width,
                gifHeight: height,
            }, result => {
                if (!result.error) {
                    resolve(result.image);
                }
            });
        });
    });
}
async function loadImage(src) {
    return new Promise((resolve, reject) => {
        var img = document.createElement('img');
        img.src = src;
        img.onload = function () {
            resolve(img);
        };
        img.onerror = function (e) {
            reject(e);
        };
    });
}

整合

html 复制代码
<button id="btn1">click1</button>
<img id="img1" src="/images/gif1.gif">
<div id="canvas-div"></div>
<img id="img-output1">
<img id="img-output2">
js 复制代码
var btn1 = document.getElementById('btn1');
var img1 = document.getElementById('img1');
var imgOutput1 = document.getElementById('img-output1');
var imgOutput2 = document.getElementById('img-output2');
var size = { width: img1.width, height: img1.height };

btn1.onclick = async () => {

    // solution1: omggif
    var canvasList = await gif2canvas(img1.src);

    // solution2: libgif
    // var canvasList = await gif2canvas2(img1);

    // solution1: gif.js
    var url1 = await canvas2gif(canvasList, size);
    imgOutput1.src = url1;

    // solution2: gifshot
    var url2 = await canvas2gif2(canvasList, size);
    imgOutput2.src = url2;
}

效果图:

gif.js方案输出的gif:

结论

本文介绍了如何纯前端给gif加文字,思路就是先转多个图片,然后再用canvas加文字,最后合成gif。

用到的第三方lib:

本文只是介绍了第三方相关库的使用,如果要做成Tools就能做成更多功能。比如在线播放gif,或者分解图片后展示在页面上,然后支持根据帧数来添加文字或特效,也就是支持gif里根据时间来显示或隐藏文字。

最后,毕竟客户端无论能力,还是性能、兼容性都有限,比如上面的库都只支持Web,不支持其它客户端例如小程序。尤其这种文件编码能力,其实在服务端能力更好,能用的库或者服务端的工具都很多,所以这种需求尽量还是放在后端实现。之后文章里也会介绍一些好用的nodejs第三方库,来实现更多图片处理需求。

源码:github.com/markz-demo/...

相关推荐
dlnu20152506221 分钟前
ssr实现方案
前端·javascript·ssr
古木20196 分钟前
前端面试宝典
前端·面试·职场和发展
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀3 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef5 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6415 小时前
【CSS】实现tag选中对钩样式
前端·css·css3