简单说下逐帧动画

逐帧动画是个啥呢??

先让我们看一下下面两个网站,上面那动画,唰唰唰,哗哗哗,啦啦啦的,看着心痒的我就忍不住 F12 了一下,然后发现都用到了逐帧动画。
http://engzell.me/
http://melanie-f.com/en/

简单来说就是利用一张动画分解的 sprite 图,通过 js 脚本改变图片的 css 属性或使用 css3 的 animation 属性来实现动画的效果。

1. 利用 css3-animation 来制作逐帧动画

css3-animation
首先看一下对 animation 属性各个浏览器的支持情况,IE 10+、Firefox 以及 Opera 支持 animation 属性。

Safari 和 Chrome 支持替代的 -webkit-animation 属性。(IE9 以下不支持)

animation 其他几个动画属性就不多说了,这次要用到的是 animation-timing-function 里的 steps()阶段函数。

animation-timing-function:ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps(<integer>[, [ start | end ] ]?) | cubic-bezier(<number>, <number>, <number>, <number>)

取值:

linear:线性过渡。等同于贝塞尔曲线(0.0, 0.0, 1.0, 1.0)

ease:平滑过渡。等同于贝塞尔曲线(0.25, 0.1, 0.25, 1.0)

ease-in:由慢到快。等同于贝塞尔曲线(0.42, 0, 1.0, 1.0)

ease-out:由快到慢。等同于贝塞尔曲线(0, 0, 0.58, 1.0)

ease-in-out:由慢到快再到慢。等同于贝塞尔曲线(0.42, 0, 0.58, 1.0)

step-start:等同于 steps(1, start)

step-end:等同于 steps(1, end)

steps(<integer>[, [ start | end ] ]?):接受两个参数的步进函数。第一个参数必须为正整数,指定函数的步数。第二个参数取值可以是 start 或 end,指定每一步的值发生变化的时间点。第二个参数是可选的,默认值为 end。

cubic-bezier(<number>, <number>, <number>, <number>):特定的贝塞尔曲线类型,4 个数值需在[0, 1]区间内

所以这个 steps()函数是个啥呢,下图是 W3C 对这个函数的图解

大概意思呢就是这个函数可以将动画分成 n 等分,start 和 end 决定动画执行是从前还是从后。
go.png

例如我们有上面这么一张逐帧动画图,因为动画是从左往右,所以先写一个关键帧 keyframes 名字叫 sprite-animate-go,背景起始位置从 0%到 100%,具体代码如下:

.go {
  background: url(https://file.gaoquanquan.com/blog/16925150876645.png) no-repeat;
  width: 50px;
  height: 65px;
  margin: 0 auto;
  animation: sprite-animate-go 1.5s steps(50) infinite;
  -webkit-animation: sprite-animate-go 1.5s steps(50) infinite;
}

@keyframes sprite-animate-go {
  from {
    background-position: 0%;
  }
  to {
    background-position: 100%;
  }
}

@-webkit-keyframes sprite-animate-go {
  from {
    background-position: 0%;
  }
  to {
    background-position: 100%;
  }
}

效果如下:

2. 使用 js 脚本操作 css 属性

上面两个网站都是用的脚本实现的,因为使用 js 可以兼容大部分浏览器,是最保险的方法。思路就很简单了,帧图片外面加个固定大小的 div,overflow: hidden, 然后用 js 不断改变帧图片的 translate 值。

代码如下:

<div class="intro-container">
  <img src="media/16925150876645.png" />
</div>
.intro-container {
  width: 50px;
  height: 65px;
  overflow: hidden;
}
window.onload = function () {
  var introSprite = $('.intro-container')
  spriteAnim(introSprite, 25, 59, 33, 2, false)
}

function spriteAnim(container, fps, frames, col, row, loop) {
  var intro = container.find('.intro')
  var width = container.width()
  var height = container.height()
  var frame = 1
  var x = (y = 0)

  intro.css({ transform: 'translate3d(0, 0, 0)', '-webkit-transform': '-webkit-translate3d(0, 0, 0)' })

  setTimeout(function () {
    playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop)
  }, 1000 / fps)
}

function playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop) {
  x++
  if (x >= col) {
    x = 0
    y++
  }

  var bgX = '-' + x * width + 'px'
  var bgY = '-' + y * height + 'px'
  intro.css({ transform: 'translate3d(' + bgX + ', ' + bgY + ', 0)', '-webkit-transform': '-webkit-translate3d(' + bgX + ', ' + bgY + ', 0)' })

  frame++
  if (frame >= frames) {
    if (loop) {
      frame = 1
      x = y = 0
    } else {
      intro.closest('.intro-container').trigger('end')
      return
    }
  }

  setTimeout(function () {
    playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop)
  }, 1000 / fps)
}

PS,那么问题来了,

1.有了 gif 干嘛还要费劲用逐帧动画?
区别大概在于可以控制逐帧动画的快慢和暂停,而且 gif 动画不够清晰,有些时候达不到我们的要求。

2.怎么得到一张逐帧图片嘞?
确切的说应该是从 AE 导出的序列帧图组怎么合成一张图嘞?这个问题好像不是应该我考虑的,但你们不想知道吗?不想吗?想吗?吗?我可是想好久呢,就是

美图秀秀啊

虽然一次最多拼 30 张,但真的挺好用的…

目录