使用 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);

}

}

// ----

// Sass (v3.4.21)

// Compass (v1.0.3)

// ----

@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);

}

}

@-webkit-keyframes move-the-object {

0% {

transform: translateX(0);

}

100% {

transform: translateX(200px);

}

}

@-moz-keyframes move-the-object {

0% {

transform: translateX(0);

}

100% {

transform: translateX(200px);

}

}

@-o-keyframes move-the-object {

0% {

transform: translateX(0);

}

100% {

transform: translateX(200px);

}

}

@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);

}

}

}

同样能编译出来:

// ----

// Sass (v3.4.21)

// Compass (v1.0.3)

// ----

@mixin keyframes($animationName) {

@keyframes #{$animationName} {

@content;

}

}

.animation {

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

@include keyframes(move-the-object) {

0% {

transform: translateX(0);

}

100% {

transform: translateX(200px);

}

}

}

.animation {

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

}

@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);

}

}

}

编译出来的代码如下:

// ----

// Sass (v3.4.21)

// Compass (v1.0.3)

// ----

@mixin keyframes($animationName) {

@keyframes #{$animationName} {

@content;

}

}

.animation {

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

@include keyframes(move-the-object) {

0% {

transform: translateX(0);

}

100% {

transform: translateX(200px);

}

}

}

.animation {

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

}

@keyframes move-the-object {

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

)

);

}

编译出来的代码如下:

// ----

// Sass (v3.4.21)

// Compass (v1.0.3)

// ----

@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};

}

.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

)

);

}

.anime {

animation: zoom 2s ease infinite;

}

@keyframes zoom {

0% {

transform: scale(1);

background-color: blue;

}

33.33333% {

transform: scale(2);

background-color: green;

}

66.66667% {

transform: scale(3);

background-color: red;

}

100% {

transform: scale(3);

background-color: red;

}

}

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

选项描述类型是否必须默认值
animationNameanimation-name的值,也就是动画名称stringNOanimation
keyframes0%100%的动画规则,也就是@keyframes的帧运动Sass map ObjectYES
durationanimation-druration的值,动画播放持续时间NumberNO1s
waitTimganimation-delay的值,动画延迟播放时间NumberNO0
timingFunctionanimation-timing-function的值,动画播放函数StringNOlinear
iterationCountanimation-iteration-count的值,动画播放次数StringNOinfinite

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

// ----

// Sass (v3.4.21)

// Compass (v1.0.3)

// ----

@mixin animation($options: ()) {

$options: map-merge((

animationName: unique-id(),

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};

}

.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

)

);

}

.anime {

animation: zoom 2s ease infinite;

}

@keyframes zoom {

0% {

transform: scale(1);

background-color: blue;

}

33.33333% {

transform: scale(2);

background-color: green;

}

66.66667% {

transform: scale(3);

background-color: red;

}

100% {

transform: scale(3);

background-color: red;

}

}

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

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

MOTION UI

总结

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

以上是 使用 Sass 来定义 Keyframes 的全部内容, 来源链接: utcz.com/p/233668.html

回到顶部