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

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

前言


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


结束语


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


写在最后


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

相关文章
|
5月前
|
存储 机器学习/深度学习 算法
python 五种算法转置后翻转、层次旋转、递归分块、一次性旋转、环状替换 实现旋转图像【力扣题48】
python 五种算法转置后翻转、层次旋转、递归分块、一次性旋转、环状替换 实现旋转图像【力扣题48】
|
6月前
|
监控 API 计算机视觉
OpenCV这么简单为啥不学——1.6、图像旋转与翻转(rotate函数、imutils环境安装、imutils任意角度旋转)
OpenCV这么简单为啥不学——1.6、图像旋转与翻转(rotate函数、imutils环境安装、imutils任意角度旋转)
75 0
|
6月前
|
C# 开发工具 Windows
C#操作PPT动画窗格并插入音频文件的一些思路
C#操作PPT动画窗格并插入音频文件的一些思路
|
JavaScript API 容器
彻底弄懂元素样式、位置、大小相关计算
在我们日常开发中偶尔会碰到获取元素样式、设置某元素样式、计算元素位置、计算滚动距离等需求。但是js中关于元素位置、样式、大小的api种类繁多,稍不留神就会搞不清楚。今天笔者就带你彻底弄清楚,让你在这类问题上不再迷茫。
215 0
An动画基础之散件动画原理与形状提示点
An动画基础之散件动画原理与形状提示点
876 0
An动画基础之散件动画原理与形状提示点
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
317 0
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(一)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)
396 0
【音频处理】Melodyne 选择工具使用 ( 主工具简介 | 修改音高 | 自动吸附 | 音符长度修改 | 长度自动吸附 | 设置音符分离线 | 设置片段分离线 )(二)
|
前端开发 算法 JavaScript
《JS原理、方法与实践》- canvas作图(二)- 组合、剪切、坐标检测
《JS原理、方法与实践》- canvas作图(二)- 组合、剪切、坐标检测
198 0
|
Java BI 开发者
生成图片(VerifyCode 类)|学习笔记
快速学习生成图片(VerifyCode 类)
186 0
生成图片(VerifyCode 类)|学习笔记
|
图形学 索引
Unity 之 查找游戏物体的几种方式解析
一篇小白也能看懂的查找游戏物体的方式解析 -- Unity 之 查找物体的几种方式。
1678 0
Unity 之 查找游戏物体的几种方式解析