CSS或JS实现逐帧动画方案

本文详细介绍了CSS逐帧动画的概念及实现方法,包括单行与多行逐帧动画的绘制技巧,并探讨了如何通过JS控制动画播放以实现更复杂的交互效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是逐帧动画

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

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

逐帧动画的源文件如下:

跑动的熊
单行逐帧动画
 多行逐帧动画

使用CSS绘制逐帧动画

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

使用方法如下:

.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%已被定义,仅需插入其余帧定义即可。

.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

.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 秒位置处立即开始。

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

example页面

FrameByFrame Animation Example

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

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值