CSS或JS实现逐帧动画方案

什么是逐帧动画

逐帧动画是一种在连续的关键帧中分解动画动作,即在时间轴的每一帧上绘制不同内容并使之连续播放成动画的一种常见的动画形式。与CSS关键帧动画不同的是,逐帧动画每一帧需要自行定义;关键帧动画只需定义部分关键帧,由计算机插值计算出这些关键帧之间的中间帧。

逐帧动画的源文件要求:每一帧画面的宽高最好相等,使用精灵图时,尽量做到每一帧画面中间无空隙,否则难以定义动画绘制方式。

逐帧动画的源文件如下:

单行逐帧动画

多行逐帧动画

使用CSS绘制逐帧动画

CSS timing-function的steps函数会在关键帧中等分插入指定数目的离散的中间帧,使动画跳跃式播放。当存在多个关键帧时,如steps(5) 0% 50% 100%,则css会在每两个关键帧中间都插入5个中间帧。

使用方法如下:

css 复制代码
.animation-example{
  background-position: 0 0;
  animation: stepsAnimation 1s steps(23);
}
@keyframes stepsAnimation {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 0 100%;
  }
}

.transition-example{
  background-position: 0 0;
  transition: background-position 1s steps(23);
}
.transition-example:hover{
  background-position: 0 100%;
}

所有可以移动图片的属性(如:background-position、left、transform)都可以用于绘制帧动画。主要原理是 元素每次将只展示一帧,其余帧被遮挡,通过steps函数间断地移动图片,跳跃到剩余的帧。

单行帧动画绘制方法:

以background-position属性为例,元素宽高定义为一帧画面的宽高(或等比例伸缩),并定义背景图片为帧动画图片,定义background-size将一帧画面显示在元素中并隐藏住其余帧。定义动画属性,steps(帧数 - 1) ------减一是因为最后一帧100%已被定义,仅需插入其余帧定义即可。

css 复制代码
.animation{
  width: 103px;
  height: 103px;
  background: url("./statics/management-icon.png") no-repeat;
  background-position: 0 0;
  background-size: 100%;
  /** 该帧动画有24帧画面,在2s內播放完成 */
  animation: stepsAnimation 2s steps(23);
}
@keyframes stepsAnimation {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 0 100%;
  }
}

多行逐帧动画绘制方法:

仅animaiton的定义与单行的不同:在定义@keyframes时需要自行定义每一帧的位置、状态,并且timing-function为steps(1)。

建议使用less或scss计算每一帧的定义。或使用我开发的一个工具。

Multiline Keyframes Generator

css 复制代码
.unlock{
    width: 50px;
    height: 66px;
    background-repeat: no-repeat;
    background-image: url(/v/iphone-se/j/images/overview/privacy/privacy_animated__dbfct2heugsy_medium.png);
    background-size: 450px 264px;
    background-position: 0px 0px;
    animation: unlock_animation-medium 3s steps(1);
}
@keyframes unlock_animation-large {
  0% {
    background-position: 0px 0px
  }

  2.7777777778% {
    background-position: -66px 0px
  }

  5.5555555556% {
    background-position: -132px 0px
  }

  8.3333333333% {
    background-position: -198px 0px
  }

  11.1111111111% {
    background-position: -264px 0px
  }

  13.8888888889% {
    background-position: -330px 0px
  }

  16.6666666667% {
    background-position: 0px -88px
  }

  19.4444444444% {
    background-position: -66px -88px
  }

  22.2222222222% {
    background-position: -132px -88px
  }

  25% {
    background-position: -198px -88px
  }

  27.7777777778% {
    background-position: -264px -88px
  }

  30.5555555556% {
    background-position: -330px -88px
  }

  33.3333333333% {
    background-position: 0px -176px
  }

  36.1111111111% {
    background-position: -66px -176px
  }

  38.8888888889% {
    background-position: -132px -176px
  }

  41.6666666667% {
    background-position: -198px -176px
  }

  44.4444444444% {
    background-position: -264px -176px
  }

  47.2222222222% {
    background-position: -330px -176px
  }

  50% {
    background-position: 0px -264px
  }

  52.7777777778% {
    background-position: -66px -264px
  }

  55.5555555556% {
    background-position: -132px -264px
  }

  58.3333333333% {
    background-position: -198px -264px
  }

  61.1111111111% {
    background-position: -264px -264px
  }

  63.8888888889% {
    background-position: -330px -264px
  }

  66.6666666667% {
    background-position: 0px -352px
  }

  69.4444444444% {
    background-position: -66px -352px
  }

  72.2222222222% {
    background-position: -132px -352px
  }

  75% {
    background-position: -198px -352px
  }

  77.7777777778% {
    background-position: -264px -352px
  }

  80.5555555556% {
    background-position: -330px -352px
  }

  83.3333333333% {
    background-position: 0px -440px
  }

  86.1111111111% {
    background-position: -66px -440px
  }

  88.8888888889% {
    background-position: -132px -440px
  }

  91.6666666667% {
    background-position: -198px -440px
  }

  94.4444444444% {
    background-position: -264px -440px
  }

  97.2222222222% {
    background-position: -330px -440px
  }

  to {
    background-position: -330px -440px
  }
}

缺点(已解决)

使用CSS绘制逐帧动画的自定义能力不高,比较适合动画连续播放;transition与animation难以做到中断并回退,不适用于有每一帧由自己控制播放、中断、回退、跳帧这一类需求的效果。

缺点解决方案,如何控制每一帧的播放

参考Apple官网 iPhone SE产品界面介绍的动画。

将动画直接暂停,并将播放时长与帧数等长,使用js更改animation-delay控制每一帧的播放。animation-delay定义一个负值会让动画立即开始。但是动画会从它的动画序列中某位置开始。例如,如果设定值为 -1s,动画会从它的动画序列的第 1 秒位置处立即开始。

css 复制代码
.animation{
    background-repeat: no-repeat;
    background-image: url(/v/iphone-se/j/images/overview/privacy/privacy_animated__dbfct2heugsy_medium.png);
    background-size: 450px 264px;
    width: 50px;
    height: 66px;
    animation: unlock_animation-medium 36s steps(1) forwards;
    /** 暂停动画,由自己控制动画播放 */
    animation-play-state: paused;
    animation-iteration-count: 1;
    /** 动画总共36帧,则duration为36s */
    animation-duration: 36s;
    background-position: 0px 0px;
}

/** 使用js更改animation-delay,控制动画播放 */
<div class='animation' style='animation-delay: -6s;'></div>

此时CSS便仅用于动画帧的定义,动画帧的播放则通过js自定义。

使用js绘制逐帧动画 (不推荐)

在使用CSS绘制逐帧动画前我遇到一个困难,CSS逐帧动画完全自动播放,难以控制中断或中断后回退等功能(已解决 ),所以我封装了一个js库用于绘制逐帧动画。

github.com/YThinker/fr...https://github.com/YThinker/frame-animation/tree/master 包含:SinglelineFrameAnimation 用于绘制单行逐帧动画;MultilineFrameAnimation 多行逐帧动画绘制。可选择使用transform,background,image src等属性绘制,包括了无限循环、往复播放、延迟播放等功能。

example页面

FrameByFrame Animation Example

使用img src配合js绘制逐帧动画(不推荐)

原理非常简单,将每一帧图片导出为单独的图片,通过替换img src的方式播放每一帧画面,该方法非常简单,但是用户体验差,会导致请求次数过多,可以通过预加载的方式优化。

相关推荐
光影少年2 小时前
react性能优化
前端·react.js·掘金·金石计划
深蓝电商API2 小时前
逆向工程入门:从Chrome DevTools到JS混淆还原
前端·javascript·chrome·爬虫·chrome devtools
石山岭2 小时前
# iOS 题库
前端
Zella折耳根2 小时前
从零解析终端小游戏开发:功能实现与核心编程知识点复盘
前端
Pikachu8032 小时前
我在早高峰地铁里对手机吼了几句,隔壁同事直接看傻了
前端·后端
半岛@少年2 小时前
Webpack在前端项目中究竟发挥什么作用?
前端·webpack·前端工程化
2501_940041743 小时前
企业官网与品牌落地页,能直接交付的前端题目
前端
小番茄夫斯基3 小时前
全球大模型的价格和能力排行汇总
前端·后端·架构
小小小小宇3 小时前
前端领域 30 个值得安装的 Agent Skills
前端