逐帧动画是个啥呢??

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

lookbook.gif

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

实现

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

20141227135938.png

首先看一下对 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对这个函数的图解
step.png

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

go.png

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

  1. .go {
  2. background: url(http://www.gaoquanquan.com/wp-content/uploads/2016/04/go.png) no-repeat;
  3. width: 50px;
  4. height: 65px;
  5. margin: 0 auto;
  6. animation: sprite-animate-go 1.5s steps(50) infinite;
  7. -webkit-animation: sprite-animate-go 1.5s steps(50) infinite;
  8. }
  9. @keyframes sprite-animate-go {
  10. from {
  11. background-position: 0%;
  12. }
  13. to {
  14. background-position: 100%;
  15. }
  16. }
  17. @-webkit-keyframes sprite-animate-go {
  18. from {
  19. background-position: 0%;
  20. }
  21. to {
  22. background-position: 100%;
  23. }
  24. }

效果如下:

go.gif

使用 js 脚本操作 css 属性

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

代码如下:

  1. <div class="intro-container">
  2. <img src="http://www.gaoquanquan.com/wp-content/uploads/2016/04/go.png">
  3. </div>
  1. .intro-container {
  2. width: 50px;
  3. height: 65px;
  4. overflow: hidden;
  5. }
  1. window.onload = function(){
  2. var introSprite = $(".intro-container");
  3. spriteAnim(introSprite, 25, 59, 33, 2, false);
  4. }
  5. function spriteAnim(container, fps, frames, col, row, loop) {
  6. var intro = container.find('.intro');
  7. var width = container.width();
  8. var height = container.height();
  9. var frame = 1;
  10. var x = y = 0;
  11. intro.css({transform: 'translate3d(0, 0, 0)', '-webkit-transform': '-webkit-translate3d(0, 0, 0)'});
  12. setTimeout(function() {
  13. playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop);
  14. }, 1000/fps);
  15. }
  16. function playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop) {
  17. x++;
  18. if (x >= col) {
  19. x = 0;
  20. y++;
  21. }
  22. var bgX = '-'+(x*width)+'px';
  23. var bgY = '-'+(y*height)+'px';
  24. intro.css({transform: 'translate3d('+bgX+', '+bgY+', 0)', '-webkit-transform': '-webkit-translate3d('+bgX+', '+bgY+', 0)'});
  25. frame++;
  26. if (frame >= frames) {
  27. if (loop) {
  28. frame = 1;
  29. x = y = 0;
  30. } else {
  31. intro.closest('.intro-container').trigger('end')
  32. return;
  33. }
  34. }
  35. setTimeout(function() {
  36. playSpriteAnim(intro, fps, width, height, frames, col, row, frame, x, y, loop);
  37. }, 1000/fps);
  38. }

思考 🤔

PS,那么问题来了,

1.有了gif干嘛还要费劲用逐帧动画?

区别大概在于可以控制逐帧动画的快慢和暂停,而且gif动画不够清晰,有些时候达不到我们的要求。

2.怎么得到一张逐帧图片嘞?

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

美图秀秀啊 简单说下逐帧动画 - 图7

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