前言
前几天隔壁组的同事问我们 leader 做没做过序列帧动画,我亲爱的 leader 直接把我推了出去:“寒草会!”,事后给我发了一张她俩对话的截图,并对我表示信任。
我一脸懵逼,心想:“诶?我没做过啊!”。
随后我开始了谷歌生涯,搜了一搜,随后便开始了我的序列帧动画编码之路。
实现
animation
我最开始办法肯定还是用 animation 做了一个 demo。
其中注意 animation 中 steps 这个属性,帧动画也是靠它实现。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> @keyframes demo { 0% { background-position: 0px 0; } 10% { background-position: -20px 0; } 20% { background-position: -40px 0; } 30% { background-position: -60px 0; } 40% { background-position: -80px 0; } 50% { background-position: -100px 0; } 60% { background-position: -120px 0; } 70% { background-position: -140px 0; } 80% { background-position: -160px 0; } 90% { background-position: -180px 0; } 100% {} } .animation { background-image: url('a.jpeg'); background-repeat: no-repeat; height: 200px; width: 200px; position: absolute; top: 200px; left: 300px; border-radius: 50%; -webkit-animation: demo 1s steps(1, end) infinite; } </style> </head> <body> <div class="animation"></div> </body> </html>
效果就是这样的
网络异常,图片无法展示
|
之后我充满了自信,可以完全接下这个序列帧动画。之后发现我拿到的素材是:
网络异常,图片无法展示
|
网络异常,图片无法展示
|
30 M 大小的 180 张图片,我人傻了!
我拒绝写 180 个状态的 keyframes!
于是我想到了下面这个 js 手段。
js 方案一
不能使用公司素材,于是在此处不进行效果展示。
想着总不能我写 180 个状态吧,于是我就想干脆用 js 吧,在图片全都 load 完成后设置一个定时器,去替换背景图片的 url。
const dom = document.getElementsByClassName('animation')[0]; let promiseAll = [] let img = [] let imgTotal = 180; for (let i = 0; i < imgTotal; i++) { promiseAll[i] = new Promise((resolve, reject) => { img[i] = new Image() img[i].src = `./asset/编组 59@2x_00${String(i).padStart(3, '0')}.png` img[i].onload = function () { resolve(img[i]) } }) } let current = 0 Promise.all(promiseAll).then((img) => { setInterval(() => { current = (++current) % 180; dom.style.backgroundImage = `url('asset/编组 59@2x_00${String(current).padStart(3, '0')}.png')` }, 40) })
此处注意两点细节吧:
- padStart 用法(我很少用这个 api)
- 用 Promise 处理图片的加载
- 时间我设置的是 40 ms,因为我想的是一秒至少 24 帧,保证流畅
但是这里还是有一个问题:
莫名其妙在配置低的电脑上,打开控制台时会闪屏...我很不解
js 方案二
现在我依然不解,如果有伙伴知道原因可以评论或者加我好友告诉我,我想到的可能原因就是:
- 图片过大,背景图的切换的渲染消耗
毕竟不打开控制台,不频繁操作时不会闪屏,但是猜测也只是猜测,毕竟我是菜狗子
我想了很多办法,比如:
- 把 180 张图片和在一起,只需要改 background-position 就好了
- 180 个 dom,之后去修改 dom 的透明度
最后我采取了方法二,是不是集齐蹩脚,又土又蹩脚,因为我想的是 dom 都已经渲染完了,每次也只需要去改两个 dom 的透明度,性能压力不大。
- 之前显示的 dom 隐藏掉
- 将下一个 dom 显示出来
let promiseAll = []; let imgList = []; let imgTotal = 180; for (let i = 0; i < imgTotal; i++) { promiseAll[i] = new Promise((resolve) => { imgList[i] = new Image(); imgList[ i ].src = `./asset/编组 59@2x_00${String( i ).padStart(3, "0")}.png`; imgList[i].onload = function () { resolve(imgList[i]); }; }); } let current = 0; let domList = []; const domWrapper = document.body; Promise.all(promiseAll).then((imgList) => { for (const img of imgList) { const domItem = document.createElement('div'); domItem.classList = 'animation'; domItem.style.backgroundImage = `url(${img.src})`; domItem.style.backgroundSize = "308px 669px"; domItem.style.opacity = 0; domWrapper.appendChild(domItem); domList.push(domItem); } setInterval(() => { domList[current].style.opacity = 0; current = ++current % 180; domList[current].style.opacity = 1; }, 40); });
自动生成 keyframes
最后还是想去用 animation 来做,所以就迎来了我的第四种方案,自动生成 好长好长的 keyframes 的关键帧:
const { writeFileSync } = require('fs'); const { join } = require('path'); const generateFunction = (number) => { let currentNum = 0; let str = ''; const step = 100 / number; while( currentNum < 180) { str += `${Number(currentNum * step).toFixed(2)}% { background-image: url('./asset/编组 59@2x_00${String( currentNum ).padStart(3, "0")}.png') } ` currentNum ++; } writeFileSync(join(__dirname, 'funca.js'), str, { encoding: 'utf-8' }) } generateFunction(180);
结束语
网络异常,图片无法展示
|
写在最后
春天落英缤纷
夏天栀子花开
秋天芙蓉三变
冬天暗香疏影
春夏秋冬
樱花
栀子
芙蓉
腊梅
花开花落
唯有你,一直在我心中盛放