使用 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));
包括以下一些参数:
选项 | 描述 | 类型 | 是否必须 | 默认值 |
---|---|---|---|---|
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
设置值:
// ---- // 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
也非常有特色,感兴趣的同学可以去 查看源码。
总结
文章主要介绍了如何使用Sass的方式给 @kefyframes
声明一个混合宏,并且通过 @include
来调用声明好的混合宏。并且简单的介绍了如何通过声明的keyframes
来更好的维护我们的代码。最后希望这篇文章对于你平时写代码有所帮助,如果您有更好的方案或者思路,欢迎在下面的评论中与我们一起分享。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论