前言
公司刚好有个需求需要用 web 加载一个 zip 的 lottie 的配置文件,文件的内容解压后如下

搜索了一下发现网上没有加载的方案就研究了一下,这里记录一下
方案
老哥们都很忙直接上方案 DEMO
See the Pen
Untitled by aizigao (@aizigao)
on CodePen.
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
const unZipLottieFile = async (zipSrc) => {
const zipBuffer = await fetch(zipSrc).then((response) => response.arrayBuffer() );
const zip = await JSZip.loadAsync(zipBuffer);
console.log("[zip]", zip);
const imageUrlsMap = {}; let oriJson = {};
for (let zipEntry of Object.values(zip.files)) { if (zipEntry.dir || /\/\./.test(zipEntry.name)) { continue; }
const isJSON = /\.json/i.test(zipEntry.name); const isImg = /\.(jpg|jpeg|png|gif)$/i.test(zipEntry.name);
if (isJSON) { const oriJsonText = await zipEntry.async("text"); if (oriJsonText) { oriJson = JSON.parse(oriJsonText); } } else if (isImg) { const imgBlob = await zipEntry.async("blob"); const fileName = zipEntry.name.split("/").pop(); imageUrlsMap[fileName] = URL.createObjectURL(new Blob([imgBlob])); } }
function deepUpdateImgPath(obj) { if (typeof obj !== "object" || obj === null) { return obj; }
for (const key in obj) { if (obj.hasOwnProperty(key)) { if (key === "p") { if (imageUrlsMap[obj[key]]) { const filePath = imageUrlsMap[obj[key]]; obj[key] = filePath.split("/").pop(); } } else { obj[key] = deepUpdateImgPath(obj[key]); } } }
return obj; }
const targetJSON = deepUpdateImgPath(oriJson); return { json: targetJSON, imageUrlsMap }; };
const loadZipAnimation = async (src) => { const { json } = await unZipLottieFile(src); console.log(json); lottie.loadAnimation({ container: document.getElementById("anim"), renderer: "svg", loop: true, autoplay: true, assetsPath: `blob:${location.origin}/`, animationData: json }); };
const remoteZipFile = "https://aizigao.xyz/files/lottie-test.zip"; loadZipAnimation(remoteZipFile);
|
过程与解析
- 首先我搜索了下 android 端的加载方案是先下载到本地,然后本地解压在加载
掘金 id 慢笑 的博文
参考这里 https://juejin.cn/post/6844904116309721095#heading-5
-
web 解压可以用 jsZip 处理,但解压后没法直接引用本地文件,解压也需要用户确认后下载到本地,本想放弃,但突然想到应该用 blob://
的临时 URL 路径应该就可以处理了
-
使用 jsZip 解压后, 可以看到 jsZip 解压后的内容

用 .
开头的隐藏文件和文件架是不用处理, 关键的是 json 文件, 可以看到的是 u
是文件目录, p
是文件名

- 现在的目标就是替换 p 的内容为 线上的地址,然后加载 json 就可以了
- 线上的地址可以直接用
URL.createObjectURL
生产一个临时的,可以生产一个临时的 blob:<location>/<uuid>
的地址,页面关闭后会立即销毁
1 2 3 4 5
| const imgBlob = await zipEntry.async("blob"); const fileName = zipEntry.name.split("/").pop();
imageUrlsMap[fileName] = URL.createObjectURL(new Blob([imgBlob]));
|
- 已递归的方式替换 JSON 内
p
属性即可, 这里需要注意的是 使用 lottie.loadAnimation
使用了 assetsPath
, 所以 JSON 对象的的数据内 p
值仅取生产临时链接的 uuid 值即可
1 2 3 4 5 6 7 8 9
| lottie.loadAnimation({ container: document.getElementById("anim"), renderer: "svg", loop: true, autoplay: true, assetsPath: `blob:${location.origin}/`, animationData: json });
|