纯前端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/...

相关推荐
咖啡の猫32 分钟前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲3 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5813 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路4 小时前
GeoTools 读取影像元数据
前端
ssshooter4 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友4 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry5 小时前
Jetpack Compose 中的状态
前端
dae bal6 小时前
关于RSA和AES加密
前端·vue.js
柳杉6 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog6 小时前
低端设备加载webp ANR
前端·算法