解析出来每一帧的数据结构
{ // The color table lookup index for each pixel
pixels: [...], // the dimensions of the gif frame (see disposal method)
dims: { top: 0, left: 10, width: 100, height: 50
}, // the time in milliseconds that this frame should be shown
delay: 50, // the disposal method (see below)
disposalType: 1, // an array of colors that the pixel data points to
colorTable: [...], // An optional color index that represents transparency (see below)
transparentIndex: 33, // Uint8ClampedArray color converted patch information for drawing
patch: [...]
}
patch 表示当前帧图,数据格式是 Uint8ClampedArray
,可以使用 putImageData
在 canvas 渲染。
Automatic Patch Generation:If the buildPatch param of the dcompressFrames() function is true, the parser will not only return the parsed and decompressed gif frames, but will also create canvas ready Uint8ClampedArray arrays of each gif frame image, so that they can easily be drawn using ctx.putImageData() for example. This requirement is common, however it was made optional because it makes assumptions about transparency. The demo makes use of this option.
ctx.putImageData( newImageData(patch, dims.width, dims.height),
dims.left,
dims.top);
const gif = newGIF({ workers: 2, quality: 10 });
gif.addFrame(imageElement, { delay: 50 });
gif.on('finished', (blob) => { window.open(URL.createObjectURL(blob));
});
gif.render();
此插件没有 npm 包,只能将文件拷贝到项目中,通过 script 标签引用。
假设生成的 GIF 时长 1200ms,GIFb 正常结束,GIFa 丢失 200ms。
平均值:(1000 + 1200) / 2 = 1100
ms
GIFa 增加 100ms,假设总共 20 帧,单帧时长 50ms。则每帧增加 100 / 20 = 5
ms,为 55ms。
GIFb 减少 100ms,假设总共 24 帧,单帧时长 50ms。则每帧减少 100 / 24 = 4
ms,为 46ms。
生成的 GIF 时长 3200ms,GIFb 正常结束,GIFa 轮播 2 次,第 3 次播放 800ms,丢失 400ms。
functioncomputeTotalTime(min: number, max: number) { const div = max / min; const decimal = div % 1; const multiple = decimal > 0.5 ? Math.ceil(div) : Math.floor(div); return min * multiple;
}
合理倍数关系,GIFa 与 GIFb 的合理倍数应该是 2 还是 3?
如果是 2,则二者差距是 3200 - (1200 * 2) = 800
ms;
如果是 3,则二者差距是 (1200 * 3) - 3200 = 400
ms;
差距越小越好,所以合理倍数关系为 3,总时长为 3600ms。
// 调整多张 gif 图的数据,适配总时长functionadaptGifFrames(gifsData: GifData[], time: number) { // 适配时长
gifsData.forEach((gifData) => { const { totalTime, frames } = gifData; // 目标时长与当前 gif 图时长的倍数关系
const multiple = Math.floor(time / totalTime); // 目标时长与当前 gif 图时长的差值
let diff = time - totalTime; if (multiple !== 0) {
diff = (time - multiple * totalTime) / multiple;
} if (diff === 0) return;
frames.forEach((frame) => { // 依据每一帧占比增加时长
const precent = frame.delay / totalTime;
frame.delay += diff * precent;
}); // 设置适配后的总时长
gifData.totalTime = Math.round(
frames.reduce((pre, cur) => pre + cur.delay, 0)
);
});
}
理想情况下,每帧延时时长为 50ms,但所使用的 GIF 图来源不一,无法保证其一致性。另外总时长越长,帧数就越多,体积就越大,GIF 图生成时间就越长。 所以限制帧数最大值为 100,如果超过 100,则每帧增加相应时长。
functioncomputeGifData(gifsData: GifData[]) { const times = gifsData.map((gif) => gif.totalTime); const minTime = Math.min(...times); const maxTime = Math.max(...times); const div = maxTime / minTime; let totalTime = 0; if (div > 2) { // 计算最大时长
totalTime = computeTotalTime(minTime, maxTime);
} else { // 取平均时长
totalTime = times.reduce((pre, cur) => pre + cur, 0) / times.length;
} let delay = DEFAULT_GIF_DELAY; let frameLen = totalTime / delay; // 帧数
if (frameLen > MAX_GIF_FRAME) {
delay = delay * (frameLen / MAX_GIF_FRAME);
frameLen = MAX_GIF_FRAME;
} return { totalTime, frameLen, delay };
}
根据帧数 frameLen 循环获取每一帧的图片,延时时长为 delay。
gif.addFrame(image, { delay });
中文字体文件体积大多以 M 为单位,导致每帧图片的 svg 体积较大,继而导致每帧图片加载耗时较长。
上图 3000ms ~ 10000ms,近 7s 时间都是在加载每帧的图片。 百度前端团队有个 NodeJS 工具 fontmin,可以按照指定文字内容对字体文件进行裁剪,返回仅包含指定文字内容的字体文件,可以极大的减少字体体积。 使用这个工具后,GIF 图生成速度得到显著提升。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8