使用Sass来定义Keyframes

发布于 大漠

CSS的Animation成为Web Animation中主要实现方式之一。使用CSS 的animation可以在Web中实现一些动画效果。而其中最为关键的还是依赖于@keyframes,让动画可以根据帧定制不同的动画效果。

其实这篇文章的主要目的不是要给大空介绍@keyframes的语法和使用方法。因为,大家对它并不会陌生,而且也知道怎么使用?那么大家在使用keyframes的时候是不是碰到一些烦恼,对于不同的动画效果,都需要去创建一个keyframes,而且对于一个复杂的动画,一个动画中用到多个元素元素时,是不是感觉到管理和维护keyframes比较痛苦,易出错。

如果你有上述这样的烦恼,那么下面的内容值得你花点时间继续往下阅读。

CSS中的keyframes

简单的回忆一下,如果要使用animation创建一个动效,就必须先用keyframes创建一个动效。

@keyframes animation-name {
    ...
}

由于各浏览器厂商对animation支持程度不一样,早期为了兼容各浏览器,需要添加各种前缀:

@-webkit-keyframes animation-name {...}
@-moz-keyframes animation-name {...}
@-o-keyframes animation-name {...}
@keyframes animation-name {...}

虽然后面有了Autoprefixer的出现,让我们变得轻松了些。可以根据不同的配置,在编译代码的时候就会自动根据配置添加需要的前缀。在Autoprefixer的帮助之下,我们可以在代码中直接写:

@keyframes animation-name {...}

但这仅仅是解决了浏览器厂商的前缀的维护问题,但对于整个代码维护并没有帮我们减轻。

SCSS中的keyframes

Autoprefixer还没有出现之前,SCSS其实帮助我们解决一些前缀的事项,言外之意,就是给keyframes定义一个混合宏(mixins):

@mixin keyframes($animationName) {
    @-webkit-keyframes #{$animationName} {
        @content;
    }
    @-moz-keyframes #{$animationName} {
        @content;
    }
    @-o-keyframes #{$animationName} {
        @content;
    }
    @keyframes #{$animationName} {
        @content;
    }
}

在调用的时候,可以直接这样使用

@include keyframes(move-the-object) {
  	0%   { 
          transform: translateX(0);
    }
  	100% { 
          transform: translateX(200px); 
    }
}

当然,如果你的工作环境中已经配置了Autoprefixer的话,上面的混合宏可以直接写:

@mixin keyframes($animationName) {
    @keyframes #{$animationName} {
        @content;
    }
}

回到keyframes调用上面来,除了独立调用声明好的混合宏之外,还可以跟随元素一起调用:

.animation {
    animation: move-the-object 2s ease both;

    @include keyframes(move-the-object) {
      	0%   { 
      		transform: translateX(0);
        }
      	100% { 
      		transform: translateX(200px); 
        }
    }
}

同样能编译出来:

除此之外,如果你想让你的animation-name有一个随机名称,可以借助Sass中的unique-id()函数功能。

@mixin animation-keyframes {
  	$animation-name: unique-id();
  	animation-name: $animation-name;

  	@keyframes #{$animation-name} {
    	@content;
  	}
}

像下面这样调用,就能随机产生一个keyframes的名称:

.animation {
    animation:  2s ease both;
  
    @include animation-keyframes {
      	0%   { 
      		transform: translateX(0);
        }
      	100% { 
      		transform: translateX(200px); 
        }
    }
}

编译出来的代码如下:

当然,上面的keyframes混合宏是一种简单的声明方式,如果你想让它变得更强大也是可以的,比如:

@mixin animation($options: ()) {

  $options: map-merge((
    animationName: animation,
    duration: 1,
    waitTime: 0,
    timingFunction: linear,
    iterationCount: infinite
  ), $options);

  $name: map-get($options, animationName);
  $kf: map-get($options, keyframes);
  $kfLength: length($kf);
  $duration: map-get($options, duration);
  $waitTime: map-get($options, waitTime);
  $timingFunction: map-get($options, timingFunction);
  $iterationCount: map-get($options, iterationCount);
  $counter: 1; // index of 'each'

  @keyframes #{$name} {
    @each $frame, $prop in $kf {
      #{$frame * $duration / ($duration + $waitTime)}% {
        @each $k, $v in $prop {
          #{$k}: #{$v}
        }
      }
      // if last in loop and waitTime is not 0, add the last frame as 100% (this is what creates the pause)
      @if $counter == $kfLength and $waitTime > 0 {
        100% {
          @each $k, $v in $prop {
            #{$k}: #{$v}
          }
        }
      }
      $counter: $counter+1;
    }
  }

  animation: #{$name} #{$duration}s #{$timingFunction} #{$iterationCount};

}

上面一堆的代码,其实并不复杂,稍微了解Sass基础的都读得懂上面的代码,这里就不做过多的阐述。那么调用的时候,只需要这样:

.anime {
  	@include animation(
          (
            animationName: zoom,
            keyframes: (
                  0: (
                    transform: scale(1),
                    background-color: blue
                  ),
                  50: (
                    transform: scale(2),
                    background-color: green
                  ),
                  100: (
                    transform: scale(3),
                    background-color: red
                  )
            ),
            duration: 2,
            waitTime: 1,
        		timingFunction: ease,
            iterationCount: infinite
          )
    );
}

编译出来的代码如下:

混合宏@include waitAnimate((options));包括以下一些参数:

选项 描述 类型 是否必须 默认值
animationName animation-name的值,也就是动画名称 string NO animation
keyframes 0%100%的动画规则,也就是@keyframes的帧运动 Sass map Object YES
duration animation-druration的值,动画播放持续时间 Number NO 1s
waitTimg animation-delay的值,动画延迟播放时间 Number NO 0
timingFunction animation-timing-function的值,动画播放函数 String NO linear
iterationCount animation-iteration-count的值,动画播放次数 String NO infinite

把上面的混合宏中的animationName的值换成unique-id()函数,就可以获取随机的animation-name值,而且在调用混合宏@nimation可以不给animationName设置值:

不过这种方式略有一个缺陷,就是不能一直同时声明多个animation-name。如果希望同时声明多个,需要在上面的基础上进行修改或者扩展。感兴趣的同学可以尝试一下。

其实上述的方法只是Sass实现keyframes的几种而以,在网络上这样的混合宏方法有很多种。大家也可以根据自己平时常使用的方式,可以定制适合自己的keyframes混合宏。最后给大家推荐一个ZURB团队做的MOTION UI,这里面声明keyframes也非常有特色,感兴趣的同学可以去查看源码

MOTION UI

总结

文章主要介绍了如何使用Sass的方式给@kefyframes声明一个混合宏,并且通过@include来调用声明好的混合宏。并且简单的介绍了如何通过声明的keyframes来更好的维护我们的代码。最后希望这篇文章对于你平时写代码有所帮助,如果您有更好的方案或者思路,欢迎在下面的评论中与我们一起分享。