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的方式播放每一帧画面,该方法非常简单,但是用户体验差,会导致请求次数过多,可以通过预加载的方式优化。

相关推荐
西安小哥6 分钟前
破局与重生:大厂前端如何借力 AI 转型“超级全栈“
前端·人工智能
sunly_36 分钟前
TypeScript总结:16、面向对象速查
前端·javascript·typescript
MXN_小南学前端40 分钟前
React超长文本域中实现“返回顶部”浮动按钮
前端·javascript·react.js
学习嵌入式的小周1 小时前
Notepad++8.8.7下载安装(附安装包)
前端·notepad++
gs801401 小时前
解构 Cordis:面向“时空可组合性”的 TypeScript 元框架深度剖析
前端·javascript·typescript
岁岁种桃花儿2 小时前
Vue核心语法第一篇:Vue是什么?
前端·javascript·vue.js
观无2 小时前
若依EasyExcel实现单元格合并
开发语言·前端·javascript
愚公搬代码3 小时前
【愚公系列】《Web应用安全》001-VMware的安装
前端·安全
xiaohaiAIgeo3 小时前
【2026年】HG/T 20656-2024化工暖通空调设计规范:新版标准的变化与影响
java·前端·javascript·科普知识
立少→万能汉编3 小时前
用“立少→超文本”写静态网页,标签<倍>
服务器·前端·javascript