我用最蹩脚的方式写了一个「序列帧动画」 🍂

简介: 我用最蹩脚的方式写了一个「序列帧动画」 🍂

前言


前几天隔壁组的同事问我们 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);


结束语


网络异常,图片无法展示
|


写在最后


春天落英缤纷
夏天栀子花开
秋天芙蓉三变
冬天暗香疏影
春夏秋冬
樱花
栀子
芙蓉
腊梅
花开花落
唯有你,一直在我心中盛放

相关文章
|
7月前
|
存储 安全 编译器
C++学习过程中的一些值得注意的小点(1)
C++学习过程中的一些值得注意的小点(1)
|
7月前
|
C#
LabVIEW中如何实现任意形状的不规则按键
LabVIEW中如何实现任意形状的不规则按键
71 0
|
7月前
|
算法 C++ Java
Java每日一练(20230422) 拼接最大数、Z字形变换、跳跃游戏
Java每日一练(20230422) 拼接最大数、Z字形变换、跳跃游戏
78 0
Java每日一练(20230422) 拼接最大数、Z字形变换、跳跃游戏
|
7月前
|
监控 API 计算机视觉
OpenCV这么简单为啥不学——1.9、cvtColor颜色空间转换(全色值效果演示)
OpenCV这么简单为啥不学——1.9、cvtColor颜色空间转换(全色值效果演示)
69 0
|
小程序 开发者
想要漂亮的效果,怎么能少了粒子插件
嗨!大家好,我是小蚂蚁,今天我们来分享一下微信小游戏制作工具中的关于粒子插件的使用,粒子插件能够帮助我们在游戏中创建各种漂亮的效果,例如爆炸,果汁四溅,漂亮的焰火等等。
95 0
关于 Qt图形视图框架自绘图元放到左边和上边之外,部分在内进行拉伸后,拉伸多余的区域无法碰撞 的解决方法
关于 Qt图形视图框架自绘图元放到左边和上边之外,部分在内进行拉伸后,拉伸多余的区域无法碰撞 的解决方法
关于 Qt图形视图框架自绘图元放到左边和上边之外,部分在内进行拉伸后,拉伸多余的区域无法碰撞 的解决方法
|
存储 算法 前端开发
数组旋转,来来来,走个K步~
在前端算法面试中,数组是经常被问到的、使用到的。今天我们来看一道经典的前端基础面试题:【数组旋转K步】。
227 0
Mac系统快速获得文件位置的方法(带图解释)
Mac系统快速获得文件位置的方法(带图解释)
189 0
Mac系统快速获得文件位置的方法(带图解释)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
332 0
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)
414 0
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)