使用 Sass 来定义 Keyframes

发布于 2021-07-24 15:10:08 字数 13679 浏览 1989 评论 0

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来更好的维护我们的代码。最后希望这篇文章对于你平时写代码有所帮助,如果您有更好的方案或者思路,欢迎在下面的评论中与我们一起分享。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

泛泛之交

暂无简介

0 文章
0 评论
21557 人气
更多

推荐作者

沧笙踏歌

文章 0 评论 0

山田美奈子

文章 0 评论 0

佚名

文章 0 评论 0

岁月无声

文章 0 评论 0

暗藏城府

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文