大家好,我是寒草😈,一只草系码猿🐒。间歇性热血🔥,持续性沙雕🌟
如果喜欢我的文章,可以关注➕ 点赞,与我一同成长吧~
加我微信:hancao97,邀你进群,一起学习交流前端,成为更优秀的工程师~
小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
前言
前几天隔壁组的同事问我们 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);
结束语
写在最后
春天落英缤纷
夏天栀子花开
秋天芙蓉三变
冬天暗香疏影
春夏秋冬
樱花
栀子
芙蓉
腊梅
花开花落
唯有你,一直在我心中盛放
喜欢我的文章,点赞和关注便是最大的鼓励,也可以加我好友:hancao97,与我深入交流