起因
某天晚上我在某牙看直播时,发现他们新推了一个表情包还挺逗,大概是这样的
于是我突发奇想做一个表情包,这样的
由于PS不支持APNG的帧动画拆分,所以我只能自己动手了 👨💻
简单介绍一下APNG
GIF:
- 最多支持 8 位 256 色,色阶过渡糟糕,图片具有颗粒感
- 不支持 Alpha 透明通道,边缘有杂边
APNG:
- 支持 24 位真彩色图片
- 支持 8 位 Alpha 透明通道
- 向下兼容 PNG
GIF的动画每帧都是完整图片
APNG会通过算法计算帧之间的差异,只存储帧之前的差异,而不是存储全帧。
APNG兼容性
apng-js
Github:https://github.com/davidmz/apng-js
apng-js 是一个拆分APNG文件并且提供播放能力的一个库,由于我想做一个拆分APNG动画帧然后生成ZIP文件下载的工具库,所以我抽取了一些核心代码重新实现了一个拆分APNG生成图片的功能。(源码)
因为APNG后面的图片只有差异帧,所以会导致图片大小不一,在canvas上重新绘制一遍,统一所有图片大小。这里只帖部分代码,感兴趣的可以点击源码查看。
createImage(width, height) {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(this.imageData);
const img = document.createElement('img');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext("2d");
/* 计算屏幕分辨率 */
const resolution = this.getScreenResolution(ctx);
canvas.width = width * resolution;
canvas.height = height * resolution;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
ctx.scale(resolution, resolution);
img.onload = () => {
URL.revokeObjectURL(url);
ctx.drawImage(img, this.left, this.top);
this.canvas = canvas;
resolve(canvas);
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error("Image creation error"));
};
img.src = url;
});
}
HTMLCanvasElement.toBlob
HTMLCanvasElement.toBlob() 方法创造Blob对象,用以展示canvas上的图片;这个图片文件可以被缓存或保存到本地,由用户代理端自行决定。如不特别指明,图片的类型默认为 image/png,分辨率为96dpi。
HTMLCanvasElement.toBlob 可以生成一个Blob对象,后续生成文件的时候就可以使用这个方法。
jszip
Github:https://github.com/Stuk/jszip
jszip是一个生成zip的库,使用方式也很简单。作者也提供了普通版本以及min版本的js,都在dist目录下。我这边简单封装了一下生成图片ZIP然后下载的方法。
ZIPjs.images = (fileName, dataSource: imagesZipProps[]) => {
var zip = new window.JSZip();
var img = zip.folder(fileName);
dataSource.forEach(({ name, imgData }) => {
img.file(name, imgData, { base64: true });
});
// 生成zip文件并下载
zip.generateAsync({
type: 'blob'
}).then(function (content) {
// 创建隐藏的可下载链接
var eleLink = document.createElement('a');
eleLink.download = fileName + '.zip';
eleLink.style.display = 'none';
// 下载内容转变成blob地址
eleLink.href = URL.createObjectURL(content);
// 触发点击
document.body.appendChild(eleLink);
eleLink.click();
// 然后移除
document.body.removeChild(eleLink);
});
}
在线体验地址:柚子青年 - 工具